diff --git a/Assets/02.Scripts/BehaviorTree/Npc/Customer/Action/OrderFood.cs b/Assets/02.Scripts/BehaviorTree/Npc/Customer/Action/OrderFood.cs
index 3053f864b..0855abe49 100644
--- a/Assets/02.Scripts/BehaviorTree/Npc/Customer/Action/OrderFood.cs
+++ b/Assets/02.Scripts/BehaviorTree/Npc/Customer/Action/OrderFood.cs
@@ -50,6 +50,9 @@ namespace BlueWater.BehaviorTrees.Actions
return TaskStatus.Running;
}
+ ///
+ ///
+ ///
private void HandleFoodInteraction()
{
var tycoonPlayer = GameManager.Instance.CurrentTycoonPlayer;
diff --git a/Assets/02.Scripts/Ui/Tycoon/FoodBalloonUi.cs b/Assets/02.Scripts/Ui/Tycoon/FoodBalloonUi.cs
index 2cf08ab48..dc006ea73 100644
--- a/Assets/02.Scripts/Ui/Tycoon/FoodBalloonUi.cs
+++ b/Assets/02.Scripts/Ui/Tycoon/FoodBalloonUi.cs
@@ -71,7 +71,7 @@ namespace BlueWater.Uis
return;
}
- if (_orderItemData.Sprite == null)
+ if (!_orderItemData.Sprite)
{
Debug.LogWarning($"{_orderItemData.Sprite} 해당 음식의 이미지가 없습니다.");
}
diff --git a/Assets/11.BehaviorTree/Customer.asset b/Assets/11.BehaviorTree/Customer.asset
index cecc72ef1..6947346db 100644
--- a/Assets/11.BehaviorTree/Customer.asset
+++ b/Assets/11.BehaviorTree/Customer.asset
@@ -21,17 +21,18 @@ MonoBehaviour:
startIndex:
variableStartIndex:
JSONSerialization: '{"EntryTask":{"Type":"BehaviorDesigner.Runtime.Tasks.EntryTask","NodeData":{"Offset":"(548.5,0)"},"ID":0,"Name":"Entry","Instant":true},"RootTask":{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(-2.878418,152.4463)","Comment":"\uc190\ub2d8\uc758
- \ud55c \uc2f8\uc774\ud074"},"ID":1,"Name":"Customer Cycle","Instant":true,"AbortTypeabortType":"None","Children":[{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(-712.8185,150)","Comment":"\uc790\ub9ac\ub97c
+ \ud55c \uc2f8\uc774\ud074"},"ID":1,"Name":"Customer Cycle","Instant":true,"AbortTypeabortType":"None","Children":[{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(-847.4339,150)","Comment":"\uc790\ub9ac\ub97c
\ucc3e\ub294\ub2e4"},"ID":2,"Name":"Find Empty Table Sequence","Instant":true,"AbortTypeabortType":"None","Children":[{"Type":"BlueWater.BehaviorTrees.Actions.FindTable","NodeData":{"Offset":"(-237.57135,147)","Comment":"\ube48\uc790\ub9ac\ub97c
\ucc3e\uc744 \ub54c\uae4c\uc9c0 \ub300\uae30"},"ID":3,"Name":"Find Table","Instant":true},{"Type":"BlueWater.BehaviorTrees.Actions.HasReachedDestination","NodeData":{"Offset":"(4.428632,152)","Comment":"\ub3c4\ucc29\ud560
\ub54c\uae4c\uc9c0 \ub300\uae30"},"ID":4,"Name":"Has Reached Destination","Instant":true},{"Type":"BlueWater.BehaviorTrees.Actions.SetTableSeatPositionAndDirection","NodeData":{"Offset":"(250,150)","Comment":"\ud14c\uc774\ube14
\uc88c\uc11d\uc5d0 \uc704\uce58\ud558\uace0, \ud14c\uc774\ube14\uc744 \ubc14\ub77c\ubd04"},"ID":5,"Name":"Set
Table Seat Position And Direction","Instant":true}]},{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(-140,150)","Comment":"\uc74c\ub8cc\ub97c
- \uc8fc\ubb38\ud55c\ub2e4"},"ID":6,"Name":"Order Drink Sequence","Instant":true,"AbortTypeabortType":"None","Children":[{"Type":"BehaviorDesigner.Runtime.Tasks.Selector","NodeData":{"Offset":"(-1.223877,154.022858)"},"ID":7,"Name":"Selector","Instant":true,"AbortTypeabortType":"None","Children":[{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(-127.381042,151.88)"},"ID":8,"Name":"Order
+ \uc8fc\ubb38\ud55c\ub2e4"},"ID":6,"Name":"Order Drink Sequence","Instant":true,"AbortTypeabortType":"None","Children":[{"Type":"BehaviorDesigner.Runtime.Tasks.Selector","NodeData":{"Offset":"(-1.223877,154.022858)"},"ID":7,"Name":"Selector","Instant":true,"AbortTypeabortType":"None","Children":[{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(-182.936584,151.88)"},"ID":8,"Name":"Order
Success Sequence","Instant":true,"AbortTypeabortType":"None","Children":[{"Type":"BlueWater.BehaviorTrees.Actions.OrderFood","NodeData":{"Offset":"(-105.555573,145.555237)","Comment":"\uc74c\ub8cc\ub97c
\uc8fc\ubb38\ud558\uace0 \uae30\ub2e4\ub9b0\ub2e4"},"ID":9,"Name":"Order
- Food","Instant":true}]},{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(166.6665,147.777832)"},"ID":10,"Name":"Order
- Failure Sequence","Instant":true,"AbortTypeabortType":"None","Children":[{"Type":"BlueWater.BehaviorTrees.Actions.Move","NodeData":{"Offset":"(-120.441589,149.5423)"},"ID":11,"Name":"Move","Instant":true,"Booleank__BackingField":true,"SharedVector3k__BackingField":{"Type":"BehaviorDesigner.Runtime.SharedVector3","Name":null,"Vector3mValue":"(0,0,-19)"},"SharedColliderk__BackingField":{"Type":"BehaviorDesigner.Runtime.SharedCollider","Name":null}},{"Type":"BehaviorDesigner.Runtime.Tasks.Unity.UnityGameObject.Destroy","NodeData":{"Offset":"(116.929504,154.032043)"},"ID":12,"Name":"Destroy","Instant":true,"SharedGameObjecttargetGameObject":{"Type":"BehaviorDesigner.Runtime.SharedGameObject","Name":"MyObj","IsShared":true},"Singletime":0}]}]}]}]},"DetachedTasks":[{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(1652.77856,300)","Comment":"\ud1f4\uc7a5\ud55c\ub2e4"},"ID":13,"Name":"Sequence","Instant":true,"Disabled":true,"AbortTypeabortType":"None"},{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(1402.77844,300)","Comment":"\uacc4\uc0b0\ud55c\ub2e4"},"ID":14,"Name":"Sequence","Instant":true,"Disabled":true,"AbortTypeabortType":"None"},{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(1112.77722,300)","Comment":"\uc74c\uc2dd\uc744
+ Food","Instant":true}]},{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(263.8887,147.777832)"},"ID":10,"Name":"Order
+ Failure Sequence","Instant":true,"AbortTypeabortType":"None","Children":[{"Type":"BlueWater.BehaviorTrees.Actions.Move","NodeData":{"Offset":"(-120.441589,149.5423)","Comment":"\uc785\uad6c\ub85c
+ \ub418\ub3cc\uc544\uac04\ub2e4"},"ID":11,"Name":"Move","Instant":true,"Booleank__BackingField":true,"SharedVector3k__BackingField":{"Type":"BehaviorDesigner.Runtime.SharedVector3","Name":null,"Vector3mValue":"(0,0,-19)"},"SharedColliderk__BackingField":{"Type":"BehaviorDesigner.Runtime.SharedCollider","Name":null}},{"Type":"BehaviorDesigner.Runtime.Tasks.Unity.UnityGameObject.Destroy","NodeData":{"Offset":"(116.929504,154.032043)"},"ID":12,"Name":"Destroy","Instant":true,"SharedGameObjecttargetGameObject":{"Type":"BehaviorDesigner.Runtime.SharedGameObject","Name":"MyObj","IsShared":true},"Singletime":0}]}]}]}]},"DetachedTasks":[{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(1652.77856,300)","Comment":"\ud1f4\uc7a5\ud55c\ub2e4"},"ID":13,"Name":"Sequence","Instant":true,"Disabled":true,"AbortTypeabortType":"None"},{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(1402.77844,300)","Comment":"\uacc4\uc0b0\ud55c\ub2e4"},"ID":14,"Name":"Sequence","Instant":true,"Disabled":true,"AbortTypeabortType":"None"},{"Type":"BehaviorDesigner.Runtime.Tasks.Sequence","NodeData":{"Offset":"(1112.77722,300)","Comment":"\uc74c\uc2dd\uc744
\uc8fc\ubb38\ud55c\ub2e4"},"ID":15,"Name":"Sequence","Instant":true,"Disabled":true,"AbortTypeabortType":"None"}],"Variables":[{"Type":"BehaviorDesigner.Runtime.SharedGameObject","Name":"MyObj","IsShared":true}]}'
fieldSerializationData:
typeName: []
diff --git a/Assets/Editor Default Resources.meta b/Assets/Editor Default Resources.meta
new file mode 100644
index 000000000..5bca1ff30
--- /dev/null
+++ b/Assets/Editor Default Resources.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: a03322c80bdb08b4c81e34c015c2c3e0
+folderAsset: yes
+timeCreated: 1523621471
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Editor Default Resources/Dialogue System.meta b/Assets/Editor Default Resources/Dialogue System.meta
new file mode 100644
index 000000000..86a3a0ff1
--- /dev/null
+++ b/Assets/Editor Default Resources/Dialogue System.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 6415a3b923f706f4cbfad95a17aeb6e0
+folderAsset: yes
+timeCreated: 1523621481
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Editor Default Resources/Dialogue System/Conditions.png b/Assets/Editor Default Resources/Dialogue System/Conditions.png
new file mode 100644
index 000000000..637d18f35
Binary files /dev/null and b/Assets/Editor Default Resources/Dialogue System/Conditions.png differ
diff --git a/Assets/Editor Default Resources/Dialogue System/Conditions.png.meta b/Assets/Editor Default Resources/Dialogue System/Conditions.png.meta
new file mode 100644
index 000000000..b5d045006
--- /dev/null
+++ b/Assets/Editor Default Resources/Dialogue System/Conditions.png.meta
@@ -0,0 +1,95 @@
+fileFormatVersion: 2
+guid: e36d98dc2889c2040b35e8b25bb3119f
+timeCreated: 1590451810
+licenseType: Store
+TextureImporter:
+ fileIDToRecycleName: {}
+ externalObjects: {}
+ serializedVersion: 4
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 0
+ aniso: -1
+ mipBias: -1
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ nPOTScale: 2
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ platformSettings:
+ - buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ - buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Editor Default Resources/Dialogue System/Conditions.png
+ uploadId: 667566
diff --git a/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Dark.png b/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Dark.png
new file mode 100644
index 000000000..f78b9f5a8
Binary files /dev/null and b/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Dark.png differ
diff --git a/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Dark.png.meta b/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Dark.png.meta
new file mode 100644
index 000000000..ca08bde84
--- /dev/null
+++ b/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Dark.png.meta
@@ -0,0 +1,53 @@
+fileFormatVersion: 2
+guid: da2299cb159f904459216ca87bce9dbb
+TextureImporter:
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: .25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 1024
+ textureSettings:
+ filterMode: -1
+ aniso: 1
+ mipBias: -1
+ wrapMode: 1
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: .5, y: .5}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 1
+ textureType: 2
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ spritePackingTag:
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector
+ Dark.png
+ uploadId: 667566
diff --git a/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Light.png b/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Light.png
new file mode 100644
index 000000000..6986ea44a
Binary files /dev/null and b/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Light.png differ
diff --git a/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Light.png.meta b/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Light.png.meta
new file mode 100644
index 000000000..d6512730d
--- /dev/null
+++ b/Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector Light.png.meta
@@ -0,0 +1,53 @@
+fileFormatVersion: 2
+guid: a69d5c321f729f1499ac11e52f110452
+TextureImporter:
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: .25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 1024
+ textureSettings:
+ filterMode: -1
+ aniso: 1
+ mipBias: -1
+ wrapMode: 1
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: .5, y: .5}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 1
+ textureType: 2
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ spritePackingTag:
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Editor Default Resources/Dialogue System/DialogueManager Inspector
+ Light.png
+ uploadId: 667566
diff --git a/Assets/Editor Default Resources/Dialogue System/EditorNode.png b/Assets/Editor Default Resources/Dialogue System/EditorNode.png
new file mode 100644
index 000000000..ec09381c0
Binary files /dev/null and b/Assets/Editor Default Resources/Dialogue System/EditorNode.png differ
diff --git a/Assets/Editor Default Resources/Dialogue System/EditorNode.png.meta b/Assets/Editor Default Resources/Dialogue System/EditorNode.png.meta
new file mode 100644
index 000000000..2038fefe8
--- /dev/null
+++ b/Assets/Editor Default Resources/Dialogue System/EditorNode.png.meta
@@ -0,0 +1,65 @@
+fileFormatVersion: 2
+guid: 9a03d2f508e30c24e826a259b1c825ee
+timeCreated: 1549325847
+licenseType: Store
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ linearTexture: 0
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ cubemapConvolution: 0
+ cubemapConvolutionSteps: 7
+ cubemapConvolutionExponent: 1.5
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 2048
+ textureSettings:
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapMode: -1
+ nPOTScale: 1
+ lightmap: 0
+ rGBM: 0
+ compressionQuality: 50
+ allowsAlphaSplitting: 0
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 0
+ textureType: -1
+ buildTargetSettings: []
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Editor Default Resources/Dialogue System/EditorNode.png
+ uploadId: 667566
diff --git a/Assets/Editor Default Resources/Dialogue System/Event.png b/Assets/Editor Default Resources/Dialogue System/Event.png
new file mode 100644
index 000000000..f66e366c3
Binary files /dev/null and b/Assets/Editor Default Resources/Dialogue System/Event.png differ
diff --git a/Assets/Editor Default Resources/Dialogue System/Event.png.meta b/Assets/Editor Default Resources/Dialogue System/Event.png.meta
new file mode 100644
index 000000000..f3aee0738
--- /dev/null
+++ b/Assets/Editor Default Resources/Dialogue System/Event.png.meta
@@ -0,0 +1,95 @@
+fileFormatVersion: 2
+guid: fe004ffe580a6b74eb585e93d5ba113d
+timeCreated: 1593887019
+licenseType: Store
+TextureImporter:
+ fileIDToRecycleName: {}
+ externalObjects: {}
+ serializedVersion: 4
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 0
+ aniso: -1
+ mipBias: -1
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ platformSettings:
+ - buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ - buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Editor Default Resources/Dialogue System/Event.png
+ uploadId: 667566
diff --git a/Assets/Editor Default Resources/Dialogue System/Resize.png b/Assets/Editor Default Resources/Dialogue System/Resize.png
new file mode 100644
index 000000000..cf15b0d02
Binary files /dev/null and b/Assets/Editor Default Resources/Dialogue System/Resize.png differ
diff --git a/Assets/Editor Default Resources/Dialogue System/Resize.png.meta b/Assets/Editor Default Resources/Dialogue System/Resize.png.meta
new file mode 100644
index 000000000..c8a7d59be
--- /dev/null
+++ b/Assets/Editor Default Resources/Dialogue System/Resize.png.meta
@@ -0,0 +1,95 @@
+fileFormatVersion: 2
+guid: 018dc0a4dc45ff14eb2b84d0c6f4e3f7
+timeCreated: 1621890141
+licenseType: Store
+TextureImporter:
+ fileIDToRecycleName: {}
+ externalObjects: {}
+ serializedVersion: 4
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 1
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: -1
+ aniso: -1
+ mipBias: -1
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ nPOTScale: 1
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ platformSettings:
+ - buildTarget: DefaultTexturePlatform
+ maxTextureSize: 32
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ - buildTarget: Standalone
+ maxTextureSize: 32
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Editor Default Resources/Dialogue System/Resize.png
+ uploadId: 667566
diff --git a/Assets/Editor Default Resources/Dialogue System/Script.png b/Assets/Editor Default Resources/Dialogue System/Script.png
new file mode 100644
index 000000000..16957a865
Binary files /dev/null and b/Assets/Editor Default Resources/Dialogue System/Script.png differ
diff --git a/Assets/Editor Default Resources/Dialogue System/Script.png.meta b/Assets/Editor Default Resources/Dialogue System/Script.png.meta
new file mode 100644
index 000000000..2118f71ab
--- /dev/null
+++ b/Assets/Editor Default Resources/Dialogue System/Script.png.meta
@@ -0,0 +1,95 @@
+fileFormatVersion: 2
+guid: cfb2ddf8e3923324ea96e23ee67d79b6
+timeCreated: 1590451810
+licenseType: Store
+TextureImporter:
+ fileIDToRecycleName: {}
+ externalObjects: {}
+ serializedVersion: 4
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 0
+ aniso: -1
+ mipBias: -1
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ nPOTScale: 2
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ platformSettings:
+ - buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ - buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Editor Default Resources/Dialogue System/Script.png
+ uploadId: 667566
diff --git a/Assets/Editor Default Resources/Dialogue System/Sequence.png b/Assets/Editor Default Resources/Dialogue System/Sequence.png
new file mode 100644
index 000000000..3ddb09363
Binary files /dev/null and b/Assets/Editor Default Resources/Dialogue System/Sequence.png differ
diff --git a/Assets/Editor Default Resources/Dialogue System/Sequence.png.meta b/Assets/Editor Default Resources/Dialogue System/Sequence.png.meta
new file mode 100644
index 000000000..5f8886771
--- /dev/null
+++ b/Assets/Editor Default Resources/Dialogue System/Sequence.png.meta
@@ -0,0 +1,95 @@
+fileFormatVersion: 2
+guid: 68479939e81df0542b3f4449e7bab4a1
+timeCreated: 1590451809
+licenseType: Store
+TextureImporter:
+ fileIDToRecycleName: {}
+ externalObjects: {}
+ serializedVersion: 4
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ sRGBTexture: 1
+ linearTexture: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapsPreserveCoverage: 0
+ alphaTestReferenceValue: 0.5
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: 0.25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 6
+ cubemapConvolution: 0
+ seamlessCubemap: 0
+ textureFormat: 1
+ maxTextureSize: 2048
+ textureSettings:
+ serializedVersion: 2
+ filterMode: 0
+ aniso: -1
+ mipBias: -1
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ nPOTScale: 2
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: 0.5, y: 0.5}
+ spritePixelsToUnits: 100
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spriteGenerateFallbackPhysicsShape: 1
+ alphaUsage: 1
+ alphaIsTransparency: 1
+ spriteTessellationDetail: -1
+ textureType: 0
+ textureShape: 1
+ maxTextureSizeSet: 0
+ compressionQualitySet: 0
+ textureFormatSet: 0
+ platformSettings:
+ - buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ - buildTarget: Standalone
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 0
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ spritePackingTag:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Editor Default Resources/Dialogue System/Sequence.png
+ uploadId: 667566
diff --git a/Assets/Gizmos/DialogueDatabase Icon.png b/Assets/Gizmos/DialogueDatabase Icon.png
new file mode 100644
index 000000000..89e417ea6
Binary files /dev/null and b/Assets/Gizmos/DialogueDatabase Icon.png differ
diff --git a/Assets/Gizmos/DialogueDatabase Icon.png.meta b/Assets/Gizmos/DialogueDatabase Icon.png.meta
new file mode 100644
index 000000000..314fe5ac8
--- /dev/null
+++ b/Assets/Gizmos/DialogueDatabase Icon.png.meta
@@ -0,0 +1,52 @@
+fileFormatVersion: 2
+guid: 8c00072895782fb4d918363c8ff501a5
+TextureImporter:
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: .25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 1024
+ textureSettings:
+ filterMode: -1
+ aniso: 1
+ mipBias: -1
+ wrapMode: 1
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: .5, y: .5}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 1
+ textureType: 2
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ spritePackingTag:
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Gizmos/DialogueDatabase Icon.png
+ uploadId: 667566
diff --git a/Assets/Gizmos/StringAsset Icon.png b/Assets/Gizmos/StringAsset Icon.png
new file mode 100644
index 000000000..e416b9a37
Binary files /dev/null and b/Assets/Gizmos/StringAsset Icon.png differ
diff --git a/Assets/Gizmos/StringAsset Icon.png.meta b/Assets/Gizmos/StringAsset Icon.png.meta
new file mode 100644
index 000000000..ecb5ad6c3
--- /dev/null
+++ b/Assets/Gizmos/StringAsset Icon.png.meta
@@ -0,0 +1,54 @@
+fileFormatVersion: 2
+guid: 4f2936fe28db54943a07b18c4366f96d
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: .25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 64
+ textureSettings:
+ filterMode: -1
+ aniso: 1
+ mipBias: -1
+ wrapMode: 1
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: .5, y: .5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 1
+ textureType: 2
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ spritePackingTag:
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Gizmos/StringAsset Icon.png
+ uploadId: 667566
diff --git a/Assets/Gizmos/TextTable Icon.png b/Assets/Gizmos/TextTable Icon.png
new file mode 100644
index 000000000..2366847f7
Binary files /dev/null and b/Assets/Gizmos/TextTable Icon.png differ
diff --git a/Assets/Gizmos/TextTable Icon.png.meta b/Assets/Gizmos/TextTable Icon.png.meta
new file mode 100644
index 000000000..4841fbd63
--- /dev/null
+++ b/Assets/Gizmos/TextTable Icon.png.meta
@@ -0,0 +1,54 @@
+fileFormatVersion: 2
+guid: f043073205c249748ae651e1518ced37
+TextureImporter:
+ fileIDToRecycleName: {}
+ serializedVersion: 2
+ mipmaps:
+ mipMapMode: 0
+ enableMipMap: 0
+ linearTexture: 1
+ correctGamma: 0
+ fadeOut: 0
+ borderMipMap: 0
+ mipMapFadeDistanceStart: 1
+ mipMapFadeDistanceEnd: 3
+ bumpmap:
+ convertToNormalMap: 0
+ externalNormalMap: 0
+ heightScale: .25
+ normalMapFilter: 0
+ isReadable: 0
+ grayScaleToAlpha: 0
+ generateCubemap: 0
+ seamlessCubemap: 0
+ textureFormat: -1
+ maxTextureSize: 64
+ textureSettings:
+ filterMode: -1
+ aniso: 1
+ mipBias: -1
+ wrapMode: 1
+ nPOTScale: 0
+ lightmap: 0
+ compressionQuality: 50
+ spriteMode: 0
+ spriteExtrude: 1
+ spriteMeshType: 1
+ alignment: 0
+ spritePivot: {x: .5, y: .5}
+ spriteBorder: {x: 0, y: 0, z: 0, w: 0}
+ spritePixelsToUnits: 100
+ alphaIsTransparency: 1
+ textureType: 2
+ buildTargetSettings: []
+ spriteSheet:
+ sprites: []
+ spritePackingTag:
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Gizmos/TextTable Icon.png
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers.meta b/Assets/Plugins/Pixel Crushers.meta
new file mode 100644
index 000000000..564c3d94e
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: e548d83123d220b4ca31fa7813b93169
+folderAsset: yes
+timeCreated: 1523621309
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common.meta b/Assets/Plugins/Pixel Crushers/Common.meta
new file mode 100644
index 000000000..06a118847
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 464fa5fb7b9423447b820fb1586bc377
+folderAsset: yes
+timeCreated: 1549415913
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Documentation.meta b/Assets/Plugins/Pixel Crushers/Common/Documentation.meta
new file mode 100644
index 000000000..7dc37235e
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Documentation.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: d251ab3a82057f646b86d4680ecf98fa
+folderAsset: yes
+timeCreated: 1549415913
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Documentation/Input_Device_Manager_Manual.pdf b/Assets/Plugins/Pixel Crushers/Common/Documentation/Input_Device_Manager_Manual.pdf
new file mode 100644
index 000000000..1c87980f7
Binary files /dev/null and b/Assets/Plugins/Pixel Crushers/Common/Documentation/Input_Device_Manager_Manual.pdf differ
diff --git a/Assets/Plugins/Pixel Crushers/Common/Documentation/Input_Device_Manager_Manual.pdf.meta b/Assets/Plugins/Pixel Crushers/Common/Documentation/Input_Device_Manager_Manual.pdf.meta
new file mode 100644
index 000000000..766cf84c8
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Documentation/Input_Device_Manager_Manual.pdf.meta
@@ -0,0 +1,16 @@
+fileFormatVersion: 2
+guid: 846626fbdca7bc043b03974780ce4ded
+timeCreated: 1583293279
+licenseType: Store
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Documentation/Input_Device_Manager_Manual.pdf
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Documentation/Save_System_Manual.pdf b/Assets/Plugins/Pixel Crushers/Common/Documentation/Save_System_Manual.pdf
new file mode 100644
index 000000000..59865de0b
Binary files /dev/null and b/Assets/Plugins/Pixel Crushers/Common/Documentation/Save_System_Manual.pdf differ
diff --git a/Assets/Plugins/Pixel Crushers/Common/Documentation/Save_System_Manual.pdf.meta b/Assets/Plugins/Pixel Crushers/Common/Documentation/Save_System_Manual.pdf.meta
new file mode 100644
index 000000000..2400e5ec8
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Documentation/Save_System_Manual.pdf.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 078b161b9252e2a49ae78f3274016e9a
+DefaultImporter:
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Documentation/Save_System_Manual.pdf
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Documentation/Text_Table_Manual.pdf b/Assets/Plugins/Pixel Crushers/Common/Documentation/Text_Table_Manual.pdf
new file mode 100644
index 000000000..a07ba4d4a
Binary files /dev/null and b/Assets/Plugins/Pixel Crushers/Common/Documentation/Text_Table_Manual.pdf differ
diff --git a/Assets/Plugins/Pixel Crushers/Common/Documentation/Text_Table_Manual.pdf.meta b/Assets/Plugins/Pixel Crushers/Common/Documentation/Text_Table_Manual.pdf.meta
new file mode 100644
index 000000000..3efccfaea
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Documentation/Text_Table_Manual.pdf.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 35e24f51cfa33ae45875a60c94afe1c4
+timeCreated: 1547864529
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Documentation/Text_Table_Manual.pdf
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts.meta
new file mode 100644
index 000000000..212a3e32a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 31a7ee3bf0d4f32409ed16f46d687b3b
+folderAsset: yes
+timeCreated: 1549415913
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/CommonAssemblyDefinitions.unitypackage.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/CommonAssemblyDefinitions.unitypackage.meta
new file mode 100644
index 000000000..7cd4b5733
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/CommonAssemblyDefinitions.unitypackage.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 4d9b575363cdb56408d92f7d7f0e5216
+timeCreated: 1533129795
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/CommonAssemblyDefinitions.unitypackage
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor.meta
new file mode 100644
index 000000000..ae05e634d
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: ad10e7c76ead24541bec6a9d58e509cc
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Message System.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Message System.meta
new file mode 100644
index 000000000..354e8440a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Message System.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 402663108598fef46a3589f86b6ce181
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Message System/MessageEventsEditor.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Message System/MessageEventsEditor.cs
new file mode 100644
index 000000000..6cf71e8f7
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Message System/MessageEventsEditor.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ [CustomEditor(typeof(MessageEvents), true)]
+ public class MessageEventsEditor : Editor
+ {
+
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+ EditorGUILayout.HelpBox("In Messages To Listen For, add messages to listen for and the events that should occur when these messages are received.", MessageType.None);
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("m_messagesToListenFor"), true);
+ EditorGUILayout.HelpBox("In Messages To Send, configure messages that you want to send. To send them, call SendToMessageSystem(index) with the index of the element in Messages To Send.", MessageType.None);
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("m_messagesToSend"), true);
+ serializedObject.ApplyModifiedProperties();
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Message System/MessageEventsEditor.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Message System/MessageEventsEditor.cs.meta
new file mode 100644
index 000000000..49e6ef0d2
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Message System/MessageEventsEditor.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 4046055df9a0a9647a98be75adc715b8
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Message System/MessageEventsEditor.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc.meta
new file mode 100644
index 000000000..b21a4e15d
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: fa99048c69f821847a3a7e8bf605c3b3
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/AssetUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/AssetUtility.cs
new file mode 100644
index 000000000..c5d8d1440
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/AssetUtility.cs
@@ -0,0 +1,127 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+using System.IO;
+
+namespace PixelCrushers
+{
+
+ public static class AssetUtility
+ {
+
+ ///
+ /// Creates a new ScriptableObject asset in the project's asset database.
+ ///
+ /// The ScriptableObject type.
+ /// The type name to use when naming the asset file in the project's asset database.
+ /// If true, select the asset.
+ /// If true, prepend 'New' to the beginning of the asset name.
+ /// The asset.
+ public static T CreateAsset(string typeName, bool select = true, bool prependNew = true) where T : ScriptableObject
+ {
+ var asset = ScriptableObjectUtility.CreateScriptableObject() as T;
+ SaveAsset(asset, typeName, select, prependNew);
+ return asset;
+ }
+
+ ///
+ /// Creates a new ScriptableObject asset in the project's asset database.
+ ///
+ /// The ScriptableObject type.
+ /// The type name to use when naming the asset file in the project's asset database.
+ /// If true, select the asset.
+ /// If true, prepend 'New' to the beginning of the asset name.
+ /// The asset.
+ public static ScriptableObject CreateAsset(System.Type type, string typeName, bool select = true, bool prependNew = true)
+ {
+ var asset = ScriptableObjectUtility.CreateScriptableObject(type);
+ SaveAsset(asset, typeName, select, prependNew);
+ return asset;
+ }
+
+ ///
+ /// Creates a new ScriptableObject asset in the project's asset database.
+ ///
+ /// The ScriptableObject type.
+ /// The filename to save the asset as.
+ /// If true, select the asset.
+ /// The asset.
+ public static T CreateAssetWithFilename(string filename, bool select = true) where T : ScriptableObject
+ {
+ var asset = ScriptableObjectUtility.CreateScriptableObject() as T;
+ SaveAssetWithFilename(asset, filename, select);
+ return asset;
+ }
+
+ ///
+ /// Creates a new ScriptableObject asset in the project's asset database.
+ ///
+ /// The ScriptableObject type.
+ /// The filename to save the asset as.
+ /// If true, select the asset.
+ /// The asset.
+ public static ScriptableObject CreateAssetWithFilename(System.Type type, string filename, bool select = true)
+ {
+ var asset = ScriptableObjectUtility.CreateScriptableObject(type);
+ SaveAssetWithFilename(asset, filename, select);
+ return asset;
+ }
+
+
+ private static void SaveAsset(ScriptableObject asset, string typeName, bool select = true, bool prependNew = true)
+ {
+ string path = AssetDatabase.GetAssetPath(Selection.activeObject);
+ if (string.IsNullOrEmpty(path))
+ {
+ path = "Assets";
+ }
+ else if (!string.IsNullOrEmpty(Path.GetExtension(path)))
+ {
+ path = path.Replace(Path.GetFileName(AssetDatabase.GetAssetPath(Selection.activeObject)), "");
+ }
+ string assetPathAndName = AssetDatabase.GenerateUniqueAssetPath(path + (prependNew ? "/New " : "/") + typeName + ".asset");
+ SaveAssetWithFilename(asset, assetPathAndName, select);
+ }
+
+ private static void SaveAssetWithFilename(ScriptableObject asset, string filename, bool select = true)
+ {
+ AssetDatabase.CreateAsset(asset, filename);
+ //AssetDatabase.SaveAssets();
+ if (select)
+ {
+ EditorUtility.FocusProjectWindow();
+ Selection.activeObject = asset;
+ }
+ }
+
+ ///
+ /// (Editor only) Registers a ScriptableObject as part of an asset in the project's asset database.
+ ///
+ /// The ScriptableObject to add.
+ /// The asset to add the ScriptableObject to.
+ public static void AddToAsset(ScriptableObject scriptableObject, UnityEngine.Object asset)
+ {
+ scriptableObject.hideFlags = HideFlags.HideInHierarchy;
+ AssetDatabase.AddObjectToAsset(scriptableObject, asset);
+ //AssetDatabase.ImportAsset(AssetDatabase.GetAssetPath(scriptableObject));
+ //AssetDatabase.SaveAssets();
+ //AssetDatabase.Refresh();
+ }
+
+ ///
+ /// (Editor only) Deletes a ScriptableObject from an asset.
+ ///
+ /// The ScriptableObject to add.
+ /// The asset to delete the ScriptableObject from.
+ public static void DeleteFromAsset(ScriptableObject scriptableObject, UnityEngine.Object asset)
+ {
+ if (scriptableObject == null) return;
+ UnityEngine.Object.DestroyImmediate(scriptableObject, true);
+ //AssetDatabase.SaveAssets();
+ //AssetDatabase.Refresh();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/AssetUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/AssetUtility.cs.meta
new file mode 100644
index 000000000..56e54510b
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/AssetUtility.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 07303eff990b078489da752f47820b89
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/AssetUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EditorGUIZoomArea.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EditorGUIZoomArea.cs
new file mode 100644
index 000000000..b213b0051
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EditorGUIZoomArea.cs
@@ -0,0 +1,38 @@
+// From "Unity Editor Window Zooming" by Martin Ecker.
+// http://martinecker.com/martincodes/unity-editor-window-zooming/
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ public class EditorGUIZoomArea
+ {
+ private const float kEditorWindowTabHeight = 21.0f;
+ private static Matrix4x4 _prevGuiMatrix;
+
+ public static Rect Begin(float zoomScale, Rect screenCoordsArea)
+ {
+ GUI.EndGroup(); // End the group Unity begins automatically for an EditorWindow to clip out the window tab. This allows us to draw outside of the size of the EditorWindow.
+
+ Rect clippedArea = screenCoordsArea.ScaleSizeBy(1.0f / zoomScale, screenCoordsArea.TopLeft());
+ clippedArea.y += kEditorWindowTabHeight;
+ GUI.BeginGroup(clippedArea);
+
+ _prevGuiMatrix = GUI.matrix;
+ Matrix4x4 translation = Matrix4x4.TRS(clippedArea.TopLeft(), Quaternion.identity, Vector3.one);
+ Matrix4x4 scale = Matrix4x4.Scale(new Vector3(zoomScale, zoomScale, 1.0f));
+ GUI.matrix = translation * scale * translation.inverse * GUI.matrix;
+
+ return clippedArea;
+ }
+
+ public static void End()
+ {
+ GUI.matrix = _prevGuiMatrix;
+ GUI.EndGroup();
+ GUI.BeginGroup(new Rect(0.0f, kEditorWindowTabHeight, Screen.width, Screen.height));
+ }
+ }
+}
+
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EditorGUIZoomArea.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EditorGUIZoomArea.cs.meta
new file mode 100644
index 000000000..64f673d7c
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EditorGUIZoomArea.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: fd7defe51a4f2314295968cbe8560d17
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EditorGUIZoomArea.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EnablePhysics2DMenuItem.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EnablePhysics2DMenuItem.cs
new file mode 100644
index 000000000..f08b23f47
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EnablePhysics2DMenuItem.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+#if UNITY_2018_1_OR_NEWER
+ public static class EnablePhysics2DMenuItem
+ {
+
+ [MenuItem("Tools/Pixel Crushers/Common/Misc/Enable Physics2D Support...", false, 100)]
+ static public void AddUSEPHYSICS2D()
+ {
+ if (EditorUtility.DisplayDialog("Enable Physics2D Support", "If your project uses 2D Physics, press OK to enable Pixel Crushers support for 2D Physics.", "OK", "Cancel"))
+ {
+ MoreEditorUtility.TryAddScriptingDefineSymbols("USE_PHYSICS2D");
+ EditorUtility.DisplayDialog("Physics2D Support Enabled", "Support for 2D Physics has been enabled. You may need to right-click on the Plugins/Pixel Crushers folder and select Reimport to recompile the scripts with 2D Physics support. If you add build platforms, you may need to select this menu item again.", "OK");
+ }
+ }
+
+ [MenuItem("Tools/Pixel Crushers/Common/Misc/Enable Physics2D Support...", true)]
+ static bool ValidateAddUSEPHYSICS2D()
+ {
+ return !MoreEditorUtility.DoesScriptingDefineSymbolExist("USE_PHYSICS2D");
+ }
+
+ }
+#endif
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EnablePhysics2DMenuItem.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EnablePhysics2DMenuItem.cs.meta
new file mode 100644
index 000000000..094df82cf
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EnablePhysics2DMenuItem.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: e2b871fdb938485479e342fcab452149
+timeCreated: 1539308973
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/EnablePhysics2DMenuItem.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/HelpBoxAttributeDrawer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/HelpBoxAttributeDrawer.cs
new file mode 100644
index 000000000..00116641a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/HelpBoxAttributeDrawer.cs
@@ -0,0 +1,59 @@
+#if !ODIN_INSPECTOR
+// Pending fix from Sirenix, to prevent Odin stack overflow bug we don't draw help boxes if Odin in installed.
+
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ [CustomPropertyDrawer(typeof(HelpBoxAttribute))]
+ public class HelpBoxAttributeDrawer : DecoratorDrawer
+ {
+
+ public override float GetHeight()
+ {
+ try
+ {
+ var helpBoxAttribute = attribute as HelpBoxAttribute;
+ if (helpBoxAttribute == null) return base.GetHeight();
+
+ var helpBoxStyle = (GUI.skin != null) ? GUI.skin.GetStyle("helpbox") : null;
+ if (helpBoxStyle == null) return base.GetHeight();
+
+ return Mathf.Max(40f, helpBoxStyle.CalcHeight(new GUIContent(helpBoxAttribute.text), EditorGUIUtility.currentViewWidth) + 4);
+ }
+ catch (System.ArgumentException) // Handles IMGUI->UITK bug in Unity 2022.2.
+ {
+ return 3 * EditorGUIUtility.singleLineHeight;
+ }
+ }
+
+ public override void OnGUI(Rect position)
+ {
+ var helpBoxAttribute = attribute as HelpBoxAttribute;
+ if (helpBoxAttribute == null) return;
+ EditorGUI.HelpBox(position, helpBoxAttribute.text, GetMessageType(helpBoxAttribute.messageType));
+ }
+
+ private MessageType GetMessageType(HelpBoxMessageType helpBoxMessageType)
+ {
+ switch (helpBoxMessageType)
+ {
+ default:
+ case HelpBoxMessageType.None:
+ return MessageType.None;
+ case HelpBoxMessageType.Info:
+ return MessageType.Info;
+ case HelpBoxMessageType.Warning:
+ return MessageType.Warning;
+ case HelpBoxMessageType.Error:
+ return MessageType.Error;
+ }
+ }
+
+ }
+}
+#endif
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/HelpBoxAttributeDrawer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/HelpBoxAttributeDrawer.cs.meta
new file mode 100644
index 000000000..c0f1684a5
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/HelpBoxAttributeDrawer.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 6b7fff4cb8c61f14ab57e6cdd5469595
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/HelpBoxAttributeDrawer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorGuiUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorGuiUtility.cs
new file mode 100644
index 000000000..6d43934a1
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorGuiUtility.cs
@@ -0,0 +1,113 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ public static class MoreEditorGuiUtility
+ {
+
+ // // Evaluation version headaches. Get it at runtime instead:
+ //#if UNITY_2022_3_OR_NEWER && !UNITY_2022_3_0
+ // public const string ToolbarSearchTextFieldName = "ToolbarSearchTextField";
+ // public const string ToolbarSearchCancelButtonName = "ToolbarSearchCancelButton";
+ // public const string ToolbarSearchCancelButtonEmpty = "ToolbarSearchCancelButtonEmpty";
+ //#else
+ // public const string ToolbarSearchTextFieldName = "ToolbarSeachTextField";
+ // public const string ToolbarSearchCancelButtonName = "ToolbarSeachCancelButton";
+ // public const string ToolbarSearchCancelButtonEmpty = "ToolbarSeachCancelButtonEmpty";
+ //#endif
+
+ private static GUIStyle toolbarSearchTextFieldStyle = null;
+ private static GUIStyle toolbarSearchCancelButtonStyle = null;
+ private static GUIStyle toolbarSearchCancelButtonEmptyStyle = null;
+ public static string ToolbarSearchTextFieldName
+ {
+ get
+ {
+ if (toolbarSearchTextFieldStyle == null)
+ {
+ toolbarSearchTextFieldStyle = FindCustomStyle("ToolbarSearchTextField", "ToolbarSeachTextField");
+ }
+ return toolbarSearchTextFieldStyle.name;
+ }
+ }
+ public static string ToolbarSearchCancelButtonName
+ {
+ get
+ {
+ if (toolbarSearchCancelButtonStyle == null)
+ {
+ toolbarSearchCancelButtonStyle = FindCustomStyle("ToolbarSearchCancelButton", "ToolbarSeachCancelButton");
+ }
+ return toolbarSearchCancelButtonStyle.name;
+ }
+ }
+ public static string ToolbarSearchCancelButtonEmpty
+ {
+ get
+ {
+ if (toolbarSearchCancelButtonEmptyStyle == null)
+ {
+ toolbarSearchCancelButtonEmptyStyle = FindCustomStyle("ToolbarSearchCancelButtonEmpty", "ToolbarSeachCancelButtonEmpty");
+ }
+ return toolbarSearchCancelButtonEmptyStyle.name;
+ }
+ }
+ private static GUIStyle FindCustomStyle(string name1, string name2)
+ {
+ foreach (var style in GUI.skin.customStyles)
+ {
+ if ((style.name == name1) || (style.name == name2)) return style;
+ }
+ return GUI.skin.label;
+ }
+
+ public const float GearWidth = 15;
+ public const float GearHeight = 14;
+
+ private static GUIStyle m_gearMenuGuiStyle = null;
+
+ ///
+ /// Draws a gear menu button.
+ ///
+ /// true if the button was clicked; otherwise false.
+ public static bool DoGearMenu(Rect rect)
+ {
+ if (!LoadGearStyle()) return false;
+ return GUI.Button(rect, GUIContent.none, m_gearMenuGuiStyle);
+ }
+
+ ///
+ /// Draws a gear menu button in GUI layout mode.
+ ///
+ /// true if the button was clicked; otherwise false.
+ public static bool DoLayoutGearMenu()
+ {
+ if (!LoadGearStyle()) return false;
+ return GUILayout.Button(GUIContent.none, m_gearMenuGuiStyle, GUILayout.Width(GearWidth), GUILayout.Height(GearHeight));
+ }
+
+ private static bool LoadGearStyle()
+ {
+ if (m_gearMenuGuiStyle == null)
+ {
+ var textureName = EditorGUIUtility.isProSkin ? "icons/d__Popup.png" : "icons/_Popup.png";
+ Texture2D gearTexture = EditorGUIUtility.Load(textureName) as Texture2D;
+ m_gearMenuGuiStyle = new GUIStyle(GUI.skin.label);
+ m_gearMenuGuiStyle.normal.background = gearTexture;
+ m_gearMenuGuiStyle.active.background = gearTexture;
+ m_gearMenuGuiStyle.focused.background = gearTexture;
+ m_gearMenuGuiStyle.hover.background = gearTexture;
+ m_gearMenuGuiStyle.onNormal.background = gearTexture;
+ m_gearMenuGuiStyle.onActive.background = gearTexture;
+ m_gearMenuGuiStyle.onFocused.background = gearTexture;
+ m_gearMenuGuiStyle.onHover.background = gearTexture;
+ }
+ return (m_gearMenuGuiStyle != null);
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorGuiUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorGuiUtility.cs.meta
new file mode 100644
index 000000000..498ae241a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorGuiUtility.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 13b677070f7ca2648ba2f9dc29726594
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorGuiUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorUtility.cs
new file mode 100644
index 000000000..e2ee3b9a6
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorUtility.cs
@@ -0,0 +1,258 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using UnityEditor;
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ public static class MoreEditorUtility
+ {
+
+ // These two methods handle API differences:
+
+ public static string GetScriptingDefineSymbolsForGroup(BuildTargetGroup group)
+ {
+#if UNITY_2023_1_OR_NEWER
+ return PlayerSettings.GetScriptingDefineSymbols(UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(group));
+#else
+ return PlayerSettings.GetScriptingDefineSymbolsForGroup(group);
+#endif
+ }
+
+ public static void SetScriptingDefineSymbolsForGroup(BuildTargetGroup group, string defines)
+ {
+#if UNITY_2023_1_OR_NEWER
+ PlayerSettings.SetScriptingDefineSymbols(UnityEditor.Build.NamedBuildTarget.FromBuildTargetGroup(group), defines);
+#else
+ PlayerSettings.SetScriptingDefineSymbolsForGroup(group, defines);
+#endif
+ }
+
+ ///
+ /// Checks if a symbol exists in the project's Scripting Define Symbols for the current build target.
+ ///
+ public static bool DoesScriptingDefineSymbolExist(string symbol)
+ {
+ var defines = GetScriptingDefineSymbolsForGroup(EditorUserBuildSettings.selectedBuildTargetGroup).Split(';');
+ for (int i = 0; i < defines.Length; i++)
+ {
+ if (string.Equals(symbol, defines[i].Trim())) return true;
+ }
+ return false;
+ }
+
+ public static HashSet GetInstalledBuildTargetGroups()
+ {
+#if UNITY_2017
+ Debug.Log("Updating all build targets. Please ignore messages about build targets not installed.");
+#endif
+ var result = new HashSet();
+ foreach (BuildTarget target in (BuildTarget[])Enum.GetValues(typeof(BuildTarget)))
+ {
+ BuildTargetGroup group = BuildPipeline.GetBuildTargetGroup(target);
+#if UNITY_2018_1_OR_NEWER
+ if (BuildPipeline.IsBuildTargetSupported(group, target))
+#endif
+ {
+ result.Add(group);
+ }
+ }
+ return result;
+ }
+
+ ///
+ /// Try to add a symbol to the project's Scripting Define Symbols for all build targets.
+ ///
+ public static void TryAddScriptingDefineSymbols(string symbol, bool touchFiles = false)
+ {
+ foreach (var group in GetInstalledBuildTargetGroups())
+ {
+ try
+ {
+ var defines = GetScriptingDefineSymbolsForGroup(group);
+ if (!string.IsNullOrEmpty(defines)) defines += ";";
+ defines += symbol;
+ SetScriptingDefineSymbolsForGroup(group, defines);
+ }
+ catch (Exception e)
+ {
+ Debug.LogException(e);
+ }
+ }
+ if (touchFiles) TouchScriptsWithScriptingSymbol(symbol);
+ RecompileScripts();
+ }
+
+ ///
+ /// Try to remove a symbol from the project's Scripting Define Symbols for all build targets.
+ ///
+ public static void TryRemoveScriptingDefineSymbols(string symbol)
+ {
+ foreach (var group in GetInstalledBuildTargetGroups())
+ {
+ try
+ {
+ var symbols = new List(GetScriptingDefineSymbolsForGroup(group).Split(';'));
+ symbols.Remove(symbol);
+ var defines = string.Join(";", symbols.ToArray());
+ SetScriptingDefineSymbolsForGroup(group, defines);
+ }
+ catch (Exception e)
+ {
+ Debug.LogException(e);
+ }
+ }
+ RecompileScripts();
+ }
+
+ ///
+ /// Add or remove a scripting define symbol.
+ ///
+ public static void ToggleScriptingDefineSymbol(string define, bool value, bool touchFiles = false)
+ {
+ if (value == true) TryAddScriptingDefineSymbols(define, touchFiles);
+ else TryRemoveScriptingDefineSymbols(define);
+ }
+
+ ///
+ /// Triggers a script recompile.
+ ///
+ public static void RecompileScripts()
+ {
+ AssetDatabase.SaveAssets();
+ AssetDatabase.Refresh();
+#if UNITY_2019_3_OR_NEWER
+ EditorUtility.RequestScriptReload();
+#else
+ UnityEditorInternal.InternalEditorUtility.RequestScriptReload();
+#endif
+ }
+
+ ///
+ /// The only reliable way to force a recompile and get the editor to recognize
+ /// MonoBehaviour scripts and wrappers in Plugins is to actually change those
+ /// files. :/
+ ///
+ /// Touch files that cehck this scripting symbol.
+ public static void TouchScriptsWithScriptingSymbol(string symbol)
+ {
+ var path = Application.dataPath + "/Plugins/Pixel Crushers/";
+ if (Application.platform == RuntimePlatform.WindowsEditor)
+ {
+ path = path.Replace("/", "\\");
+ }
+ if (!Directory.Exists(path))
+ {
+ Debug.Log("It looks like you've moved this Pixel Crushers asset. In the Project view, please right-click on the folder in its new location and select Reimport.");
+ }
+ else
+ {
+ string[] filenames = Directory.GetFiles(path, "*.cs", SearchOption.AllDirectories);
+ var found = string.Empty;
+ var recompileAtText = "// Recompile at " + DateTime.Now + "\r\n";
+ var searchString = "#if " + symbol;
+ foreach (string filename in filenames)
+ {
+ var text = File.ReadAllText(filename);
+ if (text.Contains(searchString))
+ {
+ found += filename + "\n";
+ if (text.StartsWith("// Recompile at "))
+ {
+ var lines = File.ReadAllLines(filename);
+ lines[0] = recompileAtText;
+ File.WriteAllLines(filename, lines);
+ }
+ else
+ {
+ text = recompileAtText + text;
+ File.WriteAllText(filename, text);
+ }
+ }
+ }
+ }
+ }
+
+ //=============================================================
+
+ [MenuItem("Tools/Pixel Crushers/Common/Misc/Enable TextMesh Pro Support...", false, 101)]
+ static public void AddTMPPRESENT()
+ {
+ if (EditorUtility.DisplayDialog("Enable TextMesh Pro Support", "This will enable TextMesh Pro support. Your project must already contain the TextMesh Pro package. To continue, press OK. If you need to install TextMesh Pro first, press Cancel.", "OK", "Cancel"))
+ {
+ MoreEditorUtility.TryAddScriptingDefineSymbols("TMP_PRESENT");
+ TouchScriptsWithScriptingSymbol("TMP_PRESENT");
+ EditorUtility.DisplayDialog("TextMesh Pro Support Enabled", "TextMesh Pro support has been enabled. You may need to right-click on the two files named TextMeshProTypewriterEffect and select Reimport to be able to add them to your GameObjects. If you change build platforms, you may need to select this menu item again.", "OK");
+ }
+ }
+
+ [MenuItem("Tools/Pixel Crushers/Common/Misc/Enable TextMesh Pro Support...", true)]
+ static bool ValidateAddTMPPRESENT()
+ {
+ return !MoreEditorUtility.DoesScriptingDefineSymbolExist("TMP_PRESENT");
+ }
+
+ //=============================================================
+
+ [MenuItem("Tools/Pixel Crushers/Common/Misc/Enable Super Text Mesh Support...", false, 101)]
+ static public void AddUSESTM()
+ {
+ if (EditorUtility.DisplayDialog("Enable Super Text Mesh Support", "This will enable Super Text Mesh support. Your project must already contain Super Text Mesh.\n\n*IMPORTANT*: Before pressing OK, you MUST move the Clavian folder into the Plugins folder!\n\nTo continue, press OK. If you need to install Super Text Mesh or move the folder first, press Cancel.", "OK", "Cancel"))
+ {
+ MoreEditorUtility.TryAddScriptingDefineSymbols("USE_STM");
+ EditorUtility.DisplayDialog("Super Text Mesh Support Enabled", "Super Text Mesh support has been enabled.", "OK");
+ }
+ }
+
+ [MenuItem("Tools/Pixel Crushers/Common/Misc/Enable Super Text Mesh Support...", true)]
+ static bool ValidateAddUSESTM()
+ {
+ return !MoreEditorUtility.DoesScriptingDefineSymbolExist("USE_STM");
+ }
+
+ //=============================================================
+
+#if UNITY_2019_1_OR_NEWER
+
+ [MenuItem("Tools/Pixel Crushers/Common/Misc/Use New Input System...", false, 102)]
+ static public void AddUSENEWINPUT()
+ {
+ if (EditorUtility.DisplayDialog("Use New Input System", "This will switch the Input Device Manager to read from Unity's new Input System. You must have already added the Input System package.\n\nSee the configuration manual in Plugins > Pixel Crushers > Common > Documentation.", "OK", "Cancel"))
+ {
+ MoreEditorUtility.TryAddScriptingDefineSymbols("USE_NEW_INPUT");
+ EditorUtility.DisplayDialog("Using New Input System", "See the configuration manual in Plugins > Pixel Crushers > Common > Documentation.", "OK");
+ }
+ }
+
+ [MenuItem("Tools/Pixel Crushers/Common/Misc/Use New Input System...", true)]
+ static bool ValidateAddUSENEWINPUT()
+ {
+ return !MoreEditorUtility.DoesScriptingDefineSymbolExist("USE_NEW_INPUT");
+ }
+
+#endif
+
+ //=============================================================
+
+ [MenuItem("Tools/Pixel Crushers/Common/Misc/Use NavMesh...", false, 103)]
+ static public void AddUSENAVMESH()
+ {
+ if (EditorUtility.DisplayDialog("Use NavMesh", "If your project uses Unity's NavMesh AI navigation system, this will enable Pixel Crushers support for it.", "OK", "Cancel"))
+ {
+ MoreEditorUtility.TryAddScriptingDefineSymbols("USE_NAVMESH");
+ EditorUtility.DisplayDialog("NavMesh Integration Enabled", "Pixel Crushers asset support for NavMeshes navigation is now enabled.", "OK");
+ }
+ }
+
+ [MenuItem("Tools/Pixel Crushers/Common/Misc/Use NavMesh...", true)]
+ static bool ValidateAddUSENAVMESH()
+ {
+ return !MoreEditorUtility.DoesScriptingDefineSymbolExist("USE_NAVMESH");
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorUtility.cs.meta
new file mode 100644
index 000000000..cad25bfe4
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorUtility.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: a110bd7252c4c8142b596a4b7eb0b524
+timeCreated: 1530297921
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/MoreEditorUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TimedEventEditor.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TimedEventEditor.cs
new file mode 100644
index 000000000..57091ca71
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TimedEventEditor.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ [CustomEditor(typeof(TimedEvent), true)]
+ public class TimedEventEditor : Editor
+ {
+ private SerializedProperty modeProperty;
+ private SerializedProperty durationProperty;
+ private SerializedProperty framesProperty;
+ private SerializedProperty activateOnStartProperty;
+ private SerializedProperty onTimeReachedProperty;
+
+ private void OnEnable()
+ {
+ modeProperty = serializedObject.FindProperty("m_mode");
+ durationProperty = serializedObject.FindProperty("m_duration");
+ framesProperty = serializedObject.FindProperty("m_frames");
+ activateOnStartProperty = serializedObject.FindProperty("m_activateOnStart");
+ onTimeReachedProperty = serializedObject.FindProperty("m_onTimeReached");
+ }
+
+ public override void OnInspectorGUI()
+ {
+ serializedObject.Update();
+ EditorGUILayout.PropertyField(modeProperty);
+ if (modeProperty.enumValueIndex == (int)TimedEvent.TimingMode.Frames)
+ {
+ EditorGUILayout.PropertyField(framesProperty);
+ }
+ else
+ {
+ EditorGUILayout.PropertyField(durationProperty);
+ }
+ EditorGUILayout.PropertyField(activateOnStartProperty);
+ EditorGUILayout.PropertyField(onTimeReachedProperty);
+ serializedObject.ApplyModifiedProperties();
+ }
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TimedEventEditor.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TimedEventEditor.cs.meta
new file mode 100644
index 000000000..580efd8c1
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TimedEventEditor.cs.meta
@@ -0,0 +1,20 @@
+fileFormatVersion: 2
+guid: a06a7c4f14779614f8f4c3dc3c2bcc41
+timeCreated: 1590110964
+licenseType: Store
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TimedEventEditor.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TypeUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TypeUtility.cs
new file mode 100644
index 000000000..27045990e
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TypeUtility.cs
@@ -0,0 +1,78 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+using System.Linq;
+using System.Collections.Generic;
+
+namespace PixelCrushers
+{
+
+ public static class TypeUtility
+ {
+
+ ///
+ /// Gets all non-abstract subtypes of a specified type.
+ ///
+ /// Parent type.
+ /// List of all non-abstract subtypes descended from the parent type.
+ public static List GetSubtypes() where T : class
+ {
+ var subtypes = new List();
+ foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies())
+ {
+ if (assembly.FullName.StartsWith("Mono.Cecil")) continue;
+ if (assembly.FullName.StartsWith("UnityScript")) continue;
+ if (assembly.FullName.StartsWith("Boo.Lan")) continue;
+ if (assembly.FullName.StartsWith("System")) continue;
+ if (assembly.FullName.StartsWith("I18N")) continue;
+ if (assembly.FullName.StartsWith("UnityEngine")) continue;
+ if (assembly.FullName.StartsWith("UnityEditor")) continue;
+ if (assembly.FullName.StartsWith("mscorlib")) continue;
+ try
+ {
+ foreach (Type type in assembly.GetTypes())
+ {
+ if (!type.IsClass) continue;
+ if (type.IsAbstract) continue;
+ if (!type.IsSubclassOf(typeof(T))) continue;
+ subtypes.Add(type);
+ }
+ }
+ catch (System.Reflection.ReflectionTypeLoadException)
+ {
+ }
+ }
+ return subtypes;
+ }
+
+ public static System.Type GetWrapperType(System.Type type)
+ {
+ if (type == null || !type.Namespace.StartsWith("PixelCrushers") || type.Namespace.Contains(".Wrappers.")) return type;
+ try
+ {
+ var wrapperName = type.Namespace + ".Wrappers." + type.Name;
+ var assemblies = RuntimeTypeUtility.GetAssemblies();
+ foreach (var assembly in assemblies)
+ {
+ try
+ {
+ var wrapperList = (from assemblyType in assembly.GetExportedTypes()
+ where string.Equals(assemblyType.FullName, wrapperName)
+ select assemblyType).ToArray();
+ if (wrapperList.Length > 0) return wrapperList[0];
+ }
+ catch (System.Exception)
+ {
+ // If an assembly complains, ignore it and move on.
+ }
+ }
+
+ }
+ catch (System.Exception)
+ {
+ }
+ return null;
+ }
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TypeUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TypeUtility.cs.meta
new file mode 100644
index 000000000..4f65101e6
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TypeUtility.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: a7f03e178ea6b714a929e35cf33c597a
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Misc/TypeUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System.meta
new file mode 100644
index 000000000..c798d5bef
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: b1c05611e74be014b853ee663e2fdaac
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/DiskSavedGameDataStorerEditor.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/DiskSavedGameDataStorerEditor.cs
new file mode 100644
index 000000000..9b16ea3db
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/DiskSavedGameDataStorerEditor.cs
@@ -0,0 +1,127 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+using UnityEditorInternal;
+using System.Collections.Generic;
+using System.IO;
+
+namespace PixelCrushers
+{
+
+ [CustomEditor(typeof(DiskSavedGameDataStorer), true)]
+ public class DiskSavedGameDataStorerEditor : Editor
+ {
+
+#if !(UNITY_WEBGL || UNITY_WSA)
+
+ private const int MaxSlots = 100;
+
+ private List m_files;
+ private ReorderableList m_list;
+
+ protected virtual void OnEnable()
+ {
+ var storer = target as DiskSavedGameDataStorer;
+
+ // Get active files:
+ m_files = new List();
+ int slotNum = 0;
+ var filename = storer.GetSavedGameInfoFilename();
+ if (!string.IsNullOrEmpty(filename) && File.Exists(filename))
+ {
+ try
+ {
+ using (StreamReader streamReader = new StreamReader(filename))
+ {
+ int safeguard = 0;
+ while (!streamReader.EndOfStream && safeguard < 999)
+ {
+ var sceneName = streamReader.ReadLine().Replace("", "\n");
+ m_files.Add(storer.GetSaveGameFilename(slotNum) + ": " + sceneName);
+ slotNum++;
+ safeguard++;
+ }
+ }
+ }
+ catch (System.Exception)
+ {
+ Debug.Log("Save System: DiskSavedGameDataStorer - Error reading file: " + filename);
+ }
+ }
+
+ // Setup editor list:
+ m_list = new ReorderableList(m_files, typeof(string), false, true, false, false);
+ m_list.drawHeaderCallback = OnDrawHeader;
+ m_list.drawElementCallback = OnDrawElement;
+ }
+
+ public override void OnInspectorGUI()
+ {
+ //base.OnInspectorGUI();
+
+ serializedObject.Update();
+ var locationProperty = serializedObject.FindProperty("storeSaveFilesIn");
+ EditorGUILayout.PropertyField(locationProperty);
+ if (locationProperty.enumValueIndex == (int)DiskSavedGameDataStorer.BasePath.Custom)
+ {
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("customPath"));
+ }
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("encrypt"));
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("encryptionPassword"));
+ EditorGUILayout.PropertyField(serializedObject.FindProperty("m_debug"));
+ serializedObject.ApplyModifiedProperties();
+
+ DrawSavedGameList();
+ }
+
+ protected virtual void DrawSavedGameList()
+ {
+ if (m_list != null) m_list.DoLayoutList();
+ if (GUILayout.Button(new GUIContent("Clear Saved Games", "Delete all saved game files.")))
+ {
+ if (EditorUtility.DisplayDialog("Clear Saved Games", "Delete all saved game files?", "OK", "Cancel"))
+ {
+ ClearSavedGames();
+ }
+ }
+ }
+
+ private void OnDrawHeader(Rect rect)
+ {
+ EditorGUI.LabelField(rect, "Saved Games");
+ }
+
+ private void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
+ {
+ if (!(0 <= index && index < m_files.Count)) return;
+ var key = m_files[index];
+ EditorGUI.BeginDisabledGroup(true);
+ EditorGUI.TextField(rect, key);
+ EditorGUI.EndDisabledGroup();
+ }
+
+ private void ClearSavedGames()
+ {
+ var storer = target as DiskSavedGameDataStorer;
+ for (int i = m_files.Count - 1; i >= 0; i--)
+ {
+ storer.DeleteSavedGameData(i);
+ }
+ m_files.Clear();
+ Repaint();
+ }
+
+#else
+
+ public override void OnInspectorGUI()
+ {
+ EditorGUILayout.HelpBox("DiskSavedGameDataStorer is not supported on this build platform.", MessageType.Warning);
+ base.OnInspectorGUI();
+ }
+
+#endif
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/DiskSavedGameDataStorerEditor.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/DiskSavedGameDataStorerEditor.cs.meta
new file mode 100644
index 000000000..ce163a540
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/DiskSavedGameDataStorerEditor.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 36511bad05c9fcc488213c99dbd8a3c5
+timeCreated: 1561750316
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/DiskSavedGameDataStorerEditor.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/PlayerPrefsSavedGameDataStorerEditor.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/PlayerPrefsSavedGameDataStorerEditor.cs
new file mode 100644
index 000000000..269ab5922
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/PlayerPrefsSavedGameDataStorerEditor.cs
@@ -0,0 +1,112 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+using UnityEditorInternal;
+using System.Collections.Generic;
+using System;
+
+namespace PixelCrushers
+{
+
+ [CustomEditor(typeof(PlayerPrefsSavedGameDataStorer), true)]
+ public class PlayerPrefsSavedGameDataStorerEditor : Editor
+ {
+
+ private const int MaxSlots = 100;
+ private string EmptySlotString = "-empty-";
+
+ private List m_keys;
+ private ReorderableList m_list;
+
+ protected virtual void OnEnable()
+ {
+ var storer = target as PlayerPrefsSavedGameDataStorer;
+
+ // Get active keys:
+ m_keys = new List();
+ int lastActiveSlot = -1;
+ for (int i = 1; i < MaxSlots; i++)
+ {
+ if (storer.HasDataInSlot(i)) lastActiveSlot = i;
+ }
+ for (int i = 1; i <= lastActiveSlot; i++)
+ {
+ m_keys.Add(storer.HasDataInSlot(i) ? storer.GetPlayerPrefsKey(i) : EmptySlotString);
+ }
+
+ // Setup editor list:
+ m_list = new ReorderableList(m_keys, typeof(string), false, true, false, true);
+ m_list.drawHeaderCallback = OnDrawHeader;
+ m_list.drawElementCallback = OnDrawElement;
+ m_list.onRemoveCallback = OnRemoveElement;
+ }
+
+ public override void OnInspectorGUI()
+ {
+ base.OnInspectorGUI();
+ DrawSavedGameList();
+ }
+
+ protected virtual void DrawSavedGameList()
+ {
+ if (m_list != null) m_list.DoLayoutList();
+ if (GUILayout.Button(new GUIContent("Clear Saved Games", "Delete all PlayerPrefs keys associated with saved games.")))
+ {
+ if (EditorUtility.DisplayDialog("Clear Saved Games", "Delete all PlayerPrefs keys associated with saved games?", "OK", "Cancel"))
+ {
+ ClearSavedGames();
+ }
+ }
+ }
+
+ private void OnDrawHeader(Rect rect)
+ {
+ EditorGUI.LabelField(rect, "Saved Games");
+ }
+
+ private void OnDrawElement(Rect rect, int index, bool isActive, bool isFocused)
+ {
+ if (!(0 <= index && index < m_keys.Count)) return;
+ var key = m_keys[index];
+ int buttonWidth = 48;
+ var keyRect = new Rect(rect.x, rect.y + 1, rect.width - buttonWidth, EditorGUIUtility.singleLineHeight);
+ var showRect = new Rect(rect.x + rect.width - buttonWidth, rect.y + 1, buttonWidth, EditorGUIUtility.singleLineHeight);
+ EditorGUI.BeginDisabledGroup(true);
+ EditorGUI.TextField(keyRect, key);
+ EditorGUI.EndDisabledGroup();
+ EditorGUI.BeginDisabledGroup(string.Equals(key, EmptySlotString));
+ if (GUI.Button(showRect, "Show"))
+ {
+ Debug.Log(key + ": " + PlayerPrefs.GetString(key));
+ }
+ EditorGUI.EndDisabledGroup();
+ }
+
+ private void OnRemoveElement(ReorderableList list)
+ {
+ if (!(0 <= list.index && list.index < m_keys.Count)) return;
+ var key = m_keys[list.index];
+ if (EditorUtility.DisplayDialog("Delete Saved Game", "Delete saved game " + key + "?", "OK", "Cancel"))
+ {
+ PlayerPrefs.DeleteKey(key);
+ m_keys[list.index] = EmptySlotString;
+ }
+ }
+
+
+ private void ClearSavedGames()
+ {
+ var baseKey = (target as PlayerPrefsSavedGameDataStorer).playerPrefsKeyBase;
+ for (int i = 0; i < 100; i++)
+ {
+ var key = baseKey + i;
+ if (PlayerPrefs.HasKey(key)) PlayerPrefs.DeleteKey(key);
+ }
+ m_keys.Clear();
+ Repaint();
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/PlayerPrefsSavedGameDataStorerEditor.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/PlayerPrefsSavedGameDataStorerEditor.cs.meta
new file mode 100644
index 000000000..ff29e95be
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/PlayerPrefsSavedGameDataStorerEditor.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 8cc97891a12eee040b4c022e25300313
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/PlayerPrefsSavedGameDataStorerEditor.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaveSystemEditorUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaveSystemEditorUtility.cs
new file mode 100644
index 000000000..1f2b0fcbd
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaveSystemEditorUtility.cs
@@ -0,0 +1,87 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+using UnityEditor.SceneManagement;
+using System.Collections.Generic;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Utility menu items for the Save System.
+ ///
+ public static class SaveSystemEditorUtility
+ {
+
+ private static HashSet s_keys = new HashSet();
+
+ [MenuItem("Tools/Pixel Crushers/Common/Save System/Assign Unique Keys...", false, 0)]
+ public static void AssignUniqueKeysDialog()
+ {
+ if (EditorUtility.DisplayDialog("Assign Unique Saver Keys", "Assign unique keys to all Saver components in the current scene whose Key fields are currently blank?", "OK", "Cancel"))
+ {
+ AssignUniqueKeysInScene();
+ }
+ }
+
+ public static void AssignUniqueKeysInScene()
+ {
+ s_keys.Clear();
+ for (int i = 0; i < EditorSceneManager.sceneCount; i++)
+ {
+ var s = EditorSceneManager.GetSceneAt(i);
+ if (s.isLoaded)
+ {
+ var allGameObjects = s.GetRootGameObjects();
+ for (int j = 0; j < allGameObjects.Length; j++)
+ {
+ AssignUniqueKeysInTransformHierarchy(allGameObjects[j].transform);
+ }
+ }
+ }
+ s_keys.Clear();
+ }
+
+ private static void AssignUniqueKeysInTransformHierarchy(Transform t)
+ {
+ if (t == null) return;
+ var savers = t.GetComponents();
+ for (int i = 0; i < savers.Length; i++)
+ {
+ var saver = savers[i];
+ if (string.IsNullOrEmpty(saver._internalKeyValue))
+ {
+ // Key is not assigned yet. Assign one:
+ AssignNewKey(saver, " [new]");
+ }
+ else if (s_keys.Contains(saver._internalKeyValue))
+ {
+ // Key is already used. Assign a new one:
+ AssignNewKey(saver, " [key '" + saver._internalKeyValue + "' already exists; make unique]");
+ }
+ s_keys.Add(saver._internalKeyValue);
+ }
+ foreach (Transform child in t)
+ {
+ AssignUniqueKeysInTransformHierarchy(child);
+ }
+ }
+
+ private static void AssignNewKey(Saver saver, string reason)
+ {
+ if (saver == null) return;
+ var key = CleanName(saver.name) + "_" + Mathf.Abs(saver.GetInstanceID());
+ Debug.Log(saver.name + "." + saver.GetType().Name + ".Key = " + key + reason, saver);
+ Undo.RecordObject(saver, "Key");
+ saver._internalKeyValue = key;
+ saver.appendSaverTypeToKey = false;
+ }
+
+ private static string CleanName(string name)
+ {
+ if (string.IsNullOrEmpty(name)) return name;
+ return System.Text.RegularExpressions.Regex.Replace(name, @"[\(\s\)\.\,\'""]", string.Empty);
+ }
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaveSystemEditorUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaveSystemEditorUtility.cs.meta
new file mode 100644
index 000000000..674f9f3b3
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaveSystemEditorUtility.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 0ce6b1d9bfb10c54da02aa4554949b41
+timeCreated: 1538006943
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaveSystemEditorUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaverEditor.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaverEditor.cs
new file mode 100644
index 000000000..b65681253
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaverEditor.cs
@@ -0,0 +1,51 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ [CustomEditor(typeof(Saver), true)]
+ public class SaverEditor : Editor
+ {
+
+ protected Saver[] m_saversOnGameObject;
+
+ protected virtual void OnEnable()
+ {
+ m_saversOnGameObject = (target is Saver) ? (target as Saver).GetComponents() : null;
+ }
+
+ public override void OnInspectorGUI()
+ {
+ CheckSaverKeys();
+ base.OnInspectorGUI();
+ }
+
+ protected virtual void CheckSaverKeys()
+ {
+ if (m_saversOnGameObject == null) return;
+ var numSavers = 0;
+ var anyBlankKeys = false;
+ for (int i = 0; i < m_saversOnGameObject.Length; i++)
+ {
+ var saver = m_saversOnGameObject[i];
+ if (saver != null)
+ {
+ numSavers++;
+ if (!saver.appendSaverTypeToKey && string.IsNullOrEmpty(saver._internalKeyValue))
+ {
+ anyBlankKeys = true;
+ }
+ }
+ }
+ if (numSavers > 1 && anyBlankKeys)
+ {
+ EditorGUILayout.HelpBox("GameObject has more than one Saver component. Make sure each Saver has a unique Key.", MessageType.Warning);
+ }
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaverEditor.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaverEditor.cs.meta
new file mode 100644
index 000000000..778c191bd
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaverEditor.cs.meta
@@ -0,0 +1,20 @@
+fileFormatVersion: 2
+guid: 1898d5456617565459b0cbdacb0ae67c
+timeCreated: 1629213895
+licenseType: Store
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Save System/SaverEditor.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text.meta
new file mode 100644
index 000000000..f8fbd1556
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: eee3a84c9cf0f6c4a9828ed772df921d
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/CSVUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/CSVUtility.cs
new file mode 100644
index 000000000..a165215b7
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/CSVUtility.cs
@@ -0,0 +1,170 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System.Collections.Generic;
+using System.Text;
+using System.Text.RegularExpressions;
+using System.IO;
+
+namespace PixelCrushers
+{
+
+ public static class CSVUtility
+ {
+
+ ///
+ /// Writes a table of strings to a CSV file.
+ ///
+ /// 2D list of strings.
+ /// Filename to write as.
+ /// Encoding type to use.
+ public static void WriteCSVFile(List> content, string filename, EncodingType encodingType)
+ {
+ using (var file = new StreamWriter(filename, false, EncodingTypeTools.GetEncoding(encodingType)))
+ {
+ for (int i = 0; i < content.Count; i++)
+ {
+ var row = content[i];
+ StringBuilder sb = new StringBuilder();
+ var first = true;
+ for (int j = 0; j < row.Count; j++)
+ {
+ if (!first) sb.Append(",");
+ first = false;
+ var cell = row[j];
+ sb.Append(CleanField(cell));
+ }
+ file.WriteLine(sb);
+ }
+ }
+ }
+
+ ///
+ /// Reads a CSV file into a table of strings.
+ ///
+ /// Name of CSV file to read.
+ /// Encoding type CSV file was created in.
+ /// Contents of CSV file as a 2D list of strings.
+ public static List> ReadCSVFile(string filename, EncodingType encodingType)
+ {
+ // Read the source file and combine multiline rows:
+ var sourceLines = new List();
+ using (var file = new StreamReader(filename, EncodingTypeTools.GetEncoding(encodingType)))
+ {
+ string line;
+ while ((line = file.ReadLine()) != null)
+ {
+ sourceLines.Add(line.TrimEnd());
+ }
+ }
+ CombineMultilineSourceLines(sourceLines);
+ if (sourceLines.Count < 1) return null;
+
+ var content = new List>();
+ while (sourceLines.Count > 0)
+ {
+ var values = GetValues(sourceLines[0]);
+ sourceLines.RemoveAt(0);
+ if (values == null || values.Length == 0) continue;
+ var row = new List();
+ content.Add(row);
+ for (int i = 0; i < values.Length; i++)
+ {
+ row.Add(values[i]);
+ }
+ }
+ return content;
+ }
+
+ private static string CleanField(string s)
+ {
+ if (string.IsNullOrEmpty(s)) return string.Empty;
+ string s2 = s.Contains("\n") ? s.Replace("\n", "\\n") : s;
+ if (s2.Contains("\r")) s2 = s2.Replace("\r", "\\r");
+ if (s2.Contains(",") || s2.Contains("\""))
+ {
+ return "\"" + s2.Replace("\"", "\"\"") + "\"";
+ }
+ else
+ {
+ return s2;
+ }
+ }
+
+ ///
+ /// Returns the individual comma-separated values in a line.
+ ///
+ /// The values.
+ /// Line.
+ private static string[] GetValues(string line)
+ {
+ Regex csvSplit = new Regex("(?:^|,)(\"(?:[^\"]+|\"\")*\"|[^,]*)");
+ List values = new List();
+ foreach (Match match in csvSplit.Matches(line))
+ {
+ values.Add(UnwrapValue(match.Value.TrimStart(',')));
+ }
+ return values.ToArray();
+ }
+
+ ///
+ /// Returns a "fixed" version of a comma-separated value where escaped newlines
+ /// have been converted back into real newlines, and optional surrounding quotes
+ /// have been removed.
+ ///
+ /// The value.
+ /// Value.
+ private static string UnwrapValue(string value)
+ {
+ string s = value.Replace("\\n", "\n").Replace("\\r", "\r");
+ if (s.StartsWith("\"") && s.EndsWith("\""))
+ {
+ s = s.Substring(1, s.Length - 2).Replace("\"\"", "\"");
+ }
+ return s;
+ }
+
+ ///
+ /// Combines lines that are actually a multiline CSV row. This also helps prevent the
+ /// CSV-splitting regex from hanging due to catastrophic backtracking on unterminated quotes.
+ ///
+ private static void CombineMultilineSourceLines(List sourceLines)
+ {
+ int lineNum = 0;
+ int safeguard = 0;
+ int MaxIterations = 999999;
+ while ((lineNum < sourceLines.Count) && (safeguard < MaxIterations))
+ {
+ safeguard++;
+ string line = sourceLines[lineNum];
+ if (line == null)
+ {
+ sourceLines.RemoveAt(lineNum);
+ }
+ else
+ {
+ bool terminated = true;
+ char previousChar = (char)0;
+ for (int i = 0; i < line.Length; i++)
+ {
+ char currentChar = line[i];
+ bool isQuote = (currentChar == '"') && (previousChar != '\\');
+ if (isQuote) terminated = !terminated;
+ previousChar = currentChar;
+ }
+ if (terminated || (lineNum + 1) >= sourceLines.Count)
+ {
+ if (!terminated) sourceLines[lineNum] = line + '"';
+ lineNum++;
+ }
+ else
+ {
+ sourceLines[lineNum] = line + "\\n" + sourceLines[lineNum + 1];
+ sourceLines.RemoveAt(lineNum + 1);
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/CSVUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/CSVUtility.cs.meta
new file mode 100644
index 000000000..17e55c0c4
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/CSVUtility.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 5bc76f91176340e46a2e240833f25b2a
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/CSVUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldDrawer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldDrawer.cs
new file mode 100644
index 000000000..1b73fdea3
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldDrawer.cs
@@ -0,0 +1,181 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ [CustomPropertyDrawer(typeof(StringField), true)]
+ public class StringFieldDrawer : PropertyDrawer
+ {
+
+ public const int NumExpandedLines = 5;
+
+ private static GUIStyle s_wrappedTextAreaGUIStyle = null;
+
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
+ {
+ return GetHeight(property);
+ }
+
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ Draw(position, property, label, false);
+ }
+
+ public static float GetHeight(SerializedProperty property)
+ {
+ var textProperty = property.FindPropertyRelative("m_text");
+ var stringAssetProperty = property.FindPropertyRelative("m_stringAsset");
+ var textTableProperty = property.FindPropertyRelative("m_textTable");
+ var isContentAssigned = (textProperty != null && !string.IsNullOrEmpty(textProperty.stringValue)) ||
+ (stringAssetProperty != null && stringAssetProperty.objectReferenceValue != null) ||
+ (textTableProperty != null && textTableProperty.objectReferenceValue != null);
+ return (isContentAssigned ? 1 : 3) * EditorGUIUtility.singleLineHeight;
+ }
+
+ public static void Draw(Rect position, SerializedProperty property, GUIContent label, bool expandHeight)
+ {
+ try
+ {
+ EditorGUI.BeginProperty(position, label, property);
+
+ position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
+
+ // Get child properties:
+ var textProperty = property.FindPropertyRelative("m_text");
+ var stringAssetProperty = property.FindPropertyRelative("m_stringAsset");
+ var textTableProperty = property.FindPropertyRelative("m_textTable");
+ var textTableFieldIDProperty = property.FindPropertyRelative("m_textTableFieldID");
+ if (textProperty == null || stringAssetProperty == null || textTableProperty == null || textTableFieldIDProperty == null)
+ {
+ Debug.LogError("Sorry! There was an internal editor error with a String Field. Please contact Pixel Crushers for support.");
+ return;
+ }
+ var isTextAssigned = (!string.IsNullOrEmpty(textProperty.stringValue));
+ var isStringAssetAssigned = (stringAssetProperty.objectReferenceValue != null);
+ var isTextTableAssigned = (textTableProperty.objectReferenceValue != null);
+ var isContentAssigned = isTextAssigned || isStringAssetAssigned || isTextTableAssigned;
+
+ float yOffset = 0;
+
+ // Text row:
+ if (isTextAssigned || !isContentAssigned)
+ {
+ if (expandHeight)
+ {
+ if (s_wrappedTextAreaGUIStyle == null)
+ {
+ s_wrappedTextAreaGUIStyle = new GUIStyle(EditorStyles.textArea);
+ s_wrappedTextAreaGUIStyle.wordWrap = true;
+ }
+ textProperty.stringValue = EditorGUI.TextArea(new Rect(position.x, position.y, position.width, NumExpandedLines * EditorGUIUtility.singleLineHeight), textProperty.stringValue, s_wrappedTextAreaGUIStyle);
+ }
+ else
+ {
+ EditorGUI.PropertyField(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), textProperty, GUIContent.none);
+ }
+ yOffset += (expandHeight ? NumExpandedLines : 1) * EditorGUIUtility.singleLineHeight;
+ }
+
+ if (isStringAssetAssigned || !isContentAssigned)
+ {
+ float buttonWidth = 40;
+ EditorGUI.PropertyField(new Rect(position.x, position.y + yOffset, position.width - buttonWidth, EditorGUIUtility.singleLineHeight), stringAssetProperty, GUIContent.none);
+ EditorGUI.BeginDisabledGroup(isStringAssetAssigned);
+ bool createNewAsset = GUI.Button(new Rect(position.x + position.width - buttonWidth, position.y + yOffset, buttonWidth, EditorGUIUtility.singleLineHeight),
+ new GUIContent("New", "Create and assign a new String Asset."), EditorStyles.miniButton);
+ EditorGUI.EndDisabledGroup();
+ //--Pre-DLL support: if (createNewAsset) stringAssetProperty.objectReferenceValue = AssetUtility.CreateAsset("String Asset", false);
+ if (createNewAsset)
+ {
+ var filename = EditorUtility.SaveFilePanelInProject("Create String Asset", ObjectNames.NicifyVariableName(property.name), "asset", "Save new string asset as:");
+ var type = System.Type.GetType("PixelCrushers.Wrappers.StringAsset, Assembly-CSharp-firstpass");
+ if (type == null) type = System.Type.GetType("PixelCrushers.Wrappers.StringAsset, Assembly-CSharp");
+ if (type == null)
+ {
+ Debug.LogError("Internal error: Unable to find wrapper type PixelCrushers.Wrappers.StringAsset. Please contact the developer.");
+ }
+ else
+ {
+ stringAssetProperty.objectReferenceValue = AssetUtility.CreateAssetWithFilename(type, filename, false);
+ }
+ }
+ yOffset += EditorGUIUtility.singleLineHeight;
+ }
+
+ if (isTextTableAssigned || !isContentAssigned)
+ {
+ float fieldWidth = isTextTableAssigned ? position.width / 2 : position.width;
+ EditorGUI.PropertyField(new Rect(position.x, position.y + yOffset, fieldWidth, EditorGUIUtility.singleLineHeight), textTableProperty, GUIContent.none);
+ if (isTextTableAssigned)
+ {
+ textTableFieldIDProperty.intValue = DrawTextTableFieldDropdown(new Rect(position.x + fieldWidth, position.y + yOffset, fieldWidth, EditorGUIUtility.singleLineHeight),
+ textTableProperty.objectReferenceValue as TextTable, textTableFieldIDProperty.intValue);
+ }
+ }
+ }
+ finally
+ {
+ EditorGUI.EndProperty();
+ }
+ }
+
+ private static int DrawTextTableFieldDropdown(Rect rect, TextTable textTable, int fieldID)
+ {
+ if (textTable == null || textTable.fields == null) return fieldID;
+ var fieldNames = textTable.GetFieldNames();
+ var fieldIDs = textTable.GetFieldIDs();
+ int index = -1;
+ for (int i = 0; i < fieldIDs.Length; i++)
+ {
+ if (fieldIDs[i] == fieldID)
+ {
+ index = i;
+ break;
+ }
+ }
+ var newIndex = EditorGUI.Popup(rect, index, fieldNames);
+ return (newIndex != -1 && newIndex != index) ? fieldIDs[newIndex] : fieldID;
+ }
+
+ public static string GetStringFieldValue(SerializedProperty stringFieldProperty)
+ {
+ if (stringFieldProperty == null) return string.Empty;
+ var textProperty = stringFieldProperty.FindPropertyRelative("m_text");
+ if (textProperty != null && !string.IsNullOrEmpty(textProperty.stringValue))
+ {
+ return textProperty.stringValue;
+ }
+ var stringAssetProperty = stringFieldProperty.FindPropertyRelative("m_stringAsset");
+ if (stringAssetProperty != null && stringAssetProperty.objectReferenceValue is StringAsset)
+ {
+ return (stringAssetProperty.objectReferenceValue as StringAsset).text;
+ }
+ var textTableProperty = stringFieldProperty.FindPropertyRelative("m_textTable");
+ if (textTableProperty != null && textTableProperty.objectReferenceValue is TextTable)
+ {
+ var textTable = textTableProperty.objectReferenceValue as TextTable;
+ var textTableFieldIDProperty = stringFieldProperty.FindPropertyRelative("m_textTableFieldID");
+ return textTable.GetFieldTextForLanguage(textTableFieldIDProperty.intValue,
+ (UILocalizationManager.instance != null) ? UILocalizationManager.instance.currentLanguage : string.Empty);
+ }
+ return string.Empty;
+ }
+
+ public static void SetStringFieldValue(SerializedProperty stringFieldProperty, string value)
+ {
+ if (stringFieldProperty == null) return;
+ var textProperty = stringFieldProperty.FindPropertyRelative("m_text");
+ if (textProperty != null) textProperty.stringValue = value;
+ var stringAssetProperty = stringFieldProperty.FindPropertyRelative("m_stringAsset");
+ if (stringAssetProperty != null) stringAssetProperty.objectReferenceValue = null;
+ var textTableProperty = stringFieldProperty.FindPropertyRelative("m_textTable");
+ if (textTableProperty != null) textTableProperty.objectReferenceValue = null;
+ }
+
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldDrawer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldDrawer.cs.meta
new file mode 100644
index 000000000..f6be189b2
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldDrawer.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 2d021fbf144c817489417aa063f92ca2
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldDrawer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldTextAreaAttributeDrawer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldTextAreaAttributeDrawer.cs
new file mode 100644
index 000000000..0fc2540cf
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldTextAreaAttributeDrawer.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ [CustomPropertyDrawer(typeof(StringFieldTextAreaAttribute))]
+ public class StringFieldTextAreaAttributeDrawer : PropertyDrawer
+ {
+
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
+ {
+ var stringFieldSizeAttribute = attribute as StringFieldTextAreaAttribute;
+ if (stringFieldSizeAttribute == null) return base.GetPropertyHeight(property, label);
+ var expandHeight = stringFieldSizeAttribute.expandHeight;
+
+ var textProperty = property.FindPropertyRelative("m_text");
+ var stringAssetProperty = property.FindPropertyRelative("m_stringAsset");
+ var textTableProperty = property.FindPropertyRelative("m_textTable");
+ if (textProperty == null || stringAssetProperty == null || textTableProperty == null) return base.GetPropertyHeight(property, label);
+ var isTextAssigned = (textProperty != null && !string.IsNullOrEmpty(textProperty.stringValue));
+ var isStringAssetAssigned = (stringAssetProperty != null && stringAssetProperty.objectReferenceValue != null);
+ var isTextTableAssigned = (textTableProperty != null && textTableProperty.objectReferenceValue != null);
+ var isContentAssigned = isTextAssigned || isStringAssetAssigned || isTextTableAssigned;
+
+ var textLines = expandHeight ? StringFieldDrawer.NumExpandedLines : 1;
+ if (isContentAssigned && !isTextAssigned) textLines = 0;
+ var nonTextLines = isTextAssigned ? 0 : (isContentAssigned ? 1 : 2);
+ return (textLines + nonTextLines) * EditorGUIUtility.singleLineHeight;
+ }
+
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ var stringFieldSizeAttribute = attribute as StringFieldTextAreaAttribute;
+ var expandHeight = (stringFieldSizeAttribute != null) ? stringFieldSizeAttribute.expandHeight : false;
+ StringFieldDrawer.Draw(position, property, label, expandHeight);
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldTextAreaAttributeDrawer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldTextAreaAttributeDrawer.cs.meta
new file mode 100644
index 000000000..e09d73a3b
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldTextAreaAttributeDrawer.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: f4cc1e51e3d5a7441ad01e8d84f85082
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/StringFieldTextAreaAttributeDrawer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditor.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditor.cs
new file mode 100644
index 000000000..f1edeeddb
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditor.cs
@@ -0,0 +1,74 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ [CustomEditor(typeof(TextTable), true)]
+ public class TextTableEditor : Editor
+ {
+
+ private TextTable m_textTable;
+ private int m_languageCount;
+ private int m_fieldCount;
+ private string m_languageCountText;
+ private string m_fieldCountText;
+
+ private void OnEnable()
+ {
+ EditorApplication.projectWindowItemOnGUI += OnProjectWindowItemOnGUI;
+ m_textTable = target as TextTable;
+ UpdateLabelText();
+ }
+
+ private void OnDisable()
+ {
+ EditorApplication.projectWindowItemOnGUI -= OnProjectWindowItemOnGUI;
+ }
+
+ private void OnProjectWindowItemOnGUI(string guid, Rect selectionRect)
+ {
+ // Check for double-clicks to open editor window:
+ var doubleClicked = Event.current.type == EventType.MouseDown && Event.current.clickCount == 2 && selectionRect.Contains(Event.current.mousePosition);
+ if (doubleClicked)
+ {
+ TextTableEditorWindow.ShowWindow();
+ }
+ }
+
+ public override void OnInspectorGUI()
+ {
+ if (TextTableEditorWindow.isOpen)
+ {
+ EditorGUILayout.HelpBox("A Text Table is a database of text fields and translations into any number of languages. Edit it in the Text Table window.", MessageType.None);
+ }
+ else
+ {
+ EditorGUILayout.HelpBox("A Text Table is a database of text fields and translations into any number of languages. To edit it, click on the Open Text Table Editor button below.", MessageType.None);
+ if (GUILayout.Button("Open Text Table Editor"))
+ {
+ TextTableEditorWindow.ShowWindow();
+ }
+ }
+ if (m_textTable.languages.Count != m_languageCount || m_textTable.fields.Count != m_fieldCount)
+ {
+ UpdateLabelText();
+ }
+ EditorGUILayout.LabelField(m_languageCountText);
+ EditorGUILayout.LabelField(m_fieldCountText);
+
+ // Debug: DrawDefaultInspector();
+ }
+
+ private void UpdateLabelText()
+ {
+ m_languageCount = m_textTable.languages.Count;
+ m_fieldCount = m_textTable.fields.Count;
+ m_languageCountText = "Languages: " + m_languageCount;
+ m_fieldCountText = "Fields: " + m_fieldCount;
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditor.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditor.cs.meta
new file mode 100644
index 000000000..f33b42aa9
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditor.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: d180f5ccb807d7e4489c2a31b334388b
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditor.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditorWindow.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditorWindow.cs
new file mode 100644
index 000000000..f55bc6dd9
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditorWindow.cs
@@ -0,0 +1,1068 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using System.Collections.Generic;
+using System.IO;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEditorInternal;
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Custom editor window for TextTable.
+ ///
+ public class TextTableEditorWindow : EditorWindow
+ {
+
+ #region Menu Item
+
+ [MenuItem("Tools/Pixel Crushers/Common/Text Table Editor")]
+ public static void ShowWindow()
+ {
+ GetWindow();
+ }
+
+ #endregion
+
+ #region Variables
+
+ public static bool isOpen { get { return instance != null; } }
+
+ public static TextTableEditorWindow instance { get { return s_instance; } }
+
+ private static TextTableEditorWindow s_instance = null;
+
+ private const string WindowTitle = "Text Table";
+
+ private static GUIContent[] ToolbarLabels = new GUIContent[]
+ { new GUIContent("Languages"), new GUIContent("Fields") };
+
+ [SerializeField]
+ private int m_textTableInstanceID;
+
+ [SerializeField]
+ private Vector2 m_languageListScrollPosition;
+
+ [SerializeField]
+ private Vector2 m_fieldListScrollPosition;
+
+ [SerializeField]
+ private int m_toolbarSelection = 0;
+
+ [SerializeField]
+ private int m_selectedLanguageIndex = 0;
+
+ [SerializeField]
+ private int m_selectedLanguageID = 0;
+
+ [SerializeField]
+ private string m_csvFilename = string.Empty;
+
+ [SerializeField]
+ private bool m_isSearchPanelOpen = false;
+
+ [SerializeField]
+ private string m_searchString = string.Empty;
+
+ [SerializeField]
+ private string m_replaceString = string.Empty;
+
+ [SerializeField]
+ private bool m_matchCase = false;
+
+ private TextTable m_textTable;
+
+ private bool m_needRefreshLists = true;
+ private ReorderableList m_languageList = null;
+ private ReorderableList m_fieldList = null;
+ private SerializedObject m_serializedObject = null;
+ private GUIStyle textAreaStyle = null;
+ private bool isTextAreaStyleInitialized = false;
+
+ private const string EncodingTypeEditorPrefsKey = "PixelCrushers.EncodingType";
+ private const string ToolbarSelectionPrefsKey = "PixelCrushers.TextTableEditor.Toolbar";
+ private const string SearchBarPrefsKey = "PixelCrushers.TextTableEditor.SearchBar";
+ private const double TimeBetweenUpdates = 10;
+
+ private bool m_needToUpdateSO;
+ private bool m_needToApplyBeforeUpdateSO;
+ private bool m_isPickingOtherTextTable;
+ private System.DateTime m_lastApply;
+
+ [System.Serializable]
+ public class SearchBarSettings
+ {
+ public bool open = false;
+ public string searchString = string.Empty;
+ public string replaceString = string.Empty;
+ public bool matchCase = false;
+ }
+
+ #endregion
+
+ #region Editor Entrypoints
+
+ private void OnEnable()
+ {
+ m_needToUpdateSO = true;
+ m_needToApplyBeforeUpdateSO = false;
+ m_lastApply = System.DateTime.Now;
+ s_instance = this;
+ titleContent.text = "Text Table";
+ m_needRefreshLists = true;
+ Undo.undoRedoPerformed += Repaint;
+ if (m_textTableInstanceID != 0) Selection.activeObject = EditorUtility.InstanceIDToObject(m_textTableInstanceID);
+ m_toolbarSelection = EditorPrefs.GetInt(ToolbarSelectionPrefsKey, 0);
+ if (EditorPrefs.HasKey(SearchBarPrefsKey))
+ {
+ var searchBarSettings = JsonUtility.FromJson(EditorPrefs.GetString(SearchBarPrefsKey));
+ if (searchBarSettings != null)
+ {
+ m_isSearchPanelOpen = searchBarSettings.open;
+ m_searchString = searchBarSettings.searchString;
+ m_replaceString = searchBarSettings.replaceString;
+ m_matchCase = searchBarSettings.matchCase;
+ }
+ }
+ OnSelectionChange();
+ }
+
+ private void OnDisable()
+ {
+ if (m_serializedObject != null) m_serializedObject.ApplyModifiedProperties();
+ s_instance = null;
+ Undo.undoRedoPerformed -= Repaint;
+ EditorPrefs.SetInt(ToolbarSelectionPrefsKey, m_toolbarSelection);
+ var searchBarSettings = new SearchBarSettings();
+ searchBarSettings.open = m_isSearchPanelOpen;
+ searchBarSettings.searchString = m_searchString;
+ searchBarSettings.replaceString = m_replaceString;
+ searchBarSettings.matchCase = m_matchCase;
+ EditorPrefs.SetString(SearchBarPrefsKey, JsonUtility.ToJson(searchBarSettings));
+ }
+
+ private void OnSelectionChange()
+ {
+ if (Selection.activeObject is TextTable)
+ {
+ SelectTextTable(Selection.activeObject as TextTable);
+ Repaint();
+ }
+ else if (m_textTable == null && m_textTableInstanceID != 0)
+ {
+ SelectTextTable(EditorUtility.InstanceIDToObject(m_textTableInstanceID) as TextTable);
+ Repaint();
+ }
+ }
+
+ private void SelectTextTable(TextTable newTable)
+ {
+ m_textTable = newTable;
+ ResetLanguagesTab();
+ ResetFieldsTab();
+ m_needRefreshLists = true;
+ m_needToUpdateSO = true;
+ m_needToApplyBeforeUpdateSO = false;
+ m_serializedObject = (newTable != null) ? new SerializedObject(newTable) : null;
+ if (m_textTable != null && m_textTable.languages.Count == 0) m_textTable.AddLanguage("Default");
+ m_textTableInstanceID = (newTable != null) ? newTable.GetInstanceID() : 0;
+ }
+
+ private void OnGUI()
+ {
+ if (Event.current.commandName == "ObjectSelectorClosed" || Event.current.commandName == "ObjectSelectorUpdated")
+ {
+ if (m_isPickingOtherTextTable)
+ {
+ m_isPickingOtherTextTable = false;
+ AskConfirmImportOtherTextTable(EditorGUIUtility.GetObjectPickerObject() as TextTable);
+ }
+ return;
+ }
+
+ DrawWindowContents();
+ if (m_needRefreshLists) Repaint();
+ }
+
+ private void DrawWindowContents()
+ {
+ DrawTextTableField();
+ if (m_textTable == null || m_serializedObject == null) return;
+ var now = System.DateTime.Now;
+ var elapsed = (now - m_lastApply).TotalSeconds;
+ if (m_needToUpdateSO)
+ {
+ if (m_needToApplyBeforeUpdateSO)
+ {
+ m_serializedObject.ApplyModifiedProperties();
+ m_needToApplyBeforeUpdateSO = false;
+ }
+ m_needToUpdateSO = false;
+ m_serializedObject.Update();
+ }
+ var newToolbarSelection = GUILayout.Toolbar(m_toolbarSelection, ToolbarLabels);
+ if (newToolbarSelection != m_toolbarSelection)
+ {
+ m_toolbarSelection = newToolbarSelection;
+ if (newToolbarSelection == 1) m_languageDropdownList = null;
+ }
+ if (m_toolbarSelection == 0)
+ {
+ DrawLanguagesTab();
+ }
+ else
+ {
+ DrawFieldsTab();
+ }
+ if (GUI.changed) m_needToApplyBeforeUpdateSO = true;
+ if (elapsed > TimeBetweenUpdates)
+ {
+ m_lastApply = now;
+ m_serializedObject.ApplyModifiedProperties();
+ m_needToApplyBeforeUpdateSO = false;
+ }
+ }
+
+ private void DrawTextTableField()
+ {
+ EditorGUILayout.BeginHorizontal();
+ var newTable = EditorGUILayout.ObjectField(m_textTable, typeof(TextTable), false) as TextTable;
+ if (newTable != m_textTable) SelectTextTable(newTable);
+ DrawGearMenu();
+ EditorGUILayout.EndHorizontal();
+ }
+
+ #endregion
+
+ #region Language List
+
+ private void ResetLanguagesTab()
+ {
+ m_languageList = null;
+ m_languageListScrollPosition = Vector2.zero;
+ }
+
+ private void DrawLanguagesTab()
+ {
+ if (m_languageList == null)
+ {
+ m_languageList = new ReorderableList(m_serializedObject, m_serializedObject.FindProperty("m_languageKeys"), true, true, true, true);
+ m_languageList.drawHeaderCallback = OnDrawLanguageListHeader;
+ m_languageList.drawElementCallback = OnDrawLanguageListElement;
+ m_languageList.onAddCallback = OnAddLanguageListElement;
+ m_languageList.onCanRemoveCallback = OnCanRemoveLanguageListElement;
+ m_languageList.onRemoveCallback = OnRemoveLanguageListElement;
+ m_languageList.onSelectCallback = OnSelectLanguageListElement;
+ m_languageList.onReorderCallback = OnReorderLanguageListElement;
+ }
+ m_languageListScrollPosition = GUILayout.BeginScrollView(m_languageListScrollPosition, false, false);
+ try
+ {
+ m_languageList.DoLayoutList();
+ }
+ finally
+ {
+ GUILayout.EndScrollView();
+ }
+ }
+
+ private void OnDrawLanguageListHeader(Rect rect)
+ {
+ EditorGUI.LabelField(rect, "Languages");
+ }
+
+ private void OnDrawLanguageListElement(Rect rect, int index, bool isActive, bool isFocused)
+ {
+ var languageKeysProperty = m_serializedObject.FindProperty("m_languageKeys");
+ var languageKeyProperty = languageKeysProperty.GetArrayElementAtIndex(index);
+ var languageValuesProperty = m_serializedObject.FindProperty("m_languageValues");
+ var languageValueProperty = languageValuesProperty.GetArrayElementAtIndex(index);
+ EditorGUI.BeginDisabledGroup(languageValueProperty.intValue == 0);
+ EditorGUI.PropertyField(new Rect(rect.x, rect.y + 1, rect.width, EditorGUIUtility.singleLineHeight), languageKeyProperty, GUIContent.none, false);
+ EditorGUI.EndDisabledGroup();
+ }
+
+ private void OnAddLanguageListElement(ReorderableList list)
+ {
+ m_serializedObject.ApplyModifiedProperties();
+ m_textTable.AddLanguage("Language " + m_textTable.nextLanguageID);
+ m_serializedObject.Update();
+ ResetFieldsTab();
+ }
+
+ private bool OnCanRemoveLanguageListElement(ReorderableList list)
+ {
+ var languageValuesProperty = m_serializedObject.FindProperty("m_languageValues");
+ var languageValueProperty = languageValuesProperty.GetArrayElementAtIndex(list.index);
+ return languageValueProperty.intValue > 0;
+ }
+
+ private void OnRemoveLanguageListElement(ReorderableList list)
+ {
+ var languageKeysProperty = m_serializedObject.FindProperty("m_languageKeys");
+ var languageKeyProperty = languageKeysProperty.GetArrayElementAtIndex(list.index);
+ var languageName = languageKeyProperty.stringValue;
+ var languageValuesProperty = m_serializedObject.FindProperty("m_languageValues");
+ var languageValueProperty = languageValuesProperty.GetArrayElementAtIndex(list.index);
+ var languageID = languageValueProperty.intValue;
+ if (!EditorUtility.DisplayDialog("Delete " + languageName, "Are you sure you want to delete the language '" + languageName +
+ "' and all field values associated with it?", "OK", "Cancel")) return;
+ m_serializedObject.ApplyModifiedProperties();
+ m_textTable.RemoveLanguage(languageID);
+ m_serializedObject.Update();
+ ResetFieldsTab();
+ }
+
+ private int m_selectedLanguageListIndex = -1;
+
+ private void OnSelectLanguageListElement(ReorderableList list)
+ {
+ m_selectedLanguageListIndex = list.index;
+ }
+
+ private void OnReorderLanguageListElement(ReorderableList list)
+ {
+ // Also reorder values:
+ var languageValuesProperty = m_serializedObject.FindProperty("m_languageValues");
+ var value = languageValuesProperty.GetArrayElementAtIndex(m_selectedLanguageListIndex).intValue;
+ languageValuesProperty.DeleteArrayElementAtIndex(m_selectedLanguageListIndex);
+ languageValuesProperty.InsertArrayElementAtIndex(list.index);
+ languageValuesProperty.GetArrayElementAtIndex(list.index).intValue = value;
+ ResetFieldsTab();
+ }
+
+ #endregion
+
+ #region Field List
+
+ private void ResetFieldsTab()
+ {
+ m_fieldList = null;
+ m_fieldListScrollPosition = Vector2.zero;
+ m_selectedLanguageIndex = 0;
+ m_selectedLanguageID = 0;
+ }
+
+ private void DrawFieldsTab()
+ {
+ DrawGrid();
+ DrawEntryBox();
+ if (m_isSearchPanelOpen)
+ {
+ DrawSearchPanel();
+ }
+ //else
+ //{
+ // DrawEntryBox();
+ //}
+ }
+
+ private const float MinColumnWidth = 100;
+
+ private string[] m_languageDropdownList = null;
+
+ private class CachedFieldInfo
+ {
+ public SerializedProperty fieldNameProperty;
+ public SerializedProperty fieldValueProperty;
+ public string nameControl;
+ public string valueControl;
+ public CachedFieldInfo(int index, SerializedProperty fieldNameProperty, SerializedProperty fieldValueProperty)
+ {
+ this.fieldNameProperty = fieldNameProperty;
+ this.fieldValueProperty = fieldValueProperty;
+ this.nameControl = "Field" + index;
+ this.valueControl = "Value" + index;
+ }
+ }
+ private List m_fieldCache = new List();
+
+ private void DrawGrid()
+ {
+ if (m_textTable == null) return;
+ try
+ {
+ var entryBoxHeight = IsAnyFieldSelected() ? (6 * EditorGUIUtility.singleLineHeight) : 0;
+ if (m_isSearchPanelOpen) entryBoxHeight += (4 * EditorGUIUtility.singleLineHeight);
+ GUILayout.BeginArea(new Rect(0, 2 * (EditorGUIUtility.singleLineHeight + 4), position.width,
+ position.height - (2 * (EditorGUIUtility.singleLineHeight + 4) + 4) - entryBoxHeight));
+ m_fieldListScrollPosition = GUILayout.BeginScrollView(m_fieldListScrollPosition, false, false);
+
+ if (m_needRefreshLists || m_fieldList == null || m_languageDropdownList == null)
+ {
+ m_needRefreshLists = false;
+ m_fieldList = new ReorderableList(m_serializedObject, m_serializedObject.FindProperty("m_fieldValues"), true, true, true, true);
+ m_fieldList.drawHeaderCallback = OnDrawFieldListHeader;
+ m_fieldList.drawElementCallback = OnDrawFieldListElement;
+ m_fieldList.onAddCallback = OnAddFieldListElement;
+ m_fieldList.onRemoveCallback = OnRemoveFieldListElement;
+ m_fieldList.onSelectCallback = OnSelectFieldListElement;
+ m_fieldList.onReorderCallback = OnReorderFieldListElement;
+
+ var languages = new List();
+ var languageKeysProperty = m_serializedObject.FindProperty("m_languageKeys");
+ for (int i = 0; i < languageKeysProperty.arraySize; i++)
+ {
+ languages.Add(languageKeysProperty.GetArrayElementAtIndex(i).stringValue);
+ }
+ m_languageDropdownList = languages.ToArray();
+
+ RebuildFieldCache();
+ }
+
+ m_fieldList.DoLayoutList();
+
+ CheckMouseEvents();
+ }
+ finally
+ {
+ GUILayout.EndScrollView();
+ GUILayout.EndArea();
+ }
+ }
+
+ private void RebuildFieldCache()
+ {
+ m_fieldCache.Clear();
+
+ var fieldValuesProperty = m_serializedObject.FindProperty("m_fieldValues");
+ for (int index = 0; index < fieldValuesProperty.arraySize; index++)
+ {
+ var fieldValueProperty = fieldValuesProperty.GetArrayElementAtIndex(index);
+ var fieldNameProperty = fieldValueProperty.FindPropertyRelative("m_fieldName");
+ var keysProperty = fieldValueProperty.FindPropertyRelative("m_keys");
+ var valuesProperty = fieldValueProperty.FindPropertyRelative("m_values");
+
+ var valueIndex = -1;
+ for (int i = 0; i < keysProperty.arraySize; i++)
+ {
+ if (keysProperty.GetArrayElementAtIndex(i).intValue == m_selectedLanguageID)
+ {
+ valueIndex = i;
+ break;
+ }
+ }
+ if (valueIndex == -1)
+ {
+ valueIndex = keysProperty.arraySize;
+ keysProperty.arraySize++;
+ keysProperty.GetArrayElementAtIndex(valueIndex).intValue = m_selectedLanguageID;
+ valuesProperty.arraySize++;
+ valuesProperty.GetArrayElementAtIndex(valueIndex).stringValue = string.Empty;
+ }
+ var valueProperty = valuesProperty.GetArrayElementAtIndex(valueIndex);
+
+ m_fieldCache.Add(new CachedFieldInfo(index, fieldNameProperty, valueProperty));
+ }
+ }
+
+ private void OnDrawFieldListHeader(Rect rect)
+ {
+ var headerWidth = rect.width - 14;
+ var columnWidth = headerWidth / 2;
+ EditorGUI.LabelField(new Rect(rect.x + 14, rect.y, columnWidth, rect.height), "Field");
+ var popupRect = new Rect(rect.x + rect.width - columnWidth, rect.y, columnWidth, rect.height);
+ var newIndex = EditorGUI.Popup(popupRect, m_selectedLanguageIndex, m_languageDropdownList);
+ if (newIndex != m_selectedLanguageIndex)
+ {
+ m_selectedLanguageIndex = newIndex;
+ var languageValuesProperty = m_serializedObject.FindProperty("m_languageValues");
+ var languageValueProperty = languageValuesProperty.GetArrayElementAtIndex(newIndex);
+ m_selectedLanguageID = languageValueProperty.intValue;
+ RebuildFieldCache();
+ }
+ }
+
+ private void OnDrawFieldListElement(Rect rect, int index, bool isActive, bool isFocused)
+ {
+ if (rect.width <= 0) return;
+ // Since lists can be very long, only draw elements within the visible window:
+ if (!(0 <= index && index < m_fieldCache.Count)) return;
+ var isElementVisible = rect.Overlaps(new Rect(0, m_fieldListScrollPosition.y, position.width, position.height));
+ if (!isElementVisible) return;
+
+ var columnWidth = (rect.width / 2) - 1;
+
+ var info = m_fieldCache[index];
+
+ GUI.SetNextControlName(info.nameControl);
+ EditorGUI.PropertyField(new Rect(rect.x, rect.y + 1, columnWidth, EditorGUIUtility.singleLineHeight), info.fieldNameProperty, GUIContent.none, false);
+
+ if (info.fieldValueProperty != null)
+ {
+ GUI.SetNextControlName(info.valueControl);
+ EditorGUI.PropertyField(new Rect(rect.x + rect.width - columnWidth, rect.y + 1, columnWidth, EditorGUIUtility.singleLineHeight), info.fieldValueProperty, GUIContent.none, false);
+ var focusedControl = GUI.GetNameOfFocusedControl();
+ if (string.Equals(info.nameControl, focusedControl) || string.Equals(info.valueControl, focusedControl))
+ {
+ m_selectedFieldListElement = index;
+ m_fieldList.index = index;
+ }
+ }
+ }
+
+ private void OnAddFieldListElement(ReorderableList list)
+ {
+ m_serializedObject.ApplyModifiedProperties();
+ m_textTable.AddField("Field " + m_textTable.nextFieldID);
+ m_serializedObject.Update();
+ RebuildFieldCache();
+ Repaint();
+ }
+
+ private void OnRemoveFieldListElement(ReorderableList list)
+ {
+ var fieldKeysProperty = m_serializedObject.FindProperty("m_fieldKeys");
+ var fieldKeyProperty = fieldKeysProperty.GetArrayElementAtIndex(list.index);
+ var fieldID = fieldKeyProperty.intValue;
+ var fieldValuesProperty = m_serializedObject.FindProperty("m_fieldValues");
+ var fieldValueProperty = fieldValuesProperty.GetArrayElementAtIndex(list.index);
+ var fieldNameProperty = fieldValueProperty.FindPropertyRelative("m_fieldName");
+ var fieldName = fieldNameProperty.stringValue;
+ if (!EditorUtility.DisplayDialog("Delete Field", "Are you sure you want to delete the field '" + fieldName +
+ "' and all values associated with it?", "OK", "Cancel")) return;
+ m_serializedObject.ApplyModifiedProperties();
+ m_textTable.RemoveField(fieldID);
+ m_serializedObject.Update();
+ RebuildFieldCache();
+ }
+
+ private int m_selectedFieldListElement;
+
+ private void OnSelectFieldListElement(ReorderableList list)
+ {
+ m_selectedFieldListElement = list.index;
+ }
+
+ private void OnReorderFieldListElement(ReorderableList list)
+ {
+ // Also reorder keys:
+ var fieldKeysProperty = m_serializedObject.FindProperty("m_fieldKeys");
+ var value = fieldKeysProperty.GetArrayElementAtIndex(m_selectedFieldListElement).intValue;
+ fieldKeysProperty.DeleteArrayElementAtIndex(m_selectedFieldListElement);
+ fieldKeysProperty.InsertArrayElementAtIndex(list.index);
+ fieldKeysProperty.GetArrayElementAtIndex(list.index).intValue = value;
+ }
+
+ private void CheckMouseEvents()
+ {
+ if (Event.current.type == EventType.MouseDown && Event.current.button == 1) // Right-click
+ {
+ var scrolledClickPosition = Event.current.mousePosition.y - 16;
+ var elementHeight = (EditorGUIUtility.singleLineHeight + 5);
+ var index = Mathf.FloorToInt(scrolledClickPosition / elementHeight);
+ if (0 <= index && index < m_fieldList.count)
+ {
+ var menu = new GenericMenu();
+ menu.AddItem(new GUIContent("Insert Field"), false, InsertFieldListElement, index);
+ menu.AddItem(new GUIContent("Delete Field"), false, DeleteFieldListElement, index);
+ menu.ShowAsContext();
+ }
+ }
+ }
+
+ private void InsertFieldListElement(object data)
+ {
+ int index = (int)data;
+ m_serializedObject.ApplyModifiedProperties();
+ Undo.RecordObject(m_textTable, "Insert Field");
+ m_textTable.InsertField(index, "Field " + m_textTable.nextFieldID);
+ EditorUtility.SetDirty(m_textTable);
+ m_serializedObject.Update();
+ RebuildFieldCache();
+ Repaint();
+ }
+
+ private void DeleteFieldListElement(object data)
+ {
+ int index = (int)data;
+ var info = m_fieldCache[index];
+ if (EditorUtility.DisplayDialog("Delete Field", "Delete '" + info.fieldNameProperty.stringValue + "'?", "OK", "Cancel"))
+ {
+ m_serializedObject.ApplyModifiedProperties();
+ Undo.RecordObject(m_textTable, "Delete Field");
+ m_textTable.RemoveField(info.fieldNameProperty.stringValue);
+ EditorUtility.SetDirty(m_textTable);
+ m_serializedObject.Update();
+ RebuildFieldCache();
+ Repaint();
+ }
+ }
+
+ private bool IsAnyFieldSelected()
+ {
+ return m_fieldList != null && 0 <= m_fieldList.index && m_fieldList.index < m_fieldList.serializedProperty.arraySize;
+ }
+
+ private void DrawEntryBox()
+ {
+ if (m_needRefreshLists || !IsAnyFieldSelected()) return;
+ var rect = new Rect(2, position.height - 6 * EditorGUIUtility.singleLineHeight, position.width - 4, 6 * EditorGUIUtility.singleLineHeight);
+ if (m_isSearchPanelOpen)
+ {
+ var searchPanelHeight = (4 * EditorGUIUtility.singleLineHeight);
+ rect = new Rect(rect.x, rect.y - searchPanelHeight, rect.width, rect.height);
+ }
+ var fieldValuesProperty = m_serializedObject.FindProperty("m_fieldValues");
+ var fieldValueProperty = fieldValuesProperty.GetArrayElementAtIndex(m_fieldList.index);
+ var keysProperty = fieldValueProperty.FindPropertyRelative("m_keys");
+ var valuesProperty = fieldValueProperty.FindPropertyRelative("m_values");
+ var valueIndex = -1;
+ for (int i = 0; i < keysProperty.arraySize; i++)
+ {
+ if (keysProperty.GetArrayElementAtIndex(i).intValue == m_selectedLanguageID)
+ {
+ valueIndex = i;
+ break;
+ }
+ }
+ if (valueIndex == -1)
+ {
+ valueIndex = keysProperty.arraySize;
+ keysProperty.arraySize++;
+ keysProperty.GetArrayElementAtIndex(valueIndex).intValue = m_selectedLanguageID;
+ valuesProperty.arraySize++;
+ valuesProperty.GetArrayElementAtIndex(valueIndex).stringValue = string.Empty;
+ }
+ if (textAreaStyle == null || !isTextAreaStyleInitialized)
+ {
+ isTextAreaStyleInitialized = true;
+ textAreaStyle = new GUIStyle(EditorStyles.textField);
+ textAreaStyle.wordWrap = true;
+ }
+ var valueProperty = valuesProperty.GetArrayElementAtIndex(valueIndex);
+ valueProperty.stringValue = EditorGUI.TextArea(rect, valueProperty.stringValue, textAreaStyle);
+ }
+
+ #endregion
+
+ #region Gear Menu
+
+ private void DrawGearMenu()
+ {
+ if (MoreEditorGuiUtility.DoLayoutGearMenu())
+ {
+ var menu = new GenericMenu();
+ if (m_textTable == null)
+ {
+ menu.AddDisabledItem(new GUIContent("Search..."));
+ menu.AddDisabledItem(new GUIContent("Sort..."));
+ menu.AddDisabledItem(new GUIContent("Delete All..."));
+ menu.AddDisabledItem(new GUIContent("Export/CSV..."));
+ menu.AddDisabledItem(new GUIContent("Import/CSV..."));
+ menu.AddDisabledItem(new GUIContent("Import/Other Text Table..."));
+ menu.AddDisabledItem(new GUIContent("Import/Fields From Localize UI..."));
+ }
+ else
+ {
+ menu.AddItem(new GUIContent("Search..."), false, OpenSearchPanel);
+ menu.AddItem(new GUIContent("Sort..."), false, Sort);
+ menu.AddItem(new GUIContent("Delete All..."), false, DeleteAll);
+ menu.AddItem(new GUIContent("Export/CSV..."), false, ExportCSVDialogs);
+ menu.AddItem(new GUIContent("Import/CSV..."), false, ImportCSVDialogs);
+ menu.AddItem(new GUIContent("Import/Other Text Table..."), false, ImportOtherTextTable);
+ menu.AddItem(new GUIContent("Import/Fields From Localize UI..."), false, ImportFieldsFromLocalizeUI);
+ }
+ menu.AddItem(new GUIContent("Encoding/UTF8"), GetEncodingType() == EncodingType.UTF8, SetEncodingType, EncodingType.UTF8);
+ menu.AddItem(new GUIContent("Encoding/Unicode"), GetEncodingType() == EncodingType.Unicode, SetEncodingType, EncodingType.Unicode);
+ menu.AddItem(new GUIContent("Encoding/ISO-8859-1"), GetEncodingType() == EncodingType.ISO_8859_1, SetEncodingType, EncodingType.ISO_8859_1);
+ menu.ShowAsContext();
+ }
+ }
+
+ private void DeleteAll()
+ {
+ var answer = EditorUtility.DisplayDialogComplex("Delete All", "Delete all fields or delete everything (languages and fields)?", "Fields", "Everything", "Cancel");
+ if (answer == 2) return; // Cancel.
+
+ m_serializedObject.ApplyModifiedProperties();
+ Undo.RecordObject(m_textTable, "Delete");
+ switch (answer)
+ {
+ case 0:
+ m_textTable.RemoveAllFields();
+ Debug.Log("Deleted all fields in " + m_textTable.name, m_textTable);
+ break;
+ case 1:
+ m_textTable.RemoveAll();
+ Debug.Log("Deleted everything in " + m_textTable.name, m_textTable);
+ break;
+ }
+ EditorUtility.SetDirty(m_textTable);
+ m_serializedObject.Update();
+ RebuildFieldCache();
+ Repaint();
+ }
+
+ #endregion
+
+ #region Sort
+
+ private void Sort()
+ {
+ var onLanguagesTab = (m_toolbarSelection == 0);
+ var section = onLanguagesTab ? "Languages" : "Fields";
+ if (!EditorUtility.DisplayDialog("Sort " + section, "Sort " + section.ToLower() + " alphabetically?", "OK", "Cancel")) return;
+ m_serializedObject.ApplyModifiedProperties();
+ if (onLanguagesTab)
+ {
+ m_textTable.SortLanguages();
+ }
+ else
+ {
+ m_textTable.SortFields();
+ }
+ m_serializedObject.Update();
+ RebuildFieldCache();
+ Repaint();
+ }
+
+ #endregion
+
+ #region Search
+
+ private void OpenSearchPanel()
+ {
+ m_isSearchPanelOpen = !m_isSearchPanelOpen;
+ }
+
+ private void DrawSearchPanel()
+ {
+ var rect = new Rect(2, position.height - 5 * EditorGUIUtility.singleLineHeight, position.width - 4, 5 * EditorGUIUtility.singleLineHeight);
+ var searchRect = new Rect(rect.x, rect.y + rect.height - 4 * EditorGUIUtility.singleLineHeight, rect.width, EditorGUIUtility.singleLineHeight);
+ var replaceRect = new Rect(rect.x, rect.y + rect.height - 3 * EditorGUIUtility.singleLineHeight, rect.width, EditorGUIUtility.singleLineHeight);
+ var buttonRect = new Rect(rect.x, rect.y + rect.height - 2 * EditorGUIUtility.singleLineHeight + 4, rect.width, EditorGUIUtility.singleLineHeight);
+ m_searchString = EditorGUI.TextField(searchRect, new GUIContent("Find", "Regular expressions allowed."), m_searchString);
+ m_replaceString = EditorGUI.TextField(replaceRect, "Replace With", m_replaceString);
+ var buttonWidth = 78f;
+ var toggleWidth = 90f;
+ m_matchCase = EditorGUI.ToggleLeft(new Rect(buttonRect.x + buttonRect.width - (4 * (2 + buttonWidth)) - toggleWidth, buttonRect.y, toggleWidth, buttonRect.height), "Match Case", m_matchCase);
+ if (GUI.Button(new Rect(buttonRect.x + buttonRect.width - (4 * (2 + buttonWidth)), buttonRect.y, buttonWidth, buttonRect.height), "Find Next"))
+ {
+ FindNext();
+ }
+ EditorGUI.BeginDisabledGroup(!IsAnyFieldSelected());
+ if (GUI.Button(new Rect(buttonRect.x + buttonRect.width - (3 * (2 + buttonWidth)), buttonRect.y, buttonWidth, buttonRect.height), "Replace"))
+ {
+ ReplaceCurrent();
+ }
+ EditorGUI.EndDisabledGroup();
+ if (GUI.Button(new Rect(buttonRect.x + buttonRect.width - (2 * (2 + buttonWidth)), buttonRect.y, buttonWidth, buttonRect.height), "Replace All"))
+ {
+ ReplaceAll();
+ }
+ if (GUI.Button(new Rect(buttonRect.x + buttonRect.width - (1 * (2 + buttonWidth)), buttonRect.y, buttonWidth, buttonRect.height), "Cancel"))
+ {
+ m_isSearchPanelOpen = false;
+ }
+ }
+
+ private void FindNext()
+ {
+ var found = false;
+ int currentIndex = (m_fieldList.index + 1) % m_fieldList.count;
+ var regexOptions = m_matchCase ? RegexOptions.None : RegexOptions.IgnoreCase;
+ int safeguard = 0;
+ while (!found && safeguard < 9999)
+ {
+ safeguard++;
+ var info = m_fieldCache[currentIndex];
+ if (Regex.IsMatch(info.fieldNameProperty.stringValue, m_searchString, regexOptions) ||
+ Regex.IsMatch(info.fieldValueProperty.stringValue, m_searchString, regexOptions))
+ {
+ found = true;
+ break;
+ }
+ else if (currentIndex == m_fieldList.index)
+ {
+ break; // Wrapped around, so stop.
+ }
+ else
+ {
+ currentIndex = (currentIndex + 1) % m_fieldList.count;
+ }
+ }
+ if (found)
+ {
+ m_fieldList.index = currentIndex;
+ // Scroll to position:
+ var minScrollY = m_fieldList.index * (EditorGUIUtility.singleLineHeight + 5);
+ m_fieldListScrollPosition = new Vector2(m_fieldListScrollPosition.x, minScrollY);
+ }
+ else
+ {
+ EditorUtility.DisplayDialog("Search Text Table", "String '" + m_searchString + "' not found in text table.", "OK");
+ }
+ }
+
+ private void ReplaceCurrent()
+ {
+ if (!IsAnyFieldSelected()) return;
+ var regexOptions = m_matchCase ? RegexOptions.None : RegexOptions.IgnoreCase;
+ m_fieldCache[m_fieldList.index].fieldNameProperty.stringValue = Regex.Replace(m_fieldCache[m_fieldList.index].fieldNameProperty.stringValue, m_searchString, m_replaceString, regexOptions);
+ m_fieldCache[m_fieldList.index].fieldValueProperty.stringValue = Regex.Replace(m_fieldCache[m_fieldList.index].fieldValueProperty.stringValue, m_searchString, m_replaceString, regexOptions);
+ }
+
+ private void ReplaceAll()
+ {
+ if (!EditorUtility.DisplayDialog("Replace All", "Replace:\n'" + m_searchString + "'\nwith:\n'" + m_replaceString + "'\nin entire table for current language?", "OK", "Cancel")) return;
+ var regexOptions = m_matchCase ? RegexOptions.None : RegexOptions.IgnoreCase;
+ EditorUtility.DisplayProgressBar("Replace All", "Processing text table.", 0);
+ try
+ {
+ for (int i = 0; i < m_fieldCache.Count; i++)
+ {
+ m_fieldCache[i].fieldNameProperty.stringValue = Regex.Replace(m_fieldCache[i].fieldNameProperty.stringValue, m_searchString, m_replaceString, regexOptions);
+ m_fieldCache[i].fieldValueProperty.stringValue = Regex.Replace(m_fieldCache[i].fieldValueProperty.stringValue, m_searchString, m_replaceString, regexOptions);
+
+ }
+ }
+ finally
+ {
+ EditorUtility.ClearProgressBar();
+ }
+ }
+
+ #endregion
+
+ #region CSV
+
+ private void ExportCSVDialogs()
+ {
+ string newFilename = EditorUtility.SaveFilePanel("Export to CSV", GetPath(m_csvFilename), m_csvFilename, "csv");
+ if (string.IsNullOrEmpty(newFilename)) return;
+ m_csvFilename = newFilename;
+ if (Application.platform == RuntimePlatform.WindowsEditor) m_csvFilename = m_csvFilename.Replace("/", "\\");
+ switch (EditorUtility.DisplayDialogComplex("Export CSV", "Export languages as columns in one file or as separate files?", "One", "Cancel", "Separate"))
+ {
+ case 0:
+ ExportCSV(m_csvFilename, false);
+ break;
+ case 2:
+ ExportCSV(m_csvFilename, true);
+ break;
+ default:
+ return;
+ }
+ EditorUtility.DisplayDialog("Export Complete", "The text table was exported to CSV (comma-separated values) format. ", "OK");
+ }
+
+ private void ImportCSVDialogs()
+ {
+ if (!EditorUtility.DisplayDialog("Import CSV?", "Importing from CSV will overwrite any existing languages or fields with the same name in the current contents. Are you sure?", "Import", "Cancel")) return;
+ string newFilename = EditorUtility.OpenFilePanel("Import from CSV", GetPath(m_csvFilename), "csv");
+ if (string.IsNullOrEmpty(newFilename)) return;
+ if (!File.Exists(newFilename))
+ {
+ EditorUtility.DisplayDialog("Import CSV", "Can't find the file " + newFilename + ".", "OK");
+ return;
+ }
+ m_csvFilename = newFilename;
+ if (Application.platform == RuntimePlatform.WindowsEditor) m_csvFilename = m_csvFilename.Replace("/", "\\");
+ ImportCSV(m_csvFilename);
+ OnSelectionChange();
+ Repaint();
+ }
+
+ private string GetPath(string filename)
+ {
+ if (string.IsNullOrEmpty(filename)) return string.Empty;
+ try
+ {
+ return Path.GetDirectoryName(filename);
+ }
+ catch (System.ArgumentException)
+ {
+ return string.Empty;
+ }
+ }
+
+ private EncodingType GetEncodingType()
+ {
+ return (EncodingType)EditorPrefs.GetInt(EncodingTypeEditorPrefsKey, (int)EncodingType.UTF8);
+ }
+
+ private void SetEncodingType(object data)
+ {
+ EditorPrefs.SetInt(EncodingTypeEditorPrefsKey, (int)((EncodingType)data));
+ }
+
+ private void ExportCSV(string csvFilename, bool separateFiles)
+ {
+ if (separateFiles)
+ {
+ foreach (var languageKvp in m_textTable.languages)
+ {
+ var language = languageKvp.Key;
+ var languageID = languageKvp.Value;
+ var content = new List>();
+ var row = new List();
+ row.Add("Language");
+ row.Add(language);
+ content.Add(row);
+ foreach (var fieldKvp in m_textTable.fields)
+ {
+ var field = fieldKvp.Value;
+ row = new List();
+ row.Add(field.fieldName);
+ row.Add(field.GetTextForLanguage(languageID));
+ content.Add(row);
+ }
+ var languageFilename = csvFilename.Substring(0, csvFilename.Length - 4) + "_" + language + ".csv";
+ CSVUtility.WriteCSVFile(content, languageFilename, GetEncodingType());
+ }
+ }
+ else
+ {
+ // All in one file:
+ var content = new List>();
+ var languageIDs = new List();
+
+ // Heading rows:
+ var row = new List();
+ content.Add(row);
+ row.Add("Field");
+ foreach (var kvp in m_textTable.languages)
+ {
+ var language = kvp.Key;
+ var languageID = kvp.Value;
+ languageIDs.Add(languageID);
+ row.Add(language);
+ }
+
+ // One row per field:
+ foreach (var kvp in m_textTable.fields)
+ {
+ var field = kvp.Value;
+ row = new List();
+ content.Add(row);
+ row.Add(field.fieldName);
+ for (int i = 0; i < languageIDs.Count; i++)
+ {
+ var languageID = languageIDs[i];
+ var value = field.GetTextForLanguage(languageID);
+ row.Add(value);
+ }
+ }
+ CSVUtility.WriteCSVFile(content, csvFilename, GetEncodingType());
+ }
+ }
+
+ private void ImportCSV(string csvFilename)
+ {
+ var content = CSVUtility.ReadCSVFile(csvFilename, GetEncodingType());
+ if (content == null || content.Count < 1 || content[0].Count < 2) return;
+ var fieldList = new List();
+ var firstCell = content[0][0];
+ if (string.Equals(firstCell, "Language"))
+ {
+ // Single language file:
+ var language = content[0][1];
+ if (!string.IsNullOrEmpty(language))
+ {
+ if (!m_textTable.HasLanguage(language)) m_textTable.AddLanguage(language);
+ for (int y = 1; y < content.Count; y++)
+ {
+ var field = content[y][0];
+ if (string.IsNullOrEmpty(field)) continue;
+ fieldList.Add(field);
+ if (!m_textTable.HasField(field)) m_textTable.AddField(field);
+ for (int x = 1; x < content[y].Count; x++)
+ {
+ m_textTable.SetFieldTextForLanguage(field, language, content[y][x]);
+ }
+ }
+ }
+ }
+ else
+ {
+ // All-in-one file:
+ for (int x = 1; x < content[0].Count; x++)
+ {
+ var language = content[0][x];
+ if (string.IsNullOrEmpty(language)) continue;
+ if (!m_textTable.HasLanguage(language)) m_textTable.AddLanguage(language);
+ for (int y = 1; y < content.Count; y++)
+ {
+ var field = content[y][0];
+ if (string.IsNullOrEmpty(field)) continue;
+ if (x == 1) fieldList.Add(field);
+ if (!m_textTable.HasField(field)) m_textTable.AddField(field);
+ if ((0 <= y && y < content.Count) && (0 <= x && x < content[y].Count))
+ {
+ m_textTable.SetFieldTextForLanguage(field, language, content[y][x]);
+ }
+ }
+ }
+ }
+ m_textTable.ReorderFields(fieldList);
+ m_textTable.OnBeforeSerialize();
+ m_serializedObject.Update();
+ RebuildFieldCache();
+ EditorUtility.SetDirty(m_textTable);
+ }
+
+ #endregion
+
+ #region Import Fields From Localize UI
+
+ private void ImportFieldsFromLocalizeUI()
+ {
+ if (!EditorUtility.DisplayDialog("Import From Localize UI",
+ "This will examine all Localize UI components in the current scene and create corresponding fields in the text table. Proceed?",
+ "OK", "Cancel")) return;
+ Undo.RecordObject(m_textTable, "Import");
+ foreach (var localizeUI in GameObjectUtility.FindObjectsOfTypeAlsoInactive())
+ {
+ localizeUI.ValidateFieldNames();
+ AddField(localizeUI.fieldName);
+ if (localizeUI.fieldNames != null) localizeUI.fieldNames.ForEach(fieldName => AddField(fieldName));
+#if TMP_PRESENT
+ if (localizeUI.tmpFieldNames != null) localizeUI.tmpFieldNames.ForEach(fieldName => AddField(fieldName));
+#endif
+ }
+ m_textTable.OnBeforeSerialize();
+ m_serializedObject.Update();
+ RebuildFieldCache();
+ EditorUtility.SetDirty(m_textTable);
+ m_needRefreshLists = true;
+ Repaint();
+ }
+
+ private void AddField(string fieldName)
+ {
+ if (string.IsNullOrEmpty(fieldName)) return;
+ if (m_textTable.HasField(fieldName)) return;
+ m_textTable.AddField(fieldName);
+ }
+
+#endregion
+
+ #region Import Other Text Table
+
+ private void ImportOtherTextTable()
+ {
+ m_isPickingOtherTextTable = true;
+ EditorGUIUtility.ShowObjectPicker(null, false, "t:TextTable", 0);
+ }
+
+ private void AskConfirmImportOtherTextTable(TextTable other)
+ {
+ if (other == null || m_textTable == null) return;
+ if (!EditorUtility.DisplayDialog("Import Text Table?", "Import the contents of " + other.name + " into this text table? This operation may take some time depending on the sizes of the text tables.", "Import", "Cancel")) return;
+ Undo.RecordObject(m_textTable, "Import");
+ m_textTable.ImportOtherTextTable(other);
+ m_textTable.OnBeforeSerialize();
+ m_serializedObject.Update();
+ RebuildFieldCache();
+ EditorUtility.SetDirty(m_textTable);
+ m_needRefreshLists = true;
+ Repaint();
+ }
+
+ #endregion
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditorWindow.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditorWindow.cs.meta
new file mode 100644
index 000000000..426db3d87
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditorWindow.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 9b26dc2337a4ecd4285aafb655ca55de
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableEditorWindow.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableMassExportImportWindow.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableMassExportImportWindow.cs
new file mode 100644
index 000000000..315f61631
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableMassExportImportWindow.cs
@@ -0,0 +1,408 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text.RegularExpressions;
+using UnityEditor;
+using UnityEditorInternal;
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Custom editor window for mass exporting text tables to CSV.
+ ///
+ public class TextTableMassExportImportWindow : EditorWindow
+ {
+
+ #region Menu Item
+
+ [MenuItem("Tools/Pixel Crushers/Common/Text Table Mass Export")]
+ public static void ShowWindow()
+ {
+ GetWindow();
+ }
+
+ #endregion
+
+ private const string PrefsKey = "PixelCrushers.TextTableMassExport";
+
+ [Serializable]
+ public class Prefs
+ {
+ public List textTableGuids = new List();
+ public string csvFilename;
+ public EncodingType encodingType = EncodingType.UTF8;
+ }
+
+ private Prefs prefs;
+ private List textTables = new List();
+ private ReorderableList textTablesList;
+ private Vector2 scrollPosition = Vector2.zero;
+ private string folderPath;
+
+ private void OnEnable()
+ {
+ if (EditorPrefs.HasKey(PrefsKey))
+ {
+ prefs = JsonUtility.FromJson(EditorPrefs.GetString(PrefsKey));
+ }
+ if (prefs == null) prefs = new Prefs();
+
+ textTables.Clear();
+ foreach (var textTableGuid in prefs.textTableGuids)
+ {
+ if (!string.IsNullOrEmpty(textTableGuid))
+ {
+ var textTable = AssetDatabase.LoadAssetAtPath(AssetDatabase.GUIDToAssetPath(textTableGuid));
+ if (textTable != null)
+ {
+ textTables.Add(textTable);
+ }
+ }
+ }
+ }
+
+ private void OnDisable()
+ {
+ prefs.textTableGuids.Clear();
+ foreach (var textTable in textTables)
+ {
+ prefs.textTableGuids.Add((textTable != null) ? AssetDatabase.AssetPathToGUID(AssetDatabase.GetAssetPath(textTable)) : string.Empty);
+ }
+ EditorPrefs.SetString(PrefsKey, JsonUtility.ToJson(prefs));
+ }
+
+ private void OnGUI()
+ {
+ try
+ {
+ scrollPosition = EditorGUILayout.BeginScrollView(scrollPosition);
+ if (textTablesList == null)
+ {
+ textTablesList = new ReorderableList(textTables, typeof(TextTable), true, true, true, true);
+ textTablesList.drawHeaderCallback += OnDrawTextTablesListHeader;
+ textTablesList.drawElementCallback += OnDrawTextTablesListElement;
+ textTablesList.onAddCallback += OnAddTextTable;
+ }
+ textTablesList.DoLayoutList();
+ if (GUILayout.Button("Add Folder..."))
+ {
+ AddFolder();
+ }
+ prefs.encodingType = (EncodingType)EditorGUILayout.EnumPopup("Encoding Type", prefs.encodingType);
+ EditorGUI.BeginDisabledGroup(!HasAnyTextTables());
+ if (GUILayout.Button("Export to CSV..."))
+ {
+ ExportToCSV();
+ }
+ if (GUILayout.Button("Import from CSV File..."))
+ {
+ ImportFromCSVFile();
+ }
+ if (GUILayout.Button("Import from CSV Folder..."))
+ {
+ ImportFromCSVFolder();
+ }
+ EditorGUI.EndDisabledGroup();
+ }
+ finally
+ {
+ EditorGUILayout.EndScrollView();
+ }
+ }
+
+ private bool HasAnyTextTables()
+ {
+ return textTables.Find(x => x != null) != null;
+ }
+
+ private void OnDrawTextTablesListHeader(Rect rect)
+ {
+ EditorGUI.LabelField(rect, "Text Tables");
+ }
+
+ private void OnDrawTextTablesListElement(Rect rect, int index, bool isActive, bool isFocused)
+ {
+ if (!(0 <= index && index < textTables.Count)) return;
+ textTables[index] = EditorGUI.ObjectField(rect, textTables[index], typeof(TextTable), true) as TextTable;
+ }
+
+ private void OnAddTextTable(ReorderableList list)
+ {
+ textTables.Add(null);
+ }
+
+ private void AddFolder()
+ {
+ var newPath = EditorUtility.OpenFolderPanel("Add Text Tables", folderPath, folderPath);
+ if (!string.IsNullOrEmpty(newPath))
+ {
+ folderPath = newPath;
+ var filenames = Directory.GetFiles(folderPath, "*.asset", SearchOption.AllDirectories);
+ foreach (var filename in filenames)
+ {
+ string assetPath = filename.Replace("\\", "/");
+ assetPath = "Assets/" + assetPath.Substring(Application.dataPath.Length);
+ var textTable = AssetDatabase.LoadAssetAtPath(assetPath);
+ if (textTable != null && !textTables.Contains(textTable))
+ {
+ textTables.Add(textTable);
+ }
+ }
+ if (Application.platform == RuntimePlatform.WindowsEditor) folderPath = folderPath.Replace("/", "\\");
+ }
+ }
+
+ private void ExportToCSV()
+ {
+ string newFilename = EditorUtility.SaveFilePanel("Export to CSV", GetPath(prefs.csvFilename), prefs.csvFilename, "csv");
+ if (string.IsNullOrEmpty(newFilename)) return;
+ prefs.csvFilename = newFilename;
+ if (Application.platform == RuntimePlatform.WindowsEditor) prefs.csvFilename = prefs.csvFilename.Replace("/", "\\");
+ switch (EditorUtility.DisplayDialogComplex("Export CSV", "Export languages as columns in one file or as separate files?", "One", "Cancel", "Separate"))
+ {
+ case 0:
+ ExportCSV(prefs.csvFilename, false);
+ break;
+ case 2:
+ ExportCSV(prefs.csvFilename, true);
+ break;
+ default:
+ return;
+ }
+ EditorUtility.DisplayDialog("Export Complete", "The text table was exported to CSV (comma-separated values) format. ", "OK");
+ }
+
+ private void ImportFromCSVFile()
+ {
+ if (!EditorUtility.DisplayDialog("Import CSV?", "Importing from CSV will overwrite any existing languages or fields with the same name in the current contents. Are you sure?", "Import", "Cancel")) return;
+ string newFilename = EditorUtility.OpenFilePanel("Import from CSV", GetPath(prefs.csvFilename), "csv");
+ if (string.IsNullOrEmpty(newFilename)) return;
+ if (!File.Exists(newFilename))
+ {
+ EditorUtility.DisplayDialog("Import CSV", "Can't find the file " + newFilename + ".", "OK");
+ return;
+ }
+ try
+ {
+ EditorUtility.DisplayProgressBar("Importing CSV File", newFilename, 0);
+ prefs.csvFilename = newFilename;
+ if (Application.platform == RuntimePlatform.WindowsEditor) prefs.csvFilename = prefs.csvFilename.Replace("/", "\\");
+ ImportCSVFile(prefs.csvFilename);
+ if (TextTableEditorWindow.instance != null)
+ {
+ var selection = Selection.activeObject;
+ Selection.activeObject = null;
+ Selection.activeObject = selection;
+ }
+ EditorUtility.ClearProgressBar();
+ EditorUtility.DisplayDialog("Import Complete", "The text tables have been updated from CSV. ", "OK");
+ }
+ finally
+ {
+ EditorUtility.ClearProgressBar();
+ }
+ }
+
+ private void ImportFromCSVFolder()
+ {
+ if (!EditorUtility.DisplayDialog("Import CSV?", "Importing from CSV will overwrite any existing languages or fields with the same name in the current contents. Are you sure?", "Import", "Cancel")) return;
+ string newFolder = EditorUtility.OpenFolderPanel("Import from CSV Folder", GetPath(prefs.csvFilename), "csv");
+ if (string.IsNullOrEmpty(newFolder)) return;
+ try
+ {
+ EditorUtility.DisplayProgressBar("Importing CSV Files", newFolder, 0);
+ var filenames = Directory.GetFiles(newFolder);
+ foreach (var filename in filenames)
+ {
+ if (!filename.EndsWith(".csv", StringComparison.OrdinalIgnoreCase)) continue;
+ Debug.Log($"Importing {filename}");
+ ImportCSVFile(filename);
+ }
+ if (TextTableEditorWindow.instance != null)
+ {
+ var selection = Selection.activeObject;
+ Selection.activeObject = null;
+ Selection.activeObject = selection;
+ }
+ EditorUtility.ClearProgressBar();
+ EditorUtility.DisplayDialog("Import Complete", "The text tables have been updated from CSV. ", "OK");
+ }
+ finally
+ {
+ EditorUtility.ClearProgressBar();
+ }
+ }
+
+ private string GetPath(string filename)
+ {
+ if (string.IsNullOrEmpty(filename)) return string.Empty;
+ try
+ {
+ return Path.GetDirectoryName(filename);
+ }
+ catch (System.ArgumentException)
+ {
+ return string.Empty;
+ }
+ }
+
+ private List GetLanguages()
+ {
+ var hashSet = new HashSet();
+ foreach (var textTable in textTables)
+ {
+ if (textTable == null) continue;
+ foreach (var language in textTable.languages.Keys)
+ {
+ hashSet.Add(language);
+ }
+ }
+ return new List(hashSet);
+ }
+
+ private void ExportCSV(string csvFilename, bool separateFiles)
+ {
+ var languages = GetLanguages();
+ if (separateFiles)
+ {
+ foreach (var language in languages)
+ {
+ var content = new List>();
+ var row = new List();
+ row.Add("Language");
+ row.Add(language);
+ content.Add(row);
+ foreach (var textTable in textTables)
+ {
+ if (textTable == null) continue;
+ var hasLanguage = textTable.HasLanguage(language);
+ int languageID = textTable.GetLanguageID(language);
+ foreach (var fieldKvp in textTable.fields)
+ {
+ var field = fieldKvp.Value;
+ row = new List();
+ row.Add(field.fieldName);
+ var text = hasLanguage ? field.GetTextForLanguage(languageID) : "";
+ row.Add(text);
+ content.Add(row);
+ }
+ }
+ var languageFilename = csvFilename.Substring(0, csvFilename.Length - 4) + "_" + language + ".csv";
+ CSVUtility.WriteCSVFile(content, languageFilename, prefs.encodingType);
+ }
+ }
+ else
+ {
+ // All in one file:
+ var content = new List>();
+
+ // Heading rows:
+ var row = new List();
+ content.Add(row);
+ row.Add("Field");
+ foreach (var language in languages)
+ {
+ row.Add(language);
+ }
+ foreach (var textTable in textTables)
+ {
+ if (textTable == null) continue;
+ // One row per field:
+ foreach (var kvp in textTable.fields)
+ {
+ var field = kvp.Value;
+ row = new List();
+ content.Add(row);
+ row.Add(field.fieldName);
+ foreach (var language in languages)
+ {
+ if (textTable.HasLanguage(language))
+ {
+ var languageID = textTable.GetLanguageID(language);
+ var value = field.HasTextForLanguage(languageID) ? field.GetTextForLanguage(languageID) : "";
+ row.Add(value);
+ }
+ else
+ {
+ row.Add("");
+ }
+ }
+ }
+ }
+ CSVUtility.WriteCSVFile(content, csvFilename, prefs.encodingType);
+ }
+ }
+
+ private void ImportCSVFile(string csvFilename)
+ {
+ var content = CSVUtility.ReadCSVFile(csvFilename, prefs.encodingType);
+ if (content == null || content.Count < 1 || content[0].Count < 2) return;
+ var fieldList = new List();
+ var firstCell = content[0][0];
+ if (string.Equals(firstCell, "Language"))
+ {
+ // Single language file:
+ var language = content[0][1];
+ if (!string.IsNullOrEmpty(language))
+ {
+ foreach (var textTable in textTables)
+ {
+ if (textTable == null) continue;
+ if (!textTable.HasLanguage(language)) textTable.AddLanguage(language);
+ for (int y = 1; y < content.Count; y++)
+ {
+ var field = content[y][0];
+ if (string.IsNullOrEmpty(field)) continue;
+ fieldList.Add(field);
+ if (textTable.HasField(field))
+ {
+ for (int x = 1; x < content[y].Count; x++)
+ {
+ textTable.SetFieldTextForLanguage(field, language, content[y][x]);
+ }
+ }
+ }
+ textTable.ReorderFields(fieldList);
+ textTable.OnBeforeSerialize();
+ EditorUtility.SetDirty(textTable);
+ }
+ }
+ }
+ else
+ {
+ // All-in-one file:
+ foreach (var textTable in textTables)
+ {
+ if (textTable == null) continue;
+ for (int x = 1; x < content[0].Count; x++)
+ {
+ var language = content[0][x];
+ if (string.IsNullOrEmpty(language)) continue;
+ if (!textTable.HasLanguage(language)) textTable.AddLanguage(language);
+ for (int y = 1; y < content.Count; y++)
+ {
+ var field = content[y][0];
+ if (string.IsNullOrEmpty(field)) continue;
+ if (x == 1) fieldList.Add(field);
+ if (textTable.HasField(field))
+ {
+ if ((0 <= y && y < content.Count) && (0 <= x && x < content[y].Count))
+ {
+ textTable.SetFieldTextForLanguage(field, language, content[y][x]);
+ }
+ }
+ }
+ }
+ textTable.ReorderFields(fieldList);
+ textTable.OnBeforeSerialize();
+ EditorUtility.SetDirty(textTable);
+ }
+ }
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableMassExportImportWindow.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableMassExportImportWindow.cs.meta
new file mode 100644
index 000000000..74ff3e44d
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableMassExportImportWindow.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: 74cdaa5c88e290f41b2cb6ab33f62897
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/Text/TextTableMassExportImportWindow.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI.meta
new file mode 100644
index 000000000..2a1dc5329
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 901ff9041fac8b24f8112acc388ddedc
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/CheckInputManagerSettings.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/CheckInputManagerSettings.cs
new file mode 100644
index 000000000..e54d2e6b8
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/CheckInputManagerSettings.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// This script runs when Unity starts or reloads assemblies after compilation.
+ /// If it hasn't yet asked, it asks to set the InputDeviceManager standard inputs.
+ ///
+ [InitializeOnLoad]
+ public static class CheckInputManagerSettings
+ {
+
+ private const string CheckedInputManagerSettingsEditorPrefsKey = "PixelCrushers.CheckedInputManagerSettings";
+
+ static CheckInputManagerSettings()
+ {
+#if !USE_NEW_INPUT
+ var alreadyAsked = EditorPrefs.GetBool(CheckedInputManagerSettingsEditorPrefsKey, false);
+ EditorPrefs.SetBool(CheckedInputManagerSettingsEditorPrefsKey, true);
+ if (InputDeviceManagerEditor.HasStandardInputDefinitions() || alreadyAsked) return;
+ if (EditorUtility.DisplayDialog("Add Input Manager Settings?",
+ "Do you want to add standard input definitions for joystick axes so the Input Device Manager can detect when the player is using a joystick?", "Yes", "No"))
+ {
+ InputDeviceManagerEditor.AddStandardInputDefinitions();
+ Debug.Log("Added standard input definitions.");
+ }
+#endif
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/CheckInputManagerSettings.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/CheckInputManagerSettings.cs.meta
new file mode 100644
index 000000000..121fd0cbc
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/CheckInputManagerSettings.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 50f62b6cda68c8b478898a0dd4a7b3db
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/CheckInputManagerSettings.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/InputDeviceManagerEditor.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/InputDeviceManagerEditor.cs
new file mode 100644
index 000000000..56d7c281f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/InputDeviceManagerEditor.cs
@@ -0,0 +1,261 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ [CustomEditor(typeof(InputDeviceManager), true)]
+ public class InputDeviceManagerEditor : Editor
+ {
+
+ public override void OnInspectorGUI()
+ {
+ base.OnInspectorGUI();
+ var inputDeviceManager = target as InputDeviceManager;
+ if (GUILayout.Button(new GUIContent("Add Input Definitions", "If any of the buttons or axes listed above aren't in Unity's Input Manager, add them.")))
+ {
+ AddInputDefinitions(inputDeviceManager);
+ }
+ if (inputDeviceManager.joystickAxesToCheck == null || inputDeviceManager.joystickAxesToCheck.Length == 0)
+ {
+ if (GUILayout.Button(new GUIContent("Check Default Joystick Axes", "Check joystick axis movement to detect switch to joystick mode.")))
+ {
+ SetDefaultJoystickAxesToCheck(inputDeviceManager);
+ }
+ }
+ }
+
+ public static void SetDefaultJoystickAxesToCheck(InputDeviceManager inputDeviceManager)
+ {
+ if (inputDeviceManager == null) return;
+ inputDeviceManager.joystickAxesToCheck = new string[] { "JoystickAxis1", "JoystickAxis2", "JoystickAxis3", "JoystickAxis4", "JoystickAxis6", "JoystickAxis7" };
+ AddInputDefinitions(inputDeviceManager);
+ }
+
+ public static void AddInputDefinitions(InputDeviceManager inputDeviceManager)
+ {
+ if (inputDeviceManager == null) return;
+ foreach (var button in inputDeviceManager.joystickButtonsToCheck)
+ {
+ AddInputDefinition(button);
+ }
+ foreach (var axis in inputDeviceManager.joystickAxesToCheck)
+ {
+ AddInputDefinition(axis);
+ }
+ foreach (var button in inputDeviceManager.keyButtonsToCheck)
+ {
+ AddInputDefinition(button);
+ }
+ foreach (var button in inputDeviceManager.backButtons)
+ {
+ AddInputDefinition(button);
+ }
+ Debug.Log("All input definitions are in Unity's Input Manager.");
+ }
+
+ public static bool HasStandardInputDefinitions()
+ {
+ return AxisDefined("JoystickAxis7");
+ }
+
+ public static void AddStandardInputDefinitions()
+ {
+ AddAxis(new InputAxis() { name = "JoystickAxis1", dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 1, joyNum = 0, });
+ AddAxis(new InputAxis() { name = "JoystickAxis2", dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 2, joyNum = 0, });
+ AddAxis(new InputAxis() { name = "JoystickAxis3", dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 3, joyNum = 0, });
+ AddAxis(new InputAxis() { name = "JoystickAxis4", dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 4, joyNum = 0, });
+ AddAxis(new InputAxis() { name = "JoystickAxis5", dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 5, joyNum = 0, });
+ AddAxis(new InputAxis() { name = "JoystickAxis6", dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 6, joyNum = 0, });
+ AddAxis(new InputAxis() { name = "JoystickAxis7", dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 7, joyNum = 0, });
+ }
+
+ public static void AddInputDefinition(string inputName)
+ {
+ if (string.IsNullOrEmpty(inputName)) return;
+ switch (inputName)
+ {
+ case "JoystickButton0":
+ AddAxis(new InputAxis() { name = inputName, positiveButton = "joystick button 0", gravity = 1000, dead = 0.0001f, sensitivity = 1000, type = AxisType.KeyOrMouseButton });
+ break;
+ case "JoystickButton1":
+ AddAxis(new InputAxis() { name = inputName, positiveButton = "joystick button 1", gravity = 1000, dead = 0.0001f, sensitivity = 1000, type = AxisType.KeyOrMouseButton });
+ break;
+ case "JoystickButton2":
+ AddAxis(new InputAxis() { name = inputName, positiveButton = "joystick button 2", gravity = 1000, dead = 0.0001f, sensitivity = 1000, type = AxisType.KeyOrMouseButton });
+ break;
+ case "JoystickButton3":
+ AddAxis(new InputAxis() { name = inputName, positiveButton = "joystick button 3", gravity = 1000, dead = 0.0001f, sensitivity = 1000, type = AxisType.KeyOrMouseButton });
+ break;
+ case "JoystickButton4":
+ AddAxis(new InputAxis() { name = inputName, positiveButton = "joystick button 4", gravity = 1000, dead = 0.0001f, sensitivity = 1000, type = AxisType.KeyOrMouseButton });
+ break;
+ case "JoystickButton5":
+ AddAxis(new InputAxis() { name = inputName, positiveButton = "joystick button 5", gravity = 1000, dead = 0.0001f, sensitivity = 1000, type = AxisType.KeyOrMouseButton });
+ break;
+ case "JoystickButton6":
+ AddAxis(new InputAxis() { name = inputName, positiveButton = "joystick button 6", gravity = 1000, dead = 0.0001f, sensitivity = 1000, type = AxisType.KeyOrMouseButton });
+ break;
+ case "JoystickButton7":
+ AddAxis(new InputAxis() { name = inputName, positiveButton = "joystick button 7", gravity = 1000, dead = 0.0001f, sensitivity = 1000, type = AxisType.KeyOrMouseButton });
+ break;
+ case "JoystickAxis1":
+ AddAxis(new InputAxis() { name = inputName, dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 1, joyNum = 0, });
+ break;
+ case "JoystickAxis2":
+ AddAxis(new InputAxis() { name = inputName, dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 2, joyNum = 0, });
+ break;
+ case "JoystickAxis3":
+ AddAxis(new InputAxis() { name = inputName, dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 3, joyNum = 0, });
+ break;
+ case "JoystickAxis4":
+ AddAxis(new InputAxis() { name = inputName, dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 4, joyNum = 0, });
+ break;
+ case "JoystickAxis5":
+ AddAxis(new InputAxis() { name = inputName, dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 5, joyNum = 0, });
+ break;
+ case "JoystickAxis6":
+ AddAxis(new InputAxis() { name = inputName, dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 6, joyNum = 0, });
+ break;
+ case "JoystickAxis7":
+ AddAxis(new InputAxis() { name = inputName, dead = 0.2f, sensitivity = 1f, type = AxisType.JoystickAxis, axis = 7, joyNum = 0, });
+ break;
+ default:
+ AddUnrecognizedInputDefinition(inputName);
+ return;
+ }
+ }
+
+ private static void AddUnrecognizedInputDefinition(string inputName)
+ {
+ if (string.IsNullOrEmpty(inputName)) return;
+ if (inputName.ToLower().Contains("button"))
+ {
+ Debug.LogWarning("Will add button to Input Manager: " + inputName + " but you may need to check its values (Edit > Project Settings > Input).");
+ var buttonName = ObjectNames.NicifyVariableName(inputName).ToLower();
+ AddAxis(new InputAxis() { name = inputName, positiveButton = buttonName, gravity = 1000, dead = 0.0001f, sensitivity = 1000, type = AxisType.KeyOrMouseButton });
+ }
+ else
+ {
+ AddAxisUndefined(inputName);
+ }
+ }
+
+ // From: https://plyoung.appspot.com/blog/manipulating-input-manager-in-script.html
+
+ private static SerializedProperty GetChildProperty(SerializedProperty parent, string name)
+ {
+ SerializedProperty child = parent.Copy();
+ child.Next(true);
+ do
+ {
+ if (child.name == name) return child;
+ }
+ while (child.Next(false));
+ return null;
+ }
+
+ public enum AxisType
+ {
+ KeyOrMouseButton = 0,
+ MouseMovement = 1,
+ JoystickAxis = 2
+ };
+
+ public class InputAxis
+ {
+ public string name;
+ public string descriptiveName;
+ public string descriptiveNegativeName;
+ public string negativeButton;
+ public string positiveButton;
+ public string altNegativeButton;
+ public string altPositiveButton;
+
+ public float gravity;
+ public float dead;
+ public float sensitivity;
+
+ public bool snap = false;
+ public bool invert = false;
+
+ public AxisType type;
+
+ public int axis;
+ public int joyNum;
+ }
+
+ private static bool AxisDefined(string axisName)
+ {
+#if USE_NEW_INPUT
+ return true; // Assume InputActions will define axis.
+#else
+ try
+ {
+ var assets = AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/InputManager.asset");
+ if (assets == null || assets.Length == 0) return true; // Gracefully skip if can't load InputManager.
+ SerializedObject serializedObject = new SerializedObject(assets[0]);
+ SerializedProperty axesProperty = serializedObject.FindProperty("m_Axes");
+
+ var valid = axesProperty.Next(true);
+ valid = valid || axesProperty.Next(true);
+ while (valid && axesProperty.Next(false))
+ {
+ SerializedProperty axis = axesProperty.Copy();
+ if (axis.Next(true))
+ {
+ if (axis.stringValue == axisName) return true;
+ }
+ }
+ return false;
+ }
+ catch (System.InvalidOperationException)
+ {
+ return false;
+ }
+#endif
+ }
+
+ private static void AddAxis(InputAxis axis)
+ {
+ if (AxisDefined(axis.name)) return;
+
+ Debug.Log("Added to Input Manager: " + axis.name);
+
+ SerializedObject serializedObject = new SerializedObject(AssetDatabase.LoadAllAssetsAtPath("ProjectSettings/InputManager.asset")[0]);
+ SerializedProperty axesProperty = serializedObject.FindProperty("m_Axes");
+
+ axesProperty.arraySize++;
+ serializedObject.ApplyModifiedProperties();
+
+ SerializedProperty axisProperty = axesProperty.GetArrayElementAtIndex(axesProperty.arraySize - 1);
+
+ GetChildProperty(axisProperty, "m_Name").stringValue = axis.name;
+ GetChildProperty(axisProperty, "descriptiveName").stringValue = axis.descriptiveName;
+ GetChildProperty(axisProperty, "descriptiveNegativeName").stringValue = axis.descriptiveNegativeName;
+ GetChildProperty(axisProperty, "negativeButton").stringValue = axis.negativeButton;
+ GetChildProperty(axisProperty, "positiveButton").stringValue = axis.positiveButton;
+ GetChildProperty(axisProperty, "altNegativeButton").stringValue = axis.altNegativeButton;
+ GetChildProperty(axisProperty, "altPositiveButton").stringValue = axis.altPositiveButton;
+ GetChildProperty(axisProperty, "gravity").floatValue = axis.gravity;
+ GetChildProperty(axisProperty, "dead").floatValue = axis.dead;
+ GetChildProperty(axisProperty, "sensitivity").floatValue = axis.sensitivity;
+ GetChildProperty(axisProperty, "snap").boolValue = axis.snap;
+ GetChildProperty(axisProperty, "invert").boolValue = axis.invert;
+ GetChildProperty(axisProperty, "type").intValue = (int)axis.type;
+ GetChildProperty(axisProperty, "axis").intValue = axis.axis - 1;
+ GetChildProperty(axisProperty, "joyNum").intValue = axis.joyNum;
+
+ serializedObject.ApplyModifiedProperties();
+ }
+
+ private static void AddAxisUndefined(string axisName)
+ {
+ if (AxisDefined(axisName)) return;
+ Debug.LogWarning("Will add to Input Manager: " + axisName + " but you must set its values (Edit > Project Settings > Input).");
+ AddAxis(new InputAxis() { name = axisName });
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/InputDeviceManagerEditor.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/InputDeviceManagerEditor.cs.meta
new file mode 100644
index 000000000..c58ef69e4
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/InputDeviceManagerEditor.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 0c62130f496886141a8c2725b8b12b7c
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/InputDeviceManagerEditor.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UIDropdownFieldDrawer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UIDropdownFieldDrawer.cs
new file mode 100644
index 000000000..77fa98d8b
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UIDropdownFieldDrawer.cs
@@ -0,0 +1,71 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ [CustomPropertyDrawer(typeof(UIDropdownField), true)]
+ public class UIDropdownFieldDrawer : PropertyDrawer
+ {
+
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
+ {
+ try
+ {
+ var uiDropdownProperty = property.FindPropertyRelative("m_uiDropdown");
+ var tmpDropdownProperty = property.FindPropertyRelative("m_tmpDropdown");
+ var isUIDropdownAssigned = (uiDropdownProperty != null) && (uiDropdownProperty.objectReferenceValue != null);
+ var isTMPDropdownAssigned = (tmpDropdownProperty != null) && (tmpDropdownProperty.objectReferenceValue != null);
+ var isContentAssigned = isUIDropdownAssigned || isTMPDropdownAssigned;
+ int numUnassignedLines = 1;
+ if (tmpDropdownProperty != null) numUnassignedLines++;
+ return (isContentAssigned ? 1 : numUnassignedLines) * EditorGUIUtility.singleLineHeight;
+ }
+ catch (System.ArgumentException) // Handles IMGUI->UITK bug in Unity 2022.2.
+ {
+ return 2 * EditorGUIUtility.singleLineHeight;
+ }
+ }
+
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ try
+ {
+ EditorGUI.BeginProperty(position, label, property);
+
+ position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
+
+ var uiDropdownProperty = property.FindPropertyRelative("m_uiDropdown");
+ var tmpDropdownProperty = property.FindPropertyRelative("m_tmpDropdown");
+ if (uiDropdownProperty == null)
+ {
+ Debug.LogError("Sorry! There was an internal editor error with a UI Dropdown Field. Please contact Pixel Crushers for support.");
+ return;
+ }
+ var isUIDropdownAssigned = (uiDropdownProperty != null) && (uiDropdownProperty.objectReferenceValue != null);
+ var isTMPDropdownAssigned = (tmpDropdownProperty != null) && (tmpDropdownProperty.objectReferenceValue != null);
+ var isContentAssigned = isUIDropdownAssigned || isTMPDropdownAssigned;
+
+ float yOffset = 0;
+
+ if (isUIDropdownAssigned || !isContentAssigned)
+ {
+ EditorGUI.PropertyField(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), uiDropdownProperty, GUIContent.none);
+ yOffset += EditorGUIUtility.singleLineHeight;
+ }
+
+ if (isTMPDropdownAssigned || (tmpDropdownProperty != null && !isContentAssigned))
+ {
+ EditorGUI.PropertyField(new Rect(position.x, position.y + yOffset, position.width, EditorGUIUtility.singleLineHeight), tmpDropdownProperty, GUIContent.none);
+ }
+ }
+ finally
+ {
+ EditorGUI.EndProperty();
+ }
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UIDropdownFieldDrawer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UIDropdownFieldDrawer.cs.meta
new file mode 100644
index 000000000..5a57d601f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UIDropdownFieldDrawer.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: 2fa119de2278f404c87b62a30d61f4cd
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UIDropdownFieldDrawer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UILocalizationManagerEditor.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UILocalizationManagerEditor.cs
new file mode 100644
index 000000000..0ec1289c3
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UILocalizationManagerEditor.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ [CustomEditor(typeof(UILocalizationManager), true)]
+ public class UILocalizationManagerEditor : Editor
+ {
+
+ public override void OnInspectorGUI()
+ {
+ base.OnInspectorGUI();
+ if (GUILayout.Button(new GUIContent("Reset Language PlayerPrefs", "Delete the language selection saved in PlayerPrefs.")))
+ {
+ PlayerPrefs.DeleteKey((target as UILocalizationManager).currentLanguagePlayerPrefsKey);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UILocalizationManagerEditor.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UILocalizationManagerEditor.cs.meta
new file mode 100644
index 000000000..4c040fc69
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UILocalizationManagerEditor.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 2b923b59825130147956e438e2347799
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UILocalizationManagerEditor.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UITextFieldDrawer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UITextFieldDrawer.cs
new file mode 100644
index 000000000..c50899707
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UITextFieldDrawer.cs
@@ -0,0 +1,81 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+
+namespace PixelCrushers
+{
+
+ [CustomPropertyDrawer(typeof(UITextField), true)]
+ public class UITextFieldDrawer : PropertyDrawer
+ {
+
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
+ {
+ try
+ {
+ var uiTextProperty = property.FindPropertyRelative("m_uiText");
+ var textMeshProUGUIProperty = property.FindPropertyRelative("m_textMeshProUGUI");
+ var superTextMeshProperty = property.FindPropertyRelative("m_superTextMesh");
+ var isUiTextAssigned = (uiTextProperty != null) && (uiTextProperty.objectReferenceValue != null);
+ var isTextMeshProUGUIAssigned = (textMeshProUGUIProperty != null) && (textMeshProUGUIProperty.objectReferenceValue != null);
+ var isSuperTextMeshAssigned = (superTextMeshProperty != null) && (superTextMeshProperty.objectReferenceValue != null);
+ var isContentAssigned = isUiTextAssigned || isTextMeshProUGUIAssigned || isSuperTextMeshAssigned;
+ int numUnassignedLines = 1;
+ if (textMeshProUGUIProperty != null) numUnassignedLines++;
+ if (superTextMeshProperty != null) numUnassignedLines++;
+ return (isContentAssigned ? 1 : numUnassignedLines) * EditorGUIUtility.singleLineHeight;
+ }
+ catch (System.ArgumentException) // Handles IMGUI->UITK bug in Unity 2022.2.
+ {
+ return 2 * EditorGUIUtility.singleLineHeight;
+ }
+ }
+
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ try
+ {
+ EditorGUI.BeginProperty(position, label, property);
+
+ position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
+
+ var uiTextProperty = property.FindPropertyRelative("m_uiText");
+ var textMeshProUGUIProperty = property.FindPropertyRelative("m_textMeshProUGUI");
+ var superTextMeshProperty = property.FindPropertyRelative("m_superTextMesh");
+ if (uiTextProperty == null)
+ {
+ Debug.LogError("Sorry! There was an internal editor error with a UI Text Field. Please contact Pixel Crushers for support.");
+ return;
+ }
+ var isUiTextAssigned = (uiTextProperty != null) && (uiTextProperty.objectReferenceValue != null);
+ var isTextMeshProUGUIAssigned = (textMeshProUGUIProperty != null) && (textMeshProUGUIProperty.objectReferenceValue != null);
+ var isSuperTextMeshAssigned = (superTextMeshProperty != null) && (superTextMeshProperty.objectReferenceValue != null);
+ var isContentAssigned = isUiTextAssigned || isTextMeshProUGUIAssigned || isSuperTextMeshAssigned;
+
+ float yOffset = 0;
+
+ if (isUiTextAssigned || !isContentAssigned)
+ {
+ EditorGUI.PropertyField(new Rect(position.x, position.y, position.width, EditorGUIUtility.singleLineHeight), uiTextProperty, GUIContent.none);
+ yOffset += EditorGUIUtility.singleLineHeight;
+ }
+
+ if (isTextMeshProUGUIAssigned || (textMeshProUGUIProperty != null && !isContentAssigned))
+ {
+ EditorGUI.PropertyField(new Rect(position.x, position.y + yOffset, position.width, EditorGUIUtility.singleLineHeight), textMeshProUGUIProperty, GUIContent.none);
+ }
+
+ if (isSuperTextMeshAssigned || (superTextMeshProperty != null && !isContentAssigned))
+ {
+ EditorGUI.PropertyField(new Rect(position.x, position.y + yOffset, position.width, EditorGUIUtility.singleLineHeight), superTextMeshProperty, GUIContent.none);
+ }
+ }
+ finally
+ {
+ EditorGUI.EndProperty();
+ }
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UITextFieldDrawer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UITextFieldDrawer.cs.meta
new file mode 100644
index 000000000..e6e627a98
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UITextFieldDrawer.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: d0a054d83c759ad44be2663a383637f5
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UI/UITextFieldDrawer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UnityEvents.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UnityEvents.meta
new file mode 100644
index 000000000..c0918a906
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UnityEvents.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: ddf075d19a817ce4e85d1a238e5550f3
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UnityEvents/TagMaskDrawer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UnityEvents/TagMaskDrawer.cs
new file mode 100644
index 000000000..cf732eb5f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UnityEvents/TagMaskDrawer.cs
@@ -0,0 +1,153 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEditor;
+using System;
+
+namespace PixelCrushers
+{
+
+ [CustomPropertyDrawer(typeof(TagMask), true)]
+ public class TagMaskDrawer : PropertyDrawer
+ {
+
+ public struct MenuItemTagInfo
+ {
+ public SerializedProperty property;
+ public int allTagsIndex;
+ public MenuItemTagInfo(SerializedProperty property, int allTagsIndex)
+ {
+ this.property = property;
+ this.allTagsIndex = allTagsIndex;
+ }
+ }
+
+ public override float GetPropertyHeight(SerializedProperty property, GUIContent label)
+ {
+ return EditorGUIUtility.singleLineHeight;
+ }
+
+ public override void OnGUI(Rect position, SerializedProperty property, GUIContent label)
+ {
+ EditorGUI.BeginProperty(position, label, property);
+
+ position = EditorGUI.PrefixLabel(position, GUIUtility.GetControlID(FocusType.Passive), label);
+
+ try
+ {
+ // Count how many tags are selected in the m_tag property:
+ var allTags = UnityEditorInternal.InternalEditorUtility.tags;
+ var tagsProperty = property.FindPropertyRelative("m_tags");
+ var lastSelectedTag = string.Empty;
+ int numSelected = 0;
+ for (int i = 0; i < allTags.Length; i++)
+ {
+ for (int j = 0; j < tagsProperty.arraySize; j++)
+ {
+ var selectedTag = tagsProperty.GetArrayElementAtIndex(j).stringValue;
+ if (string.Equals(selectedTag, allTags[i]))
+ {
+ lastSelectedTag = selectedTag;
+ numSelected++;
+ break;
+ }
+ }
+ }
+
+ // Show the dropdown button:
+ var dropdownButtonSummary = (numSelected == 0) ? "Nothing"
+ : (numSelected == allTags.Length) ? "Everything"
+ : (numSelected > 1) ? "Mixed"
+ : lastSelectedTag;
+#if UNITY_5_3 || UNITY_5_4 || UNITY_5_5
+ if (GUI.Button(position, dropdownButtonSummary))
+#else
+ if (EditorGUI.DropdownButton(position, new GUIContent(dropdownButtonSummary), FocusType.Keyboard))
+#endif
+ {
+ // If dropdown button is clicked, show a context menu that looks like EditorGUI.MaskField:
+ var menu = new GenericMenu();
+ menu.AddItem(new GUIContent("Nothing"), false, OnSelectNothing, property);
+ menu.AddItem(new GUIContent("Everything"), false, OnSelectEverything, property);
+ for (int i = 0; i < allTags.Length; i++)
+ {
+ var isSelected = false;
+ for (int j = 0; j < tagsProperty.arraySize; j++)
+ {
+ var selectedTag = tagsProperty.GetArrayElementAtIndex(j).stringValue;
+ if (string.Equals(selectedTag, allTags[i]))
+ {
+ isSelected = true;
+ break;
+ }
+ }
+ menu.AddItem(new GUIContent(allTags[i]), isSelected, OnSelectTag, new MenuItemTagInfo(property, i));
+ }
+ menu.ShowAsContext();
+ }
+ }
+ finally
+ {
+ EditorGUI.EndProperty();
+ }
+ }
+
+ private void OnSelectNothing(object data)
+ {
+ var property = (SerializedProperty)data;
+ var tagsProperty = property.FindPropertyRelative("m_tags");
+ property.serializedObject.Update();
+ tagsProperty.ClearArray();
+ property.serializedObject.ApplyModifiedProperties();
+ }
+
+ private void OnSelectEverything(object data)
+ {
+ var property = (SerializedProperty)data;
+ var tagsProperty = property.FindPropertyRelative("m_tags");
+ property.serializedObject.Update();
+ tagsProperty.ClearArray();
+ var allTags = UnityEditorInternal.InternalEditorUtility.tags;
+ tagsProperty.arraySize = allTags.Length;
+ for (int i = 0; i < allTags.Length; i++)
+ {
+ tagsProperty.GetArrayElementAtIndex(i).stringValue = allTags[i];
+ }
+ property.serializedObject.ApplyModifiedProperties();
+ }
+
+ private void OnSelectTag(object data)
+ {
+ var menuItemInfo = (MenuItemTagInfo)data;
+ var selectedIndex = menuItemInfo.allTagsIndex;
+ var allTags = UnityEditorInternal.InternalEditorUtility.tags;
+ if (0 <= selectedIndex && selectedIndex < allTags.Length)
+ {
+ var selectedTag = allTags[selectedIndex];
+ var tagsProperty = menuItemInfo.property.FindPropertyRelative("m_tags");
+ int indexInTagsProperty = -1;
+ for (int i = 0; i < tagsProperty.arraySize; i++)
+ {
+ if (string.Equals(tagsProperty.GetArrayElementAtIndex(i).stringValue, selectedTag))
+ {
+ indexInTagsProperty = i;
+ break;
+ }
+ }
+ tagsProperty.serializedObject.Update();
+ if (indexInTagsProperty == -1)
+ {
+ // Add:
+ tagsProperty.arraySize++;
+ tagsProperty.GetArrayElementAtIndex(tagsProperty.arraySize - 1).stringValue = selectedTag;
+ }
+ else
+ {
+ // Delete:
+ tagsProperty.DeleteArrayElementAtIndex(indexInTagsProperty);
+ }
+ tagsProperty.serializedObject.ApplyModifiedProperties();
+ }
+ }
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UnityEvents/TagMaskDrawer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UnityEvents/TagMaskDrawer.cs.meta
new file mode 100644
index 000000000..c9dc9a32d
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UnityEvents/TagMaskDrawer.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 2dfee8025c152284d88e0a153cfb30c6
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Editor/UnityEvents/TagMaskDrawer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System.meta
new file mode 100644
index 000000000..23257c123
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: d5343f589cb0763408d0d03ff14197ea
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/DataSynchronizer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/DataSynchronizer.cs
new file mode 100644
index 000000000..5b095ec61
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/DataSynchronizer.cs
@@ -0,0 +1,82 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Uses the MessageSystem to keep data synchronized between a source and a listener.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class DataSynchronizer : MonoBehaviour, IMessageHandler
+ {
+
+ ///
+ /// Message that data source sends to inform listeners that the value changed.
+ ///
+ public const string DataSourceValueChangedMessage = "Data Source Value Changed";
+
+ ///
+ /// Message that listeners send to request this data synchronizer to change the data source's value.
+ ///
+ public const string RequestDataSourceChangeValueMessage = "Request Data Source Change Value";
+
+ [Tooltip("A name to associate with the data source. Data change messages that reference this name will invoke the value update events.")]
+ [SerializeField]
+ private string m_dataSourceName;
+
+ [SerializeField]
+ private ObjectUnityEvent m_onRequestDataSourceChangeValue = new ObjectUnityEvent();
+
+ ///
+ /// A name to associate with the data source. Data change messages that reference this name will invoke the value update events.
+ ///
+ public string dataSourceName
+ {
+ get { return m_dataSourceName; }
+ set { m_dataSourceName = value; }
+ }
+
+ ///
+ /// Event to send to data source to request it to change value.
+ ///
+ public ObjectUnityEvent onRequestDataSourceChangeValue
+ {
+ get { return m_onRequestDataSourceChangeValue; }
+ set { m_onRequestDataSourceChangeValue = value; }
+ }
+
+ protected virtual void OnEnable()
+ {
+ MessageSystem.AddListener(this, RequestDataSourceChangeValueMessage, dataSourceName);
+ }
+
+ protected virtual void OnDisable()
+ {
+ MessageSystem.RemoveListener(this, RequestDataSourceChangeValueMessage, dataSourceName);
+ }
+
+ ///
+ /// Handles messages requesting to change the value of the data source. On receipt of the
+ /// RequestDataSourceChangeValueMessage, invokes the onRequestDataSourceChangeValue event,
+ /// passing the first argument of the message as the new value.
+ ///
+ /// First argument is the new value.
+ public void OnMessage(MessageArgs messageArgs)
+ {
+ onRequestDataSourceChangeValue.Invoke(messageArgs.firstValue);
+ }
+
+ ///
+ /// When the data source has changed its value, it should call this method to inform listeners.
+ ///
+ /// The data source's new value.
+ public void DataSourceValueChanged(object newValue)
+ {
+ MessageSystem.SendMessage(this, DataSourceValueChangedMessage, dataSourceName, newValue);
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/DataSynchronizer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/DataSynchronizer.cs.meta
new file mode 100644
index 000000000..6f487e377
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/DataSynchronizer.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: cdcf52bf8761a4a4399a14ff13b13c75
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/DataSynchronizer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/IMessageHandler.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/IMessageHandler.cs
new file mode 100644
index 000000000..981e32ab2
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/IMessageHandler.cs
@@ -0,0 +1,18 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Interface for MessageSystem message handlers.
+ ///
+ public interface IMessageHandler
+ {
+ ///
+ /// Handles a message that the message handler is listening for.
+ ///
+ /// The message that was sent to the MessageSystem.
+ void OnMessage(MessageArgs messageArgs);
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/IMessageHandler.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/IMessageHandler.cs.meta
new file mode 100644
index 000000000..06cc88b8a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/IMessageHandler.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 724cd730cf793a948b7e455d28be0088
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/IMessageHandler.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageArgs.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageArgs.cs
new file mode 100644
index 000000000..d161fb4a0
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageArgs.cs
@@ -0,0 +1,172 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// This struct is passed to listeners of the MessageSystem when a message is sent.
+ ///
+ [Serializable]
+ public struct MessageArgs
+ {
+ ///
+ /// Reference to the message sender (e.g., GameObject or possibly custom-defined string ID).
+ ///
+ public object sender;
+
+ ///
+ /// Reference to the message target (e.g., GameObject or possibly custom-defined string ID).
+ /// Typically null or blank string is interpreted as broadcast to all targets.
+ ///
+ public object target;
+
+ public string message;
+
+ public string parameter;
+
+ public object[] values;
+
+ ///
+ /// If true, the message arguments specify a target.
+ ///
+ public bool hasTarget {
+ get
+ {
+ return !(target == null || string.IsNullOrEmpty(targetString));
+ }
+ }
+
+ ///
+ /// True if the target value is a string or StringField.
+ ///
+ public bool isTargetString {
+ get
+ {
+ var type = (target != null) ? target.GetType() : null;
+ return target != null && (type == typeof(string) || type == typeof(StringField));
+ }
+ }
+
+ ///
+ /// If the target is a string or StringField, its value.
+ ///
+ public string targetString {
+ get
+ {
+ if (target == null) return string.Empty;
+ var type = target.GetType();
+ return (type == typeof(string)) ? (string)target :
+ ((type == typeof(StringField)) ? StringField.GetStringValue((StringField)target) : string.Empty);
+ }
+ }
+
+ public MessageArgs(object sender, object target, string message, string parameter, object[] values = null)
+ {
+ this.sender = sender;
+ this.target = target;
+ this.message = message;
+ this.parameter = parameter;
+ this.values = values;
+ }
+
+ public MessageArgs(object sender, string message, string parameter, object[] values = null)
+ {
+ this.sender = sender;
+ this.target = null;
+ this.message = message;
+ this.parameter = parameter;
+ this.values = values;
+ }
+
+ public bool Matches(string message, string parameter)
+ {
+ return string.Equals(message, this.message) && (string.IsNullOrEmpty(parameter) || string.Equals(parameter, this.parameter));
+ }
+
+ public bool Matches(StringField message, StringField parameter)
+ {
+ return string.Equals(message.value, this.message) && (StringField.IsNullOrEmpty(parameter) || string.Equals(parameter.value, this.parameter));
+ }
+
+ public bool Matches(StringField message, string parameter)
+ {
+ return string.Equals(message.value, this.message) && (string.IsNullOrEmpty(parameter) || string.Equals(parameter, this.parameter));
+ }
+
+ public bool Matches(string message, StringField parameter)
+ {
+ return string.Equals(message, this.message) && (StringField.IsNullOrEmpty(parameter) || string.Equals(parameter.value, this.parameter));
+ }
+
+ ///
+ /// Returns true if the args' sender matches a required sender.
+ ///
+ public bool IsRequiredSender(string requiredSender)
+ {
+ return string.IsNullOrEmpty(requiredSender) || string.Equals(requiredSender, GetSenderString());
+
+ }
+
+ ///
+ /// Returns true if the args' target matches a required target.
+ ///
+ public bool IsRequiredTarget(string requiredTarget)
+ {
+ return string.IsNullOrEmpty(requiredTarget) || string.Equals(requiredTarget, GetTargetString());
+ }
+
+ ///
+ /// Returns the string name of the sender.
+ ///
+ public string GetSenderString()
+ {
+ return GetObjectString(sender);
+ }
+
+ ///
+ /// Returns the string name of the target.
+ ///
+ public string GetTargetString()
+ {
+ return GetObjectString(target);
+ }
+
+ private string GetObjectString(object obj)
+ {
+ if (obj == null) return string.Empty;
+ var type = obj.GetType();
+ if (type == typeof(string)) return (string)obj;
+ if (type == typeof(StringField)) return StringField.GetStringValue((StringField)obj);
+ if (type == typeof(GameObject)) return (obj as GameObject).name;
+ if (type == typeof(Component)) return (obj as Component).name;
+ return obj.ToString();
+ }
+
+ public object firstValue
+ {
+ get
+ {
+ return (values != null && values.Length > 0) ? values[0] : null;
+ }
+ }
+
+ public int intValue
+ {
+ get
+ {
+ try
+ {
+ return (int)firstValue;
+ }
+ catch (Exception)
+ {
+ return 0;
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageArgs.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageArgs.cs.meta
new file mode 100644
index 000000000..c33bd94c3
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageArgs.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 6b76be43298c10c4fa3a4923b84f7083
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageArgs.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageEvents.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageEvents.cs
new file mode 100644
index 000000000..e2458999a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageEvents.cs
@@ -0,0 +1,128 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEngine.Events;
+using System;
+
+namespace PixelCrushers
+{
+
+ [Serializable]
+ public class MessageArgsEvent : UnityEvent { }
+
+ ///
+ /// Provides a in-editor way to listen for messages and invoke events when they occur,
+ /// and to send a message on demand.
+ ///
+ [Serializable]
+ [AddComponentMenu("")] // Use wrapper.
+ public class MessageEvents : MonoBehaviour, IMessageHandler
+ {
+
+ [Serializable]
+ public class MessageEvent
+ {
+ [Tooltip("(Optional) If set, only react to messages sent from this sender.")]
+ public GameObject requiredSender;
+
+ [Tooltip("(Optional) If set, only react to messages sent to this target.")]
+ public GameObject requiredTarget;
+
+ [Tooltip("Listen for this message.")]
+ public StringField message;
+
+ [Tooltip("(Optional) If set, listen for this parameter with the message.")]
+ public StringField parameter;
+
+ public MessageArgsEvent onMessage = new MessageArgsEvent();
+ }
+
+ [Serializable]
+ public class MessageToSend
+ {
+ [Tooltip("(Optional) If set, specify this GameObject as the message target.")]
+ public GameObject target;
+
+ [Tooltip("Send this message.")]
+ public StringField message;
+
+ [Tooltip("(Optional) If set, send this parameter with the message.")]
+ public StringField parameter;
+ }
+
+ [SerializeField]
+ private MessageEvent[] m_messagesToListenFor;
+
+ [SerializeField]
+ private MessageToSend[] m_messagesToSend;
+
+ ///
+ /// Listen for these messages and invoke an event when each message occurs.
+ ///
+ public MessageEvent[] messagesToListenFor
+ {
+ get { return m_messagesToListenFor; }
+ set { m_messagesToListenFor = value; }
+ }
+
+ ///
+ /// Send this message on demand by calling SendToMessageSystem().
+ ///
+ public MessageToSend[] messagesToSend
+ {
+ get { return m_messagesToSend; }
+ set { m_messagesToSend = value; }
+ }
+
+ protected virtual void OnEnable()
+ {
+ for (int i = 0; i < messagesToListenFor.Length; i++)
+ {
+ var messageEvent = messagesToListenFor[i];
+ MessageSystem.AddListener(this, messageEvent.message, messageEvent.parameter);
+ }
+ }
+
+ protected virtual void OnDisable()
+ {
+ for (int i = 0; i < messagesToListenFor.Length; i++)
+ {
+ var messageEvent = messagesToListenFor[i];
+ MessageSystem.RemoveListener(this, messageEvent.message, messageEvent.parameter);
+ }
+ }
+
+ public virtual void OnMessage(MessageArgs messageArgs)
+ {
+ for (int i = 0; i < messagesToListenFor.Length; i++)
+ {
+ var messageEvent = messagesToListenFor[i];
+ if (IsParticipantOk(messageEvent.requiredSender, messageArgs.sender) &&
+ IsParticipantOk(messageEvent.requiredTarget, messageArgs.target) &&
+ string.Equals(messageEvent.message, messageArgs.message) &&
+ (StringField.IsNullOrEmpty(messageEvent.parameter) || string.Equals(messageEvent.parameter, messageArgs.parameter)))
+ {
+ messageEvent.onMessage.Invoke(messageArgs);
+ }
+ }
+ }
+
+ protected virtual bool IsParticipantOk(GameObject requiredParticipant, object actualParticipant)
+ {
+ if (requiredParticipant == null) return true;
+ if (actualParticipant == null) return false;
+ return (actualParticipant as GameObject == requiredParticipant) ||
+ (actualParticipant as Transform == requiredParticipant.transform) ||
+ ((actualParticipant is MonoBehaviour) && (actualParticipant as MonoBehaviour).gameObject == requiredParticipant) ||
+ (actualParticipant.GetType() == typeof(string) && (string)actualParticipant == requiredParticipant.name) ||
+ (actualParticipant.GetType() == typeof(StringField) && StringField.GetStringValue(actualParticipant as StringField) == requiredParticipant.name);
+ }
+
+ public virtual void SendToMessageSystem(int index)
+ {
+ if (messagesToSend == null) return;
+ if (!(0 <= index && index < messagesToSend.Length)) return;
+ MessageSystem.SendMessageWithTarget(this, messagesToSend[index].target, messagesToSend[index].message, messagesToSend[index].parameter);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageEvents.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageEvents.cs.meta
new file mode 100644
index 000000000..3432c4f62
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageEvents.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: acd6f0e76fe660c4db56937d2c693902
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageEvents.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystem.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystem.cs
new file mode 100644
index 000000000..bb2e710ee
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystem.cs
@@ -0,0 +1,530 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System.Collections.Generic;
+using System;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// General purpose message system.
+ ///
+ public static class MessageSystem
+ {
+
+ public class ListenerInfo
+ {
+ public IMessageHandler listener;
+ public string message;
+ public string parameter;
+ public int frameAdded;
+ public bool removed;
+
+ public ListenerInfo() { }
+
+ public ListenerInfo(IMessageHandler listener, string message, string parameter)
+ {
+ this.listener = listener;
+ this.message = message;
+ this.parameter = parameter;
+ this.frameAdded = Time.frameCount;
+ this.removed = false;
+ }
+
+ public void Assign(IMessageHandler listener, string message, string parameter)
+ {
+ this.listener = listener;
+ this.message = message;
+ this.parameter = parameter;
+ this.frameAdded = Time.frameCount;
+ this.removed = false;
+ }
+
+ public void Clear()
+ {
+ this.listener = null;
+ this.message = null;
+ this.parameter = null;
+ this.removed = false;
+ }
+ }
+
+ private static List s_listenerInfo = new List();
+
+ private static Pool s_listenerInfoPool = new Pool();
+
+ private static HashSet s_sendersToLog = new HashSet();
+ private static HashSet s_listenersToLog = new HashSet();
+
+ private static bool s_sendInEditMode = false;
+
+ private static bool s_allowReceiveSameFrameAdded = true;
+
+ private static bool s_debug = false;
+ private static bool s_allowExceptions = false;
+
+ private static int s_sendMessageDepth = 0;
+
+#if UNITY_2019_3_OR_NEWER && UNITY_EDITOR
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
+ static void InitStaticVariables()
+ {
+ s_listenerInfo = new List();
+ s_listenerInfoPool = new Pool();
+ s_sendersToLog = new HashSet();
+ s_listenersToLog = new HashSet();
+ s_sendInEditMode = false;
+ s_allowReceiveSameFrameAdded = true;
+ s_debug = false;
+ s_sendMessageDepth = 0;
+ }
+#endif
+
+ ///
+ /// Send messages even when not playing.
+ ///
+ public static bool sendInEditMode
+ {
+ get { return s_sendInEditMode; }
+ set { s_sendInEditMode = value; }
+ }
+
+ ///
+ /// Allow listeners to receive messages on the same frame they registered with the MessageSystem.
+ ///
+ public static bool allowReceiveSameFrameAdded
+ {
+ get { return s_allowReceiveSameFrameAdded; }
+ set { s_allowReceiveSameFrameAdded = value; }
+ }
+
+ ///
+ /// Log message system activity.
+ ///
+ public static bool debug
+ {
+ get { return s_debug && Debug.isDebugBuild; }
+ set { s_debug = value; }
+ }
+
+ ///
+ /// Don't catch exceptions thrown by message recipients.
+ ///
+ public static bool allowExceptions
+ {
+ get { return s_allowExceptions && Debug.isDebugBuild; }
+ set { s_allowExceptions = value; }
+ }
+
+ private static List listenerInfo { get { return s_listenerInfo; } }
+
+ private static Pool listenerInfoPool { get { return s_listenerInfoPool; } }
+
+ ///
+ /// When we're in SendMessage(), don't remove items from listenerInfo because SendMessage() is
+ /// currently looping through listenerInfo. Instead, mark them for removal afterward.
+ ///
+ private static int sendMessageDepth { get { return s_sendMessageDepth; } set { s_sendMessageDepth = value; } }
+
+ ///
+ /// Checks if the specified listener, message, and parameter is registered with the message system.
+ ///
+ /// Listener to check.
+ /// Message to check.
+ /// Parameter to check, or blank for any parameter.
+ ///
+ public static bool IsListenerRegistered(IMessageHandler listener, string message, string parameter)
+ {
+ for (int i = 0; i < listenerInfo.Count; i++)
+ {
+ var x = listenerInfo[i];
+ if (!x.removed && x.listener == listener && string.Equals(x.message, message) && (string.Equals(x.parameter, parameter) || string.IsNullOrEmpty(x.parameter)))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// Adds a listener.
+ ///
+ /// Listener.
+ /// Message to listen for.
+ /// Message parameter to listen for, or blank for any parameter with the message.
+ public static void AddListener(IMessageHandler listener, string message, string parameter)
+ {
+ if (debug) Debug.Log("MessageSystem.AddListener(listener=" + listener + ": " + message + "," + parameter + ")");
+
+ // Check if listener is already registered:
+ for (int i = 0; i < listenerInfo.Count; i++)
+ {
+ var x = listenerInfo[i];
+ if (x.listener == listener && string.Equals(x.message, message) && (string.Equals(x.parameter, parameter) || string.IsNullOrEmpty(x.parameter)))
+ {
+ x.removed = false;
+ return;
+ }
+ }
+
+ // Otherwise add:
+ var info = listenerInfoPool.Get();
+ info.Assign(listener, message, parameter);
+ listenerInfo.Add(info);
+ }
+
+ ///
+ /// Adds a listener.
+ ///
+ /// Listener.
+ /// Message to listen for.
+ /// Message parameter to listen for, or blank for any parameter with the message.
+ public static void AddListener(IMessageHandler listener, StringField message, StringField parameter)
+ {
+ AddListener(listener, StringField.GetStringValue(message), StringField.GetStringValue(parameter));
+ }
+
+ ///
+ /// Adds a listener.
+ ///
+ /// Listener.
+ /// Message to listen for.
+ /// Message parameter to listen for, or blank for any parameter with the message.
+ public static void AddListener(IMessageHandler listener, StringField message, string parameter)
+ {
+ AddListener(listener, StringField.GetStringValue(message), parameter);
+ }
+
+ ///
+ /// Adds a listener.
+ ///
+ /// Listener.
+ /// Message to listen for.
+ /// Message parameter to listen for, or blank for any parameter with the message.
+ public static void AddListener(IMessageHandler listener, string message, StringField parameter)
+ {
+ AddListener(listener, message, StringField.GetStringValue(parameter));
+ }
+
+ ///
+ /// Removes a listener from listening to a specific message and parameter.
+ ///
+ /// Listener.
+ /// Message to no longer listen for, or blank for all messages.
+ /// Message parameter, or blank for all parameters.
+ public static void RemoveListener(IMessageHandler listener, string message, string parameter)
+ {
+ if (debug) Debug.Log("MessageSystem.RemoveListener(listener=" + listener + ": " + message + "," + parameter + ")");
+ if (listenerInfo.Count <= 0) return;
+ for (int i = listenerInfo.Count - 1; i >= 0; i--)
+ {
+ var x = listenerInfo[i];
+ if (x.listener == listener &&
+ (string.Equals(x.message, message) || string.IsNullOrEmpty(message)) &&
+ (string.Equals(x.parameter, parameter) || string.IsNullOrEmpty(parameter)))
+ {
+ x.removed = true;
+ if (sendMessageDepth == 0)
+ {
+ listenerInfo.RemoveAt(i);
+ x.Clear();
+ listenerInfoPool.Release(x);
+ }
+ }
+ }
+ }
+
+ private static void RemoveMarkedListenerInfo()
+ {
+ var listenersToRemove = listenerInfo.FindAll(x => x.removed);
+ listenerInfo.RemoveAll(x => x.removed);
+ for (int i = 0; i < listenersToRemove.Count; i++)
+ {
+ var listenerToRemove = listenersToRemove[i];
+ listenerToRemove.Clear();
+ listenerInfoPool.Release(listenerToRemove);
+ }
+ }
+
+ ///
+ /// Removes a listener from listening to a specific message and parameter.
+ ///
+ /// Listener.
+ /// Message to no longer listen for.
+ /// Messaeg parameter, or blank for all parameters.
+ public static void RemoveListener(IMessageHandler listener, StringField message, StringField parameter)
+ {
+ RemoveListener(listener, StringField.GetStringValue(message), StringField.GetStringValue(parameter));
+ }
+
+ ///
+ /// Removes a listener from listening to a specific message and parameter.
+ ///
+ /// Listener.
+ /// Message to no longer listen for.
+ /// Messaeg parameter, or blank for all parameters.
+ public static void RemoveListener(IMessageHandler listener, StringField message, string parameter)
+ {
+ RemoveListener(listener, StringField.GetStringValue(message), parameter);
+ }
+
+ ///
+ /// Removes a listener from listening to a specific message and parameter.
+ ///
+ /// Listener.
+ /// Message to no longer listen for.
+ /// Messaeg parameter, or blank for all parameters.
+ public static void RemoveListener(IMessageHandler listener, string message, StringField parameter)
+ {
+ RemoveListener(listener, message, StringField.GetStringValue(parameter));
+ }
+
+ ///
+ /// Removes a listener from listening to all messages.
+ ///
+ public static void RemoveListener(IMessageHandler listener)
+ {
+ RemoveListener(listener, string.Empty, string.Empty);
+ }
+
+ ///
+ /// Log a debug message when this object sends a message.
+ ///
+ public static void LogWhenSendingMessages(GameObject sender)
+ {
+ if (sender == null) return;
+ s_sendersToLog.Add(sender);
+ }
+
+ ///
+ /// Stop logging debug messages when this object sends a message.
+ ///
+ public static void StopLoggingWhenSendingMessages(GameObject sender)
+ {
+ if (sender == null) return;
+ s_sendersToLog.Remove(sender);
+ }
+
+ ///
+ /// Log a debug message when this listener receives a message.
+ ///
+ public static void LogWhenReceivingMessages(GameObject listener)
+ {
+ if (listener == null) return;
+ s_listenersToLog.Add(listener);
+ }
+
+ ///
+ /// Stop logging debug messages when this listener receives a message.
+ ///
+ public static void StopLoggingWhenReceivingMessages(GameObject listener)
+ {
+ if (listener == null) return;
+ s_listenersToLog.Add(listener);
+ }
+
+ private static bool ShouldLogSender(object sender)
+ {
+ if (sender is UnityEngine.Object && (sender as UnityEngine.Object) == null) return false;
+ return (sender is GameObject && s_sendersToLog.Contains(sender as GameObject)) ||
+ (sender is Component && s_sendersToLog.Contains((sender as Component).gameObject));
+ }
+
+ private static bool ShouldLogReceiver(IMessageHandler receiver)
+ {
+ return (receiver is Component && (receiver as Component) != null && s_listenersToLog.Contains((receiver as Component).gameObject));
+ }
+
+ ///
+ /// Sends a message to listeners.
+ ///
+ /// Object/info about object that's sending the message.
+ /// Intended recipient, or null for any.
+ /// Message.
+ /// Message parameter.
+ /// Any number of additional values to send with message.
+ public static void SendMessageWithTarget(object sender, object target, string message, string parameter, params object[] values)
+ {
+ if (!(Application.isPlaying || sendInEditMode)) return;
+ if (debug || ShouldLogSender(sender)) Debug.Log("MessageSystem.SendMessage(sender=" + sender +
+ ((target == null) ? string.Empty : (" target=" + target)) +
+ ": " + message + "," + parameter + ")");
+ var messageArgs = new MessageArgs(sender, target, message, parameter, values); // struct passed on stack; no heap allocated.
+ try
+ {
+ sendMessageDepth++;
+ for (int i = 0; i < listenerInfo.Count; i++)
+ {
+ var x = listenerInfo[i];
+ if (x == null || x.removed) continue;
+ if (x.listener == null)
+ {
+ x.removed = true;
+ continue;
+ }
+ if (!allowReceiveSameFrameAdded && x.frameAdded == Time.frameCount) continue;
+ if (string.Equals(x.message, message) && (string.Equals(x.parameter, parameter) || string.IsNullOrEmpty(x.parameter)))
+ {
+ if (allowExceptions)
+ {
+ SendMessageToListener(x, sender, target, message, parameter, messageArgs);
+ }
+ else
+ {
+ try
+ {
+ SendMessageToListener(x, sender, target, message, parameter, messageArgs);
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogError("Message System exception sending '" + message + "'/'" + parameter + "' to " + x.listener + ": " + e.Message);
+ }
+ }
+ }
+ }
+ }
+ finally
+ {
+ sendMessageDepth--;
+ if (sendMessageDepth == 0) RemoveMarkedListenerInfo();
+ }
+ }
+
+ private static void SendMessageToListener(ListenerInfo x, object sender, object target, string message, string parameter, MessageArgs messageArgs)
+ {
+ if (ShouldLogReceiver(x.listener))
+ {
+ Debug.Log("MessageSystem.SendMessage(sender=" + sender +
+ ((target == null) ? string.Empty : (" target=" + target)) +
+ ": " + message + "," + parameter + ")");
+ }
+ x.listener.OnMessage(messageArgs);
+ }
+
+ ///
+ /// Sends a message to listeners.
+ ///
+ /// Object/info about object that's sending the message.
+ /// Intended recipient, or null for any.
+ /// Message.
+ /// Message parameter.
+ /// Any number of additional values to send with message.
+ public static void SendMessageWithTarget(object sender, object target, StringField message, string parameter, params object[] values)
+ {
+ SendMessageWithTarget(sender, target, StringField.GetStringValue(message), parameter, values);
+ }
+
+ ///
+ /// Sends a message to listeners.
+ ///
+ /// Object/info about object that's sending the message.
+ /// Intended recipient, or null for any.
+ /// Message.
+ /// Message parameter.
+ /// Any number of additional values to send with message.
+ public static void SendMessageWithTarget(object sender, object target, StringField message, StringField parameter, params object[] values)
+ {
+ SendMessageWithTarget(sender, target, StringField.GetStringValue(message), StringField.GetStringValue(parameter), values);
+ }
+
+ ///
+ /// Sends a message to listeners.
+ ///
+ /// Object/info about object that's sending the message.
+ /// Intended recipient, or null for any.
+ /// Message.
+ /// Message parameter.
+ /// Any number of additional values to send with message.
+ public static void SendMessageWithTarget(object sender, object target, string message, StringField parameter, params object[] values)
+ {
+ SendMessageWithTarget(sender, target, message, StringField.GetStringValue(parameter), values);
+ }
+
+ ///
+ /// Sends a message to listeners.
+ ///
+ /// Object/info about object that's sending the message.
+ /// Message.
+ /// Message parameter.
+ /// Any number of additional values to send with message.
+ public static void SendMessage(object sender, string message, string parameter, params object[] values)
+ {
+ SendMessageWithTarget(sender, null, message, parameter, values);
+ }
+
+ ///
+ /// Sends a message to listeners.
+ ///
+ /// Object/info about object that's sending the message.
+ /// Message.
+ /// Message parameter.
+ /// Any number of additional values to send with message.
+ public static void SendMessage(object sender, StringField message, StringField parameter, params object[] values)
+ {
+ SendMessageWithTarget(sender, null, StringField.GetStringValue(message), StringField.GetStringValue(parameter), values);
+ }
+
+ ///
+ /// Sends a message to listeners.
+ ///
+ /// Object/info about object that's sending the message.
+ /// Message.
+ /// Message parameter.
+ /// Any number of additional values to send with message.
+ public static void SendMessage(object sender, StringField message, string parameter, params object[] values)
+ {
+ SendMessageWithTarget(sender, null, StringField.GetStringValue(message), parameter, values);
+ }
+
+ ///
+ /// Sends a message to listeners.
+ ///
+ /// Object/info about object that's sending the message.
+ /// Message.
+ /// Message parameter.
+ /// Any number of additional values to send with message.
+ public static void SendMessage(object sender, string message, StringField parameter, params object[] values)
+ {
+ SendMessageWithTarget(sender, null, message, StringField.GetStringValue(parameter), values);
+ }
+
+ ///
+ /// Sends a message. If the message contains a colon (:), the part after the
+ /// colon is sent as the parameter. If it contains a second colon, the part
+ /// after the second colon is sent as a value.
+ ///
+ public static void SendCompositeMessage(object sender, string message)
+ {
+ if (string.IsNullOrEmpty(message)) return;
+ var parameter = string.Empty;
+ object value = null;
+ if (message.Contains(":")) // Parameter?
+ {
+ var colonPos = message.IndexOf(':');
+ parameter = message.Substring(colonPos + 1);
+ message = message.Substring(0, colonPos);
+
+ if (parameter.Contains(":")) // Value?
+ {
+ colonPos = parameter.IndexOf(':');
+ var valueString = parameter.Substring(colonPos + 1);
+ parameter = parameter.Substring(0, colonPos);
+ int valueInt;
+ bool isNumeric = int.TryParse(valueString, out valueInt);
+ if (isNumeric) value = valueInt; else value = valueString;
+ }
+ }
+ if (value == null)
+ {
+ SendMessage(sender, message, parameter);
+ }
+ else
+ {
+ SendMessage(sender, message, parameter, value);
+ }
+ }
+
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystem.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystem.cs.meta
new file mode 100644
index 000000000..ca158e360
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystem.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 41925bd09522d5542a5ec18437c20726
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystem.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystemLogger.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystemLogger.cs
new file mode 100644
index 000000000..e7e9a98e6
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystemLogger.cs
@@ -0,0 +1,49 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ [AddComponentMenu("")] // Use wrapper.
+ public class MessageSystemLogger : MonoBehaviour
+ {
+
+ [Tooltip("Log a message when this GameObject sends a message to the Message System.")]
+ public bool logWhenSendingMessages;
+
+ [Tooltip("Log a message when this GameObject receives a message from the Message System.")]
+ public bool logWhenReceivingMessages;
+
+ private void OnEnable()
+ {
+ if (logWhenSendingMessages)
+ {
+ MessageSystem.LogWhenSendingMessages(gameObject);
+ }
+ if (logWhenReceivingMessages)
+ {
+ if (GetComponent(typeof(IMessageHandler)) == null)
+ {
+ if (Debug.isDebugBuild) Debug.LogWarning("MessageSystem: " + name + " doesn't have any IMessageHandler components. Can't log when receiving messages.", this);
+ }
+ else
+ {
+ MessageSystem.LogWhenReceivingMessages(gameObject);
+ }
+ }
+ }
+
+ private void OnDisable()
+ {
+ if (logWhenSendingMessages)
+ {
+ MessageSystem.StopLoggingWhenSendingMessages(gameObject);
+ }
+ if (logWhenReceivingMessages)
+ {
+ MessageSystem.StopLoggingWhenReceivingMessages(gameObject);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystemLogger.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystemLogger.cs.meta
new file mode 100644
index 000000000..9c5715655
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystemLogger.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: c07557b3afdd241448890f047ab03f0b
+timeCreated: 1544971617
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Message System/MessageSystemLogger.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc.meta
new file mode 100644
index 000000000..9d51c159b
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 38a5ea82a86d19f4895e7a7bba48a6f2
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/AlwaysFaceCamera.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/AlwaysFaceCamera.cs
new file mode 100644
index 000000000..592b38755
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/AlwaysFaceCamera.cs
@@ -0,0 +1,71 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Always keeps the GameObject facing the main camera.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class AlwaysFaceCamera : MonoBehaviour
+ {
+
+ [Tooltip("Leave Y rotation untouched.")]
+ [SerializeField]
+ private bool m_yAxisOnly = false;
+
+ [Tooltip("Flip 180 degrees.")]
+ [SerializeField]
+ private bool m_rotate180 = false;
+
+ ///
+ /// Set `true` to leave Y rotation untouched.
+ ///
+ public bool yAxisOnly
+ {
+ get { return m_yAxisOnly; }
+ set { m_yAxisOnly = value; }
+ }
+
+ public bool rotate180
+ {
+ get { return m_rotate180; }
+ set { m_rotate180 = value; }
+ }
+
+ private Camera m_mainCamera = null;
+
+ private void Update()
+ {
+ if (m_mainCamera == null || !m_mainCamera.enabled || !m_mainCamera.gameObject.activeInHierarchy) m_mainCamera = Camera.main;
+ if (m_mainCamera == null || !m_mainCamera.enabled || !m_mainCamera.gameObject.activeInHierarchy) return;
+ if (rotate180)
+ {
+ if (yAxisOnly)
+ {
+ transform.rotation = Quaternion.Euler(transform.rotation.eulerAngles.x, (m_mainCamera.transform.rotation.eulerAngles + 180f * Vector3.up).y, transform.rotation.eulerAngles.z);
+ }
+ else
+ {
+ transform.rotation = Quaternion.LookRotation(-m_mainCamera.transform.forward, m_mainCamera.transform.up);
+ }
+ }
+ else
+ {
+ if (yAxisOnly)
+ {
+ transform.rotation = Quaternion.Euler(transform.rotation.eulerAngles.x, m_mainCamera.transform.rotation.eulerAngles.y, transform.rotation.eulerAngles.z);
+ }
+ else
+ {
+ transform.rotation = m_mainCamera.transform.rotation;
+ }
+ }
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/AlwaysFaceCamera.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/AlwaysFaceCamera.cs.meta
new file mode 100644
index 000000000..b96c8c731
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/AlwaysFaceCamera.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: c08da6b2e5fe8ff4f9bff1ebb3401617
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/AlwaysFaceCamera.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CheckPhysics2D.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CheckPhysics2D.cs
new file mode 100644
index 000000000..02e91c3be
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CheckPhysics2D.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEngine.Events;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Checks if the scripting symbol USE_PHYSICS2D is defined.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class CheckPhysics2D : MonoBehaviour
+ {
+
+ public UnityEvent usePhysics2DDefined = new UnityEvent();
+ public UnityEvent usePhysics2DUndefined = new UnityEvent();
+
+ private void Start()
+ {
+#if USE_PHYSICS2D || !UNITY_2018_1_OR_NEWER
+ usePhysics2DDefined.Invoke();
+#else
+ usePhysics2DUndefined.Invoke();
+#endif
+ }
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CheckPhysics2D.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CheckPhysics2D.cs.meta
new file mode 100644
index 000000000..1f345047a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CheckPhysics2D.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 8abc57a7f35628247adce21cd6203245
+timeCreated: 1538407277
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CheckPhysics2D.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ComponentUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ComponentUtility.cs
new file mode 100644
index 000000000..ca8cbdf1b
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ComponentUtility.cs
@@ -0,0 +1,44 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Utility functions for working with components.
+ ///
+ public static class ComponentUtility
+ {
+
+ ///
+ /// Returns true if the component is enabled; false otherwise.
+ ///
+ public static bool IsComponentEnabled(Component component)
+ {
+ if (component is Behaviour) return (component as Behaviour).enabled;
+ if (component is Renderer) return (component as Renderer).enabled;
+ if (component is Collider) return (component as Collider).enabled;
+ if (component is Animation) return (component as Animation).enabled;
+ if (component is Animator) return (component as Animator).enabled;
+ if (component is AudioSource) return (component as AudioSource).enabled;
+ return false;
+ }
+
+ ///
+ /// Sets a component's enabled state.
+ ///
+ public static void SetComponentEnabled(Component component, bool value)
+ {
+ if (component == null) return;
+ if (component is Behaviour) (component as Behaviour).enabled = value;
+ if (component is Renderer) (component as Renderer).enabled = value;
+ if (component is Collider) (component as Collider).enabled = value;
+ if (component is Animation) (component as Animation).enabled = value;
+ if (component is Animator) (component as Animator).enabled = value;
+ if (component is AudioSource) (component as AudioSource).enabled = value;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ComponentUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ComponentUtility.cs.meta
new file mode 100644
index 000000000..ec9f3014f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ComponentUtility.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 48cf243a809a04c4e8b4c4884de4fc22
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ComponentUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CoroutineUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CoroutineUtility.cs
new file mode 100644
index 000000000..437a3fcdf
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CoroutineUtility.cs
@@ -0,0 +1,37 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Utility functions for working with coroutines.
+ /// Provides a static endOfFrame that is set to aWaitForEndOfFrame object under
+ /// normal operation but is set to null if BATCH_MODE is defined, to allow for
+ /// batch mode testing in which WaitForEndOfFrame isn't supported.
+ ///
+ public static class CoroutineUtility
+ {
+
+#if BATCH_MODE
+ public static WaitForEndOfFrame endOfFrame = null;
+#else
+ public static WaitForEndOfFrame endOfFrame = new WaitForEndOfFrame();
+#endif
+
+//--- Some compilers have issue with this, and it's not really needed anyway.
+//#if UNITY_2019_3_OR_NEWER && UNITY_EDITOR
+// [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
+// static void InitStaticVariables()
+// {
+//#if BATCH_MODE
+// endOfFrame = null;
+//#else
+// endOfFrame = new WaitForEndOfFrame();
+//#endif
+// }
+//#endif
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CoroutineUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CoroutineUtility.cs.meta
new file mode 100644
index 000000000..83105083e
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CoroutineUtility.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: 0108513946cfc9a49b5398f783c2bc17
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CoroutineUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CursorControl.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CursorControl.cs
new file mode 100644
index 000000000..b31591a3d
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CursorControl.cs
@@ -0,0 +1,79 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Methods to hide and show the cursor.
+ ///
+ public static class CursorControl
+ {
+
+ public static CursorLockMode cursorLockMode { get; set; } = CursorLockMode.Locked;
+
+ public static bool isCursorActive
+ {
+ get { return isCursorVisible && !isCursorLocked; }
+ }
+
+ public static void SetCursorActive(bool value)
+ {
+ ShowCursor(value);
+ LockCursor(!value);
+ }
+
+#if UNITY_4_3 || UNITY_4_5 || UNITY_4_6
+
+ public static bool isCursorVisible
+ {
+ get { return Screen.showCursor; }
+ }
+
+ public static bool isCursorLocked
+ {
+ get { return Screen.lockCursor; }
+ }
+
+ public static void ShowCursor(bool value)
+ {
+ Screen.showCursor = value;
+ }
+
+ public static void LockCursor(bool value)
+ {
+ Screen.lockCursor = value;
+ }
+
+#else
+
+ public static bool isCursorVisible
+ {
+ get { return Cursor.visible; }
+ }
+
+ public static bool isCursorLocked
+ {
+ get { return Cursor.lockState != CursorLockMode.None; }
+ }
+
+ public static void ShowCursor(bool value)
+ {
+ Cursor.visible = value;
+ }
+
+ public static void LockCursor(bool value)
+ {
+ if (value == false && isCursorLocked)
+ {
+ cursorLockMode = Cursor.lockState;
+ }
+ Cursor.lockState = value ? cursorLockMode : CursorLockMode.None;
+ }
+
+#endif
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CursorControl.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CursorControl.cs.meta
new file mode 100644
index 000000000..d2e10a544
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CursorControl.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 5cdc68121f6b1ed419ca162049482704
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/CursorControl.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DictionaryExtensions.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DictionaryExtensions.cs
new file mode 100644
index 000000000..f12324cd3
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DictionaryExtensions.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Extension methods for generic dictionaries.
+ ///
+ public static class DictionaryExtensions
+ {
+
+ ///
+ /// Works like List.RemoveAll.
+ ///
+ /// Key type
+ /// Value type
+ /// Dictionary to remove entries from
+ /// Delegate to match keys
+ /// Number of entries removed
+ public static int RemoveAll(this IDictionary dictionary, Predicate match)
+ {
+ if (dictionary == null || match == null) return 0;
+ var keysToRemove = dictionary.Keys.Where(k => match(k)).ToList();
+ if (keysToRemove.Count > 0)
+ {
+ foreach (var key in keysToRemove)
+ {
+ dictionary.Remove(key);
+ }
+ }
+ return keysToRemove.Count;
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DictionaryExtensions.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DictionaryExtensions.cs.meta
new file mode 100644
index 000000000..de11c63ad
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DictionaryExtensions.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 24971f7e81b4d314889af501a1a5a66c
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DictionaryExtensions.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Dimension.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Dimension.cs
new file mode 100644
index 000000000..d15fb600a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Dimension.cs
@@ -0,0 +1,24 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// This enum is used to specify 2D or 3D.
+ ///
+ public enum Dimension
+ {
+ ///
+ /// The context is 2D (e.g., sprites, 2D Physics, etc.).
+ ///
+ Is2D,
+
+ ///
+ /// The context is 3D (e.g., models, 3D Physics, etc.).
+ ///
+ Is3D
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Dimension.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Dimension.cs.meta
new file mode 100644
index 000000000..149fad7b8
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Dimension.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 0c0e1aa702f0204439ecc32c47cecd6d
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Dimension.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DontDestroyGameObject.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DontDestroyGameObject.cs
new file mode 100644
index 000000000..bed2b8705
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DontDestroyGameObject.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Marks the GameObject as DontDestroyOnLoad.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class DontDestroyGameObject : MonoBehaviour
+ {
+
+ private void Awake()
+ {
+#if UNITY_EDITOR && UNITY_2019_3_OR_NEWER
+ if (Application.isPlaying)
+ {
+ UnityEditor.SceneVisibilityManager.instance.Show(gameObject, false);
+ }
+#endif
+ transform.SetParent(null);
+ DontDestroyOnLoad(gameObject);
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DontDestroyGameObject.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DontDestroyGameObject.cs.meta
new file mode 100644
index 000000000..d1b8dd525
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DontDestroyGameObject.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 61a8845407078a7439d71ecbed1932da
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/DontDestroyGameObject.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/EnableOnStart.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/EnableOnStart.cs
new file mode 100644
index 000000000..e87c51b91
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/EnableOnStart.cs
@@ -0,0 +1,29 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Enables a component when the scene starts.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class EnableOnStart : MonoBehaviour
+ {
+ [Tooltip("Enable this component when on start.")]
+ [SerializeField]
+ private Component m_component;
+
+ public Component component
+ {
+ get { return m_component; }
+ set { m_component = value; }
+ }
+
+ private void Start()
+ {
+ ComponentUtility.SetComponentEnabled(m_component, true);
+ }
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/EnableOnStart.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/EnableOnStart.cs.meta
new file mode 100644
index 000000000..3f61b847b
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/EnableOnStart.cs.meta
@@ -0,0 +1,20 @@
+fileFormatVersion: 2
+guid: 65e9d7325c2897a43b4518ea8823e0d8
+timeCreated: 1594219244
+licenseType: Store
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/EnableOnStart.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameObjectUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameObjectUtility.cs
new file mode 100644
index 000000000..1d0cd268a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameObjectUtility.cs
@@ -0,0 +1,308 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Utility functions for working with GameObjects.
+ ///
+ public static class GameObjectUtility
+ {
+
+ // These methods handle API differences:
+
+ public static T FindFirstObjectByType() where T : UnityEngine.Object
+ {
+#if UNITY_2023_1_OR_NEWER
+ return UnityEngine.Object.FindFirstObjectByType();
+#else
+ return UnityEngine.Object.FindObjectOfType();
+#endif
+ }
+
+ public static UnityEngine.Object FindFirstObjectByType(System.Type type)
+ {
+#if UNITY_2023_1_OR_NEWER
+ return UnityEngine.Object.FindFirstObjectByType(type);
+#else
+ return UnityEngine.Object.FindObjectOfType(type);
+#endif
+ }
+
+ public static T[] FindObjectsByType() where T : UnityEngine.Object
+ {
+#if UNITY_2023_1_OR_NEWER
+ return UnityEngine.Object.FindObjectsByType(FindObjectsSortMode.None);
+#else
+ return UnityEngine.Object.FindObjectsOfType();
+#endif
+ }
+
+ public static UnityEngine.Object[] FindObjectsByType(System.Type type)
+ {
+#if UNITY_2023_1_OR_NEWER
+ return UnityEngine.Object.FindObjectsByType(type, FindObjectsSortMode.None);
+#else
+ return UnityEngine.Object.FindObjectsOfType(type);
+#endif
+ }
+
+ ///
+ /// Determines if a GameObject reference is a non-instantiated prefab or a scene object.
+ /// If `go` is `null`, active in the scene, or its parent is active in the scene, it's
+ /// considered a scene object. Otherwise this method searches all scene objects for
+ /// matches. If it doesn't find any matches, this is a prefab.
+ ///
+ /// true if a prefab; otherwise, false.
+ /// GameObject.
+ public static bool IsPrefab(GameObject go)
+ {
+ if (go == null) return false;
+ if (go.activeInHierarchy) return false;
+ if ((go.transform.parent != null) && go.transform.parent.gameObject.activeSelf) return false;
+ var list = FindObjectsByType();
+ for (int i = 0; i < list.Length; i++)
+ {
+ if (list[i] == go) return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Returns true if the end of a transform's hierarchy path matches a specified path.
+ /// For example, if the transform's hierarchy is A/B/C/D, it will match a
+ /// requiredPath of C/D.
+ ///
+ public static bool DoesGameObjectPathMatch(Transform t, string requiredPath)
+ {
+ if (t == null) return false;
+ if (t.name.Contains("/"))
+ {
+ var fullPath = GetHierarchyPath(t);
+ return fullPath.EndsWith(requiredPath);
+ }
+ else
+ {
+ return string.Equals(t.name, requiredPath);
+ }
+ }
+
+ ///
+ /// Returns the full hierarchy path
+ ///
+ ///
+ ///
+ public static string GetHierarchyPath(Transform t)
+ {
+ if (t == null) return string.Empty;
+ if (t.parent == null) return t.name;
+ return GetHierarchyPath(t.parent) + "/" + t.name;
+ }
+
+ ///
+ /// Finds an in-scene GameObject by name even if it's inactive.
+ ///
+ /// Name of the GameObject.
+ /// If true, check all open scenes; otherwise only check active scene.
+ /// The GameObject.
+ public static GameObject GameObjectHardFind(string goName, bool checkAllScenes = true)
+ {
+ if (string.IsNullOrEmpty(goName)) return null;
+
+ // Try the normal method to find an active GameObject first:
+ GameObject result = GameObject.Find(goName);
+ if (result != null) return result;
+
+ // Otherwise check all GameObjects, active and inactive:
+ if (checkAllScenes)
+ {
+ for (int i = 0; i < UnityEngine.SceneManagement.SceneManager.sceneCount; i++)
+ {
+ var rootGameObjects = UnityEngine.SceneManagement.SceneManager.GetSceneAt(i).GetRootGameObjects();
+ result = GameObjectHardFindRootObjects(goName, string.Empty, rootGameObjects);
+ if (result != null) return result;
+ }
+ return null;
+ }
+ else
+ {
+ var activeSceneRootGameObjects = UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects();
+ return GameObjectHardFindRootObjects(goName, string.Empty, activeSceneRootGameObjects);
+ }
+ }
+
+ ///
+ /// Finds an in-scene GameObject by name and tag even if it's inactive.
+ ///
+ /// GameObject name to search for.
+ /// Tag to search for.
+ /// If true, check all open scenes; otherwise only check active scene.
+ ///
+ public static GameObject GameObjectHardFind(string goName, string tag, bool checkAllScenes = true)
+ {
+ // Try the normal method to find active GameObjects with tag first:
+ GameObject result = null;
+ var gameObjects = GameObject.FindGameObjectsWithTag(tag);
+ foreach (var go in gameObjects)
+ {
+ if (string.Equals(go.name, goName)) return go;
+ }
+
+ // Otherwise check all GameObjects, active and inactive:
+ if (checkAllScenes)
+ {
+ var allRootGOs = new List();
+ var allTransforms = Resources.FindObjectsOfTypeAll();
+ foreach (var t in allTransforms)
+ {
+ var root = t.root;
+ if (root.hideFlags == HideFlags.None)
+ {
+ allRootGOs.Add(t.gameObject);
+ }
+ }
+ result = GameObjectHardFindRootObjects(goName, tag, allRootGOs.ToArray());
+ if (result != null) return result;
+ return null;
+ }
+ else
+ {
+ var activeSceneRootGameObjects = UnityEngine.SceneManagement.SceneManager.GetActiveScene().GetRootGameObjects();
+ return GameObjectHardFindRootObjects(goName, tag, activeSceneRootGameObjects);
+ }
+ }
+
+ private static GameObject GameObjectHardFindRootObjects(string goName, string tag, GameObject[] rootGameObjects)
+ {
+ if (rootGameObjects == null) return null;
+ for (int i = 0; i < rootGameObjects.Length; i++)
+ {
+ var result = GameObjectSearchHierarchy(rootGameObjects[i].transform, goName, tag);
+ if (result != null) return result;
+ }
+ return null;
+ }
+
+ private static GameObject GameObjectSearchHierarchy(Transform t, string goName, string tag)
+ {
+ if (t == null) return null;
+ if (string.Equals(t.name, goName) && (string.IsNullOrEmpty(tag) || string.Equals(t.tag, tag))) return t.gameObject;
+ foreach (Transform child in t)
+ {
+ var result = GameObjectSearchHierarchy(child, goName, tag);
+ if (result != null) return result;
+ }
+ return null;
+ }
+
+ ///
+ /// Finds all objects of a type, including on inactive GameObjects.
+ /// If true, check all open scenes; otherwise only check active scene.
+ ///
+ public static T[] FindObjectsOfTypeAlsoInactive(bool checkAllScenes = true) where T : Component
+ {
+#if UNITY_2022_3_OR_NEWER || UNITY_2023_1_OR_NEWER
+ return UnityEngine.Object.FindObjectsByType(FindObjectsInactive.Include, FindObjectsSortMode.None);
+#else
+ var list = new List();
+
+ var allT = Resources.FindObjectsOfTypeAll();
+ foreach (var t in allT)
+ {
+ var root = t.transform.root;
+ if (root.hideFlags == HideFlags.None)
+ {
+ list.Add(t);
+ }
+ }
+ return list.ToArray();
+#endif
+ }
+
+ private static void FindObjectsSearchRootObjects(GameObject[] rootGameObjects, System.Collections.Generic.List list) where T : Component
+ {
+ if (rootGameObjects == null) return;
+ for (int i = 0; i < rootGameObjects.Length; i++)
+ {
+ FindObjectsSearchHierarchy(rootGameObjects[i].transform, list);
+ }
+ }
+
+ private static void FindObjectsSearchHierarchy(Transform t, System.Collections.Generic.List list) where T : Component
+ {
+ if (t == null) return;
+ var components = t.GetComponents();
+ if (components.Length > 0) list.AddRange(components);
+ foreach (Transform child in t)
+ {
+ FindObjectsSearchHierarchy(child, list);
+ }
+ }
+
+ ///
+ /// Like GetComponentInChildren(), but also searches parents.
+ ///
+ /// The component, or null if not found.
+ /// Game object to search.
+ /// The component type.
+ public static T GetComponentAnywhere(GameObject gameObject) where T : Component
+ {
+ if (!gameObject) return null;
+ T component = gameObject.GetComponentInChildren();
+ if (component) return component;
+ Transform ancestor = gameObject.transform.parent;
+ int safeguard = 0;
+ while (!component && ancestor && safeguard < 256)
+ {
+ component = ancestor.GetComponentInChildren();
+ ancestor = ancestor.parent;
+ }
+ return component;
+ }
+
+ ///
+ /// Gets the height of the game object based on its collider. This only works if the
+ /// game object has a CharacterController, CapsuleCollider, BoxCollider, or SphereCollider.
+ ///
+ /// The game object height if it has a recognized type of collider; otherwise 0.
+ /// Game object.
+ public static float GetGameObjectHeight(GameObject gameObject)
+ {
+ CharacterController controller = gameObject.GetComponent();
+ if (controller != null)
+ {
+ return controller.height;
+ }
+ else
+ {
+ CapsuleCollider capsuleCollider = gameObject.GetComponent();
+ if (capsuleCollider != null)
+ {
+ return capsuleCollider.height;
+ }
+ else
+ {
+ BoxCollider boxCollider = gameObject.GetComponent();
+ if (boxCollider != null)
+ {
+ return boxCollider.center.y + boxCollider.size.y;
+ }
+ else
+ {
+ SphereCollider sphereCollider = gameObject.GetComponent();
+ if (sphereCollider != null)
+ {
+ return sphereCollider.center.y + sphereCollider.radius;
+ }
+ }
+ }
+ }
+ return 0;
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameObjectUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameObjectUtility.cs.meta
new file mode 100644
index 000000000..c5f27348e
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameObjectUtility.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: c85f45f7d87ba1545818659c955448a7
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameObjectUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameTime.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameTime.cs
new file mode 100644
index 000000000..24989be7a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameTime.cs
@@ -0,0 +1,137 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ public enum GameTimeMode
+ {
+ ///
+ /// Direct mapping to Unity's Time class (e.g., Time.time).
+ ///
+ UnityStandard,
+
+ ///
+ /// Realtime, ignoring Time.timeScale. Never pauses.
+ ///
+ Realtime,
+
+ ///
+ /// Manually-controlled time. You must set GameTime.time and GameTime.deltaTime.
+ ///
+ Manual
+ }
+
+ ///
+ /// This is a wrapper around Unity's Time class that allows you to specify a mode:
+ /// UnityStandard (Time.time), Realtime (Time.realtimeSinceStartup), or Manual
+ /// (you set the time values each frame).
+ ///
+ public static class GameTime
+ {
+
+ private static GameTimeMode s_mode = GameTimeMode.UnityStandard;
+ private static float s_manualTime = 0;
+ private static float s_manualDeltaTime = 0;
+ private static bool s_manualPaused = false;
+
+#if UNITY_2019_3_OR_NEWER && UNITY_EDITOR
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
+ static void InitStaticVariables()
+ {
+ s_mode = GameTimeMode.UnityStandard;
+ s_manualTime = 0;
+ s_manualDeltaTime = 0;
+ s_manualPaused = false;
+ }
+#endif
+
+ public static GameTimeMode mode
+ {
+ get { return s_mode; }
+ set { s_mode = value; }
+ }
+
+ public static float time
+ {
+ get
+ {
+ switch (mode)
+ {
+ default:
+ case GameTimeMode.UnityStandard:
+ return Time.time;
+ case GameTimeMode.Realtime:
+ return Time.realtimeSinceStartup;
+ case GameTimeMode.Manual:
+ return s_manualTime;
+ }
+ }
+ set
+ {
+ s_manualTime = value;
+ }
+ }
+
+ public static float deltaTime
+ {
+ get
+ {
+ switch (mode)
+ {
+ default:
+ case GameTimeMode.UnityStandard:
+ return Time.deltaTime;
+ case GameTimeMode.Realtime:
+ return Time.unscaledDeltaTime;
+ case GameTimeMode.Manual:
+ return s_manualDeltaTime;
+ }
+ }
+ set
+ {
+ s_manualDeltaTime = value;
+ }
+ }
+
+ public static float timeScale
+ {
+ get { return Time.timeScale; }
+ }
+
+ public static bool isPaused
+ {
+ get
+ {
+ switch (mode)
+ {
+ default:
+ case GameTimeMode.UnityStandard:
+ return Mathf.Approximately(0, Time.timeScale);
+ case GameTimeMode.Realtime:
+ return false;
+ case GameTimeMode.Manual:
+ return s_manualPaused;
+ }
+ }
+ set
+ {
+ switch (mode)
+ {
+ default:
+ case GameTimeMode.UnityStandard:
+ Time.timeScale = value ? 0 : 1;
+ break;
+ case GameTimeMode.Realtime:
+ break;
+ case GameTimeMode.Manual:
+ s_manualPaused = value;
+ break;
+ }
+ }
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameTime.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameTime.cs.meta
new file mode 100644
index 000000000..cf6823443
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameTime.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 1bc83e69aa4a98c42b20e32f30b6d3bd
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/GameTime.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/HelpBoxAttribute.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/HelpBoxAttribute.cs
new file mode 100644
index 000000000..662677d64
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/HelpBoxAttribute.cs
@@ -0,0 +1,25 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ public enum HelpBoxMessageType { None, Info, Warning, Error }
+
+ ///
+ /// Attribute to draw a help box.
+ ///
+ public class HelpBoxAttribute : PropertyAttribute
+ {
+
+ public string text;
+ public HelpBoxMessageType messageType;
+
+ public HelpBoxAttribute(string text, HelpBoxMessageType messageType = HelpBoxMessageType.None)
+ {
+ this.text = text;
+ this.messageType = messageType;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/HelpBoxAttribute.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/HelpBoxAttribute.cs.meta
new file mode 100644
index 000000000..fa6729b5e
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/HelpBoxAttribute.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 94b687356c93bb24195ea5544151657a
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/HelpBoxAttribute.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/InstantiatePrefabs.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/InstantiatePrefabs.cs
new file mode 100644
index 000000000..1f381e164
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/InstantiatePrefabs.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Instantiates prefabs on Awake.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class InstantiatePrefabs : MonoBehaviour
+ {
+
+ [Tooltip("Make instances children of this parent. If unassigned, use this GameObject.")]
+ [SerializeField]
+ private Transform m_parent;
+
+ [Tooltip("Prefabs to instantiate.")]
+ [SerializeField]
+ private GameObject[] m_prefabs = new GameObject[0];
+
+ public enum Position { ScreenSpaceUI, OriginalPosition, ParentPosition }
+
+ [Tooltip("Untick for screen-space GameObjects such as UI elements; tick for world-space GameObjects.")]
+ [SerializeField]
+ private Position m_position = Position.ScreenSpaceUI;
+
+ private void OnEnable()
+ {
+ if (m_parent == null) m_parent = this.transform;
+ for (int i = 0; i < m_prefabs.Length; i++)
+ {
+ var prefab = m_prefabs[i];
+ if (prefab != null)
+ {
+ var instance = (m_position == Position.ParentPosition)
+ ? Instantiate(prefab, m_parent.position, m_parent.rotation) as GameObject
+ : Instantiate(prefab) as GameObject;
+ if (instance == null)
+ {
+ Debug.LogWarning("Instantiate Prefabs was unable to instantiate " + prefab, this);
+ }
+ else
+ {
+ instance.transform.SetParent(m_parent, (m_position != Position.ScreenSpaceUI));
+ instance.name = prefab.name;
+ }
+ }
+ }
+ Destroy(this);
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/InstantiatePrefabs.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/InstantiatePrefabs.cs.meta
new file mode 100644
index 000000000..104caaea1
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/InstantiatePrefabs.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: bd0c236e29ff6df47b805e4f4385821f
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/InstantiatePrefabs.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/LODManager.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/LODManager.cs
new file mode 100644
index 000000000..ee09236f4
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/LODManager.cs
@@ -0,0 +1,161 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Implements a Level of Detail (LOD) system according to distance from the player.
+ /// Add this component to any GameObject with script(s) that implement a method
+ /// named `OnLOD(int)`. It's the script's responsibility to handle the message
+ /// accordingly. For example, an AI script could reduce the frequency of perception
+ /// checks as the LOD number increases.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class LODManager : MonoBehaviour
+ {
+
+ [Serializable]
+ public class LOD
+ {
+
+ [Tooltip("The minimum distance for this LOD.")]
+ [SerializeField]
+ private float m_minDistance = 0;
+
+ [Tooltip("The max distance for this LOD.")]
+ [SerializeField]
+ private float m_maxDistance = Mathf.Infinity;
+
+ ///
+ /// The minimum distance for this LOD.
+ ///
+ public float minDistance
+ {
+ get { return m_minDistance; }
+ set { m_minDistance = value; }
+ }
+
+ ///
+ /// The max distance for this LOD.
+ ///
+ public float maxDistance
+ {
+ get { return m_maxDistance; }
+ set { m_maxDistance = value; }
+ }
+
+ public bool Contains(float distance)
+ {
+ return (minDistance <= distance && distance <= maxDistance);
+ }
+
+ }
+
+ [Tooltip("The LODs (levels of detail).")]
+ [SerializeField]
+ private LOD[] m_levels;
+
+ [Tooltip("The frequency at which to check distance from the player and update the current LOD if necessary.")]
+ [SerializeField]
+ private float m_monitorFrequency = 5f;
+
+ ///
+ /// The LODs.
+ ///
+ public LOD[] levels
+ {
+ get { return m_levels; }
+ set { m_levels = value; }
+ }
+
+ ///
+ /// The frequency at which to check distance from the player and update
+ /// the current LOD if necessary.
+ ///
+ public float monitorFrequency
+ {
+ get { return m_monitorFrequency; }
+ set { m_monitorFrequency = value; }
+ }
+
+ ///
+ /// Gets or sets the player's transform. The Start method assigns the
+ /// GameObject tagged "Player" to this property.
+ ///
+ /// The player.
+ public Transform player { get; set; }
+
+ private int m_currentLevel = 0;
+ private WaitForSeconds m_currentWaitForSeconds = new WaitForSeconds(60); // Cache to reduce garbage collection.
+ private float m_currentWaitForSecondsValue = 0;
+
+ private void Start()
+ {
+ FindPlayer();
+ StartCoroutine(MonitorLOD());
+ }
+
+ ///
+ /// Assigns the GameObject tagged "Player" to the player property.
+ ///
+ public void FindPlayer()
+ {
+ var go = GameObject.FindWithTag("Player");
+ player = (go != null) ? go.transform : null;
+ }
+
+ private IEnumerator MonitorLOD()
+ {
+ yield return new WaitForSeconds(UnityEngine.Random.value); // Stagger GameObjects.
+ m_currentWaitForSecondsValue = -1;
+ while (true)
+ {
+ CheckLOD();
+ if (monitorFrequency != m_currentWaitForSecondsValue)
+ {
+ m_currentWaitForSecondsValue = monitorFrequency;
+ m_currentWaitForSeconds = new WaitForSeconds(monitorFrequency);
+ }
+ yield return m_currentWaitForSeconds;
+ }
+ }
+
+ ///
+ /// Updates the current level of detail based on distance from the player and
+ /// the component's LOD ranges.
+ ///
+ public void CheckLOD()
+ {
+ if (player == null || levels == null || levels.Length == 0) return;
+ float distance = Vector3.Distance(transform.position, player.position);
+ if (levels[m_currentLevel].Contains(distance)) return;
+ for (int level = 0; level < levels.Length; level++)
+ {
+ if (levels[level].Contains(distance))
+ {
+ m_currentLevel = level;
+ BroadcastMessage("OnLOD", level, SendMessageOptions.DontRequireReceiver);
+ return;
+ }
+ }
+ }
+
+ ///
+ /// For optional UtopiaWorx Zone Controller integration.
+ ///
+ /// The properties that Zone Controller can control.
+ public static List ZonePluginActivator()
+ {
+ List controllable = new List();
+ controllable.Add("monitorFrequency|System.Single|0|99999|1|The frequency at which to check distance from the player and update the current LOD if necessary.");
+ return controllable;
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/LODManager.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/LODManager.cs.meta
new file mode 100644
index 000000000..c79cfa6cb
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/LODManager.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: bef41bce765897e4eaf3465b000ace81
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/LODManager.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ListExtensions.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ListExtensions.cs
new file mode 100644
index 000000000..b76ee8dec
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ListExtensions.cs
@@ -0,0 +1,64 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+using System.Collections.Generic;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Extension methods for generic lists.
+ ///
+ public static class ListExtensions
+ {
+
+ ///
+ /// Adds an item to a list in sorted order. The list items must implement
+ /// IComparable.
+ ///
+ /// This list.
+ /// Item to add.
+ /// The list type.
+ public static void AddSorted(this List @this, T item) where T : IComparable
+ {
+ if (@this.Count == 0)
+ {
+ @this.Add(item);
+ }
+ else if (@this[@this.Count - 1].CompareTo(item) <= 0)
+ {
+ @this.Add(item);
+ }
+ else if (@this[0].CompareTo(item) >= 0)
+ {
+ @this.Insert(0, item);
+ }
+ else
+ {
+ int index = @this.BinarySearch(item);
+ if (index < 0)
+ {
+ index = ~index;
+ }
+ @this.Insert(index, item);
+ }
+ }
+
+ ///
+ /// Fisher-Yates shuffle.
+ ///
+ public static void Shuffle(this List @this)
+ {
+ if (@this == null || @this.Count < 2) return;
+ for (int i = 0; i < @this.Count - 2; i++)
+ {
+ int j = UnityEngine.Random.Range(i, @this.Count);
+ T tmp = @this[i];
+ @this[i] = @this[j];
+ @this[j] = tmp;
+ }
+ }
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ListExtensions.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ListExtensions.cs.meta
new file mode 100644
index 000000000..8f6b3638d
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ListExtensions.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: a874d3caa547bed4c9283dbdda2dfc99
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ListExtensions.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MoreGizmos.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MoreGizmos.cs
new file mode 100644
index 000000000..0426f73bf
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MoreGizmos.cs
@@ -0,0 +1,41 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Provides more drawing routines for gizmos.
+ ///
+ public static class MoreGizmos
+ {
+
+ ///
+ /// Draws an arrow, which is a ray with an arrowhead at the end.
+ ///
+ ///
+ /// Start of the ray.
+ ///
+ ///
+ /// Direction and length the ray extends.
+ ///
+ ///
+ /// Length of the arrowhead that extends from the end the ray.
+ ///
+ ///
+ /// Angle of the arrowhead in degrees.
+ ///
+ public static void DrawArrow(Vector3 from, Vector3 direction, float arrowheadLength = 0.2f, float arrowheadAngle = 30f)
+ {
+ if (Mathf.Approximately(direction.magnitude, 0)) return;
+ Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180f + arrowheadAngle, 0) * Vector3.forward;
+ Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180f - arrowheadAngle, 0) * Vector3.forward;
+ Gizmos.DrawRay(from, direction);
+ Gizmos.DrawRay(from + direction, right * arrowheadLength);
+ Gizmos.DrawRay(from + direction, left * arrowheadLength);
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MoreGizmos.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MoreGizmos.cs.meta
new file mode 100644
index 000000000..ddd82bb71
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MoreGizmos.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 3acbe59bc81e54d40924ff0b8c7f9faa
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MoreGizmos.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MorePhysics2D.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MorePhysics2D.cs
new file mode 100644
index 000000000..fab56e7f0
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MorePhysics2D.cs
@@ -0,0 +1,121 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+#if USE_PHYSICS2D || !UNITY_2018_1_OR_NEWER
+
+ ///
+ /// Provides more routines for Physics2D.
+ ///
+ public static class MorePhysics2D
+ {
+
+ ///
+ /// Wrapper for Physics2D.queriesStartInColliders.
+ ///
+ public static bool queriesStartInColliders
+ {
+ get
+ {
+ return Physics2D.queriesStartInColliders;
+ }
+ set
+ {
+ Physics2D.queriesStartInColliders = value;
+ }
+ }
+
+ ///
+ /// Size of the preallocated array for nonallocating raycasts.
+ ///
+ public static int maxRaycastResults
+ {
+ get
+ {
+ return raycastResults.Length;
+ }
+ set
+ {
+ if (value != raycastResults.Length)
+ {
+ raycastResults = new RaycastHit2D[value];
+ }
+ }
+ }
+
+ private static RaycastHit2D[] raycastResults = new RaycastHit2D[20];
+ private static ContactFilter2D contactFilter = new ContactFilter2D();
+
+ ///
+ /// Runs a nonallocating linecast, ignoring the source.
+ ///
+ public static GameObject Raycast2DWithoutSelf(Transform source, Transform destination, LayerMask layerMask)
+ {
+ var start2D = new Vector2(source.position.x, source.position.y);
+ var end2D = new Vector2(destination.position.x, destination.position.y);
+ var originalRaycastsStartInColliders = MorePhysics2D.queriesStartInColliders;
+ MorePhysics2D.queriesStartInColliders = false;
+ contactFilter.layerMask = layerMask;
+ var numResults = Physics2D.Linecast(start2D, end2D, contactFilter, raycastResults);
+ MorePhysics2D.queriesStartInColliders = originalRaycastsStartInColliders;
+ for (int i = 0; i < numResults; i++)
+ {
+ var result = raycastResults[i];
+ if (result.transform == source) continue; // Skip source.
+ return result.collider.gameObject; // Array is in distance order, so return first non-source.
+ }
+ return null;
+ }
+
+ }
+
+#else
+
+ ///
+ /// Provides more routines for Physics2D.
+ ///
+ public static class MorePhysics2D
+ {
+
+
+ ///
+ /// Stub wrapper for Physics2D.queriesStartInColliders.
+ ///
+ public static bool queriesStartInColliders
+ {
+ get { return false; }
+ set { }
+ }
+
+ ///
+ /// Stub for size of the preallocated array for nonallocating raycasts.
+ ///
+ public static int maxRaycastResults
+ {
+ get { return 0; }
+ set { }
+ }
+
+ ///
+ /// Stub for running a nonallocating linecast, ignoring the source.
+ ///
+ public static GameObject Raycast2DWithoutSelf(Transform source, Transform destination, LayerMask layerMask)
+ {
+ LogUsePhysics2DWarning();
+ return null;
+ }
+
+ public static void LogUsePhysics2DWarning()
+ {
+ if (Debug.isDebugBuild) Debug.LogWarning("To enable Physics2D support for a Pixel Crushers asset, add the Scripting Define Symbol 'USE_PHYSICS2D'.");
+ }
+
+ }
+
+#endif
+
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MorePhysics2D.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MorePhysics2D.cs.meta
new file mode 100644
index 000000000..e74ae77fe
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MorePhysics2D.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: a17660db60d47a24ca2a73d9490f686a
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/MorePhysics2D.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Pool.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Pool.cs
new file mode 100644
index 000000000..4fee18fe1
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Pool.cs
@@ -0,0 +1,81 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System.Collections.Generic;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// This generic class implements an object pool. It helps prevent garbage collection
+ /// stutter by reusing objects without allocating and deallocating memory.
+ ///
+ public class Pool where T : new()
+ {
+
+ private List m_free = new List();
+
+ private List m_used = new List();
+
+ ///
+ /// Gets an object from the pool. After getting an object, you should
+ /// initialize its fields because it will have the values from its
+ /// previous use.
+ ///
+ public T Get()
+ {
+ lock (m_free)
+ {
+ if (m_free.Count > 0)
+ {
+ T item = m_free[0];
+ m_used.Add(item);
+ m_free.RemoveAt(0);
+ return item;
+ }
+ else
+ {
+ T item = new T();
+ m_used.Add(item);
+ return item;
+ }
+ }
+ }
+
+ ///
+ /// Releases an object back to the pool.
+ ///
+ /// Item.
+ public void Release(T item)
+ {
+ lock (m_free)
+ {
+ m_free.Add(item);
+ m_used.Remove(item);
+ }
+ }
+
+ ///
+ /// Preallocates a number of objects into the pool.
+ ///
+ /// Initial size.
+ public void Allocate(int initialSize)
+ {
+ while (m_free.Count < initialSize)
+ {
+ m_free.Add(new T());
+ }
+ }
+
+ ///
+ /// Trims the pool to a maximum number of objects.
+ ///
+ /// Max objects.
+ public void Trim(int max)
+ {
+ m_free.RemoveRange(0, Mathf.Min(m_free.Count, max));
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Pool.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Pool.cs.meta
new file mode 100644
index 000000000..7ebf19636
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Pool.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 14f88b9c6b8104a4a8cb74c6a35f7392
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/Pool.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RectExtensions.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RectExtensions.cs
new file mode 100644
index 000000000..b38e5b722
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RectExtensions.cs
@@ -0,0 +1,53 @@
+// From "Unity Editor Window Zooming" by Martin Ecker.
+// http://martinecker.com/martincodes/unity-editor-window-zooming/
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ // Helper Rect extension methods
+ public static class RectExtensions
+ {
+ public static Vector2 TopLeft(this Rect rect)
+ {
+ return new Vector2(rect.xMin, rect.yMin);
+ }
+ public static Rect ScaleSizeBy(this Rect rect, float scale)
+ {
+ return rect.ScaleSizeBy(scale, rect.center);
+ }
+ public static Rect ScaleSizeBy(this Rect rect, float scale, Vector2 pivotPoint)
+ {
+ Rect result = rect;
+ result.x -= pivotPoint.x;
+ result.y -= pivotPoint.y;
+ result.xMin *= scale;
+ result.xMax *= scale;
+ result.yMin *= scale;
+ result.yMax *= scale;
+ result.x += pivotPoint.x;
+ result.y += pivotPoint.y;
+ return result;
+ }
+ public static Rect ScaleSizeBy(this Rect rect, Vector2 scale)
+ {
+ return rect.ScaleSizeBy(scale, rect.center);
+ }
+ public static Rect ScaleSizeBy(this Rect rect, Vector2 scale, Vector2 pivotPoint)
+ {
+ Rect result = rect;
+ result.x -= pivotPoint.x;
+ result.y -= pivotPoint.y;
+ result.xMin *= scale.x;
+ result.xMax *= scale.x;
+ result.yMin *= scale.y;
+ result.yMax *= scale.y;
+ result.x += pivotPoint.x;
+ result.y += pivotPoint.y;
+ return result;
+ }
+ }
+
+}
+
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RectExtensions.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RectExtensions.cs.meta
new file mode 100644
index 000000000..9a6730cab
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RectExtensions.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 604b3608f35ffdc438500786022f72c9
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RectExtensions.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RuntimeTypeUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RuntimeTypeUtility.cs
new file mode 100644
index 000000000..7ad139206
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RuntimeTypeUtility.cs
@@ -0,0 +1,86 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+using System.Linq;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Utility methods to work with types.
+ ///
+ public static class RuntimeTypeUtility
+ {
+
+ ///
+ /// Searches all assemblies for a type with a specified name.
+ ///
+ /// Fully-qualified type name.
+ /// A type, or null if none matches.
+ public static System.Type GetTypeFromName(string typeName)
+ {
+ var assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
+ for (int i = 0; i < assemblies.Length; i++)
+ {
+ var assembly = assemblies[i];
+ try
+ {
+ var type = assembly.GetType(typeName);
+ if (type != null) return type;
+ }
+ catch (Exception)
+ {
+ // Ignore exceptions.
+ }
+ }
+ return null;
+ }
+
+ public static System.Reflection.Assembly[] GetAssemblies()
+ {
+#if NET_STANDARD_2_0 || UNITY_IOS
+ return AppDomain.CurrentDomain.GetAssemblies(); // Used to exclude dynamic assemblies, but unsupported in some iOS.
+#else
+ //---Was: return AppDomain.CurrentDomain.GetAssemblies().Where(p => !(p.ManifestModule is System.Reflection.Emit.ModuleBuilder)).ToArray(); // Exclude dynamic assemblies.
+ return AppDomain.CurrentDomain.GetAssemblies(); // Allows evaluation version to build to iOS.
+#endif
+ }
+
+ ///
+ /// Gets the wrapper type for a Pixel Crushers type, or returns the
+ /// type itself for a non-Pixel Crushers type. Wrappers are used to
+ /// maintain references when switching between source and DLLs.
+ ///
+ /// Original type.
+ /// Wrapper type.
+ public static System.Type GetWrapperType(System.Type type)
+ {
+ if (type == null || string.IsNullOrEmpty(type.Namespace) || !type.Namespace.StartsWith("PixelCrushers")) return type;
+ try
+ {
+ var wrapperName = type.Namespace + ".Wrappers." + type.Name;
+ var assemblies = GetAssemblies();
+ foreach (var assembly in assemblies)
+ {
+ try
+ {
+ var wrapperList = (from assemblyType in assembly.GetExportedTypes()
+ where string.Equals(assemblyType.FullName, wrapperName)
+ select assemblyType).ToArray();
+ if (wrapperList.Length > 0) return wrapperList[0];
+ }
+ catch (System.Exception)
+ {
+ // If an assembly complains, ignore it and move on.
+ }
+ }
+ }
+ catch (System.Exception)
+ {
+ }
+ return null;
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RuntimeTypeUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RuntimeTypeUtility.cs.meta
new file mode 100644
index 000000000..d42771cae
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RuntimeTypeUtility.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 2145308cdb7b1cb4fa549c59277c0b3f
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/RuntimeTypeUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SafeConvert.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SafeConvert.cs
new file mode 100644
index 000000000..c564d7c4c
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SafeConvert.cs
@@ -0,0 +1,74 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Conversion methods that return default values instead of throwing exceptions.
+ ///
+ public static class SafeConvert
+ {
+
+ ///
+ /// Converts a string to an int.
+ ///
+ /// Source string.
+ /// int value, or 0 on conversion failure.
+ public static int ToInt(string s)
+ {
+ int result;
+ return int.TryParse(s, out result) ? result : 0;
+ }
+
+ ///
+ /// Converts a string to a float.
+ ///
+ /// Source string.
+ /// float value, or 0 on conversion failure.
+ public static float ToFloat(string s)
+ {
+ float result;
+ return float.TryParse(s, System.Globalization.NumberStyles.Any, System.Globalization.CultureInfo.InvariantCulture, out result) ? result : 0;
+ }
+
+ private const string CommaTag = "%COMMA%";
+ private const string DoubleQuoteTag = "%QUOTE%";
+ private const string NewlineTag = "%NEWLINE%";
+
+ ///
+ /// Sanitizes a string so it can be serialized to string-based serialization systems.
+ ///
+ /// Source string.
+ /// Sanitized version.
+ public static string ToSerializedElement(string s)
+ {
+ if (s.Contains(",") || s.Contains("\"") || s.Contains("\n"))
+ {
+ return s.Replace(",", CommaTag).Replace("\"", DoubleQuoteTag).Replace("\n", NewlineTag);
+ }
+ else
+ {
+ return s;
+ }
+ }
+
+ ///
+ /// Desanitizes a string that was previously sanitized for use in string-based serialization systems.
+ ///
+ /// Sanitized version.
+ /// Original source string.
+ public static string FromSerializedElement(string s)
+ {
+ if (s.Contains(CommaTag) || s.Contains(DoubleQuoteTag) || s.Contains(NewlineTag))
+ {
+ return s.Replace(CommaTag, ",").Replace(DoubleQuoteTag, "\"").Replace(NewlineTag, "\n");
+ }
+ else
+ {
+ return s;
+ }
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SafeConvert.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SafeConvert.cs.meta
new file mode 100644
index 000000000..45327c657
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SafeConvert.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 00c31fdd4e61a0d4c9b88dc4abebf30f
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SafeConvert.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SceneNotifier.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SceneNotifier.cs
new file mode 100644
index 000000000..8057bdcd4
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SceneNotifier.cs
@@ -0,0 +1,33 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Service to notify subscribers when a scene is being unloaded.
+ ///
+ public static class SceneNotifier
+ {
+
+ public delegate void UnloadSceneDelegate(int sceneIndex);
+
+ ///
+ /// Invoked by NotifyWillUnloadScene(sceneIndex), which should be called
+ /// before unloading a scene.
+ ///
+ public static event UnloadSceneDelegate willUnloadScene = delegate { };
+
+ ///
+ /// Notifies all subscribers that the scene with the specified index will be unloaded.
+ ///
+ /// Scene index in build settings.
+ public static void NotifyWillUnloadScene(int sceneIndex)
+ {
+ willUnloadScene(sceneIndex);
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SceneNotifier.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SceneNotifier.cs.meta
new file mode 100644
index 000000000..e26e4a44a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SceneNotifier.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: e6d56a2b48d2db94a80430efe14fe9da
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/SceneNotifier.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ScriptableObjectUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ScriptableObjectUtility.cs
new file mode 100644
index 000000000..a12936108
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ScriptableObjectUtility.cs
@@ -0,0 +1,147 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+using System.Collections.Generic;
+#if UNITY_WINRT
+using System.Linq.Expressions;
+using System.Reflection;
+#endif
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Utility functions for creating ScriptableObjects.
+ ///
+ public static class ScriptableObjectUtility
+ {
+
+ ///
+ /// Create a ScriptableObject of type T, calling the method Initialize() if present.
+ ///
+ /// The ScriptableObject type.
+ /// The new ScriptableObject.
+ public static T CreateScriptableObject() where T : ScriptableObject
+ {
+ var scriptableObject = ScriptableObject.CreateInstance() as T;
+ InitializeScriptableObject(scriptableObject);
+ return scriptableObject;
+ }
+
+ ///
+ /// Create a ScriptableObject of a specified type, calling Initialize() if present.
+ ///
+ /// The ScriptableObject type.
+ /// The new ScriptableObject.
+ public static ScriptableObject CreateScriptableObject(Type type)
+ {
+ var scriptableObject = ScriptableObject.CreateInstance(type);
+ InitializeScriptableObject(scriptableObject);
+ return scriptableObject;
+ }
+
+ ///
+ /// Calls Initialize() on a ScriptableObject if present.
+ ///
+ /// The ScriptableObject to initialize.
+ public static void InitializeScriptableObject(ScriptableObject scriptableObject)
+ {
+ if (scriptableObject == null) return;
+ var methodInfo = scriptableObject.GetType().GetMethod("Initialize");
+ if (methodInfo != null) methodInfo.Invoke(scriptableObject, null);
+ }
+
+ ///
+ /// Makes a deep copy of a ScriptableObject list by instantiating copies of the
+ /// list elements.
+ ///
+ /// List to clone.
+ /// Optional reference to source object to report in log if operation fails.
+ /// A second list containing new instances of the list elements.
+ public static List CloneList(List original, UnityEngine.Object source = null) where T : ScriptableObject
+ {
+ var copy = new List();
+ if (original != null)
+ {
+ for (int i = 0; i < original.Count; i++)
+ {
+ if (original[i] != null && original[i] is T)
+ {
+ copy.Add(ScriptableObject.Instantiate(original[i]) as T);
+ }
+ else
+ {
+ if (Debug.isDebugBuild)
+ {
+ if (source != null)
+ {
+ Debug.LogWarning("CloneList<" + typeof(T).Name + ">: Element " + i + " is null in a list in " + source + ".", source);
+ }
+ else
+ {
+ Debug.LogWarning("CloneList<" + typeof(T).Name + ">: Element " + i + " is null.");
+ }
+ }
+ copy.Add(null);
+ }
+ }
+ }
+ return copy;
+ }
+
+#if UNITY_WINRT
+ ///
+ /// Given a lambda expression that calls a method, returns the method info.
+ ///
+ ///
+ /// The expression.
+ ///
+ public static MethodInfo GetMethodInfo(Expression expression)
+ {
+ return GetMethodInfo((LambdaExpression)expression);
+ }
+
+ ///
+ /// Given a lambda expression that calls a method, returns the method info.
+ ///
+ ///
+ /// The expression.
+ ///
+ public static MethodInfo GetMethodInfo(Expression> expression)
+ {
+ return GetMethodInfo((LambdaExpression)expression);
+ }
+
+ ///
+ /// Given a lambda expression that calls a method, returns the method info.
+ ///
+ ///
+ /// The expression.
+ ///
+ public static MethodInfo GetMethodInfo(Expression> expression)
+ {
+ return GetMethodInfo((LambdaExpression)expression);
+ }
+
+ ///
+ /// Given a lambda expression that calls a method, returns the method info.
+ ///
+ /// The expression.
+ ///
+ public static MethodInfo GetMethodInfo(LambdaExpression expression)
+ {
+ MethodCallExpression outermostExpression = expression.Body as MethodCallExpression;
+
+ if (outermostExpression == null)
+ {
+ throw new ArgumentException("Invalid Expression. Expression should consist of a Method call only.");
+ }
+
+ return outermostExpression.Method;
+ }
+#endif
+
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ScriptableObjectUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ScriptableObjectUtility.cs.meta
new file mode 100644
index 000000000..0d3eb9293
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ScriptableObjectUtility.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 557ca9fc744c84d41955636b0afaad9f
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Misc/ScriptableObjectUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System.meta
new file mode 100644
index 000000000..3b83c86c2
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 8b8096312eee0714fab2bbc58c4d72a9
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc.meta
new file mode 100644
index 000000000..5f1745313
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 328fb7ef6bdd3b244997dd8ba220ae9c
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/AutoSaveLoad.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/AutoSaveLoad.cs
new file mode 100644
index 000000000..ff1718594
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/AutoSaveLoad.cs
@@ -0,0 +1,126 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Auto-saves when the game closes and auto-loads when the game opens.
+ /// Useful for mobile games.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class AutoSaveLoad : MonoBehaviour
+ {
+
+ [Tooltip("Save to this slot.")]
+ public int saveSlotNumber = 1;
+
+ [Tooltip("Don't auto-save in these scene indices.")]
+ public int[] dontSaveInScenes = new int[0];
+
+ [Tooltip("Load the saved game when this component starts.")]
+ public bool loadOnStart = true;
+
+ [Tooltip("Save when the player quits the app.")]
+ public bool saveOnQuit = true;
+
+ [Tooltip("Save when the player pauses or minimizes the app; tick this for mobile builds.")]
+ public bool saveOnPause = true;
+
+ [Tooltip("Save when the app loses focus.")]
+ public bool saveOnLoseFocus = false;
+
+ ///
+ /// When starting, load the game.
+ ///
+ private void Start()
+ {
+ if (loadOnStart && SaveSystem.HasSavedGameInSlot(saveSlotNumber))
+ {
+ SaveSystem.LoadFromSlot(saveSlotNumber);
+ }
+ }
+
+#if UNITY_2018_1_OR_NEWER
+ private void OnEnable()
+ {
+ Application.wantsToQuit -= OnWantsToQuit;
+ Application.wantsToQuit += OnWantsToQuit;
+ }
+
+ private void OnDisable()
+ {
+ Application.wantsToQuit -= OnWantsToQuit;
+ }
+
+ private bool OnWantsToQuit()
+ {
+ CheckSaveOnQuit();
+ return true;
+ }
+#else
+ ///
+ /// When quitting, save the game.
+ ///
+ private void OnApplicationQuit()
+ {
+ CheckSaveOnQuit();
+ }
+#endif
+
+ private void CheckSaveOnQuit()
+ {
+ if (enabled && saveOnQuit && CanSaveInThisScene())
+ {
+ SaveSystem.SaveToSlotImmediate(saveSlotNumber);
+ }
+ }
+
+ ///
+ /// When app is paused (e.g., minimized) and saveOnPause is true, save game.
+ ///
+ /// True indicates game is being paused.
+ private void OnApplicationPause(bool paused)
+ {
+ if (enabled && paused && saveOnPause && CanSaveInThisScene())
+ {
+ SaveSystem.SaveToSlotImmediate(saveSlotNumber);
+ }
+ }
+
+ ///
+ /// When app loses focus and saveOnLoseFocus is true, save the game.
+ ///
+ /// False indicates game is losing focus.
+ void OnApplicationFocus(bool focusStatus)
+ {
+ if (enabled && saveOnLoseFocus && focusStatus == false && CanSaveInThisScene())
+ {
+ SaveSystem.SaveToSlotImmediate(saveSlotNumber);
+ }
+ }
+
+ private bool CanSaveInThisScene()
+ {
+ var sceneIndex = SaveSystem.GetCurrentSceneIndex();
+ for (int i = 0; i < dontSaveInScenes.Length; i++)
+ {
+ if (sceneIndex == dontSaveInScenes[i]) return false;
+ }
+ return true;
+ }
+
+ ///
+ /// Clears the saved game data and restarts the game at a specified scene.
+ ///
+ ///
+ public void Restart(string startingSceneName)
+ {
+ SaveSystem.DeleteSavedGameInSlot(saveSlotNumber);
+ SaveSystem.RestartGame(startingSceneName);
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/AutoSaveLoad.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/AutoSaveLoad.cs.meta
new file mode 100644
index 000000000..f522a9098
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/AutoSaveLoad.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: a8f438484909b3b4d86e4bb9e1110e10
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/AutoSaveLoad.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemEvents.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemEvents.cs
new file mode 100644
index 000000000..99ffe81fc
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemEvents.cs
@@ -0,0 +1,86 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using System;
+using UnityEngine;
+using UnityEngine.Events;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Provides Save System UnityEvents.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class SaveSystemEvents : MonoBehaviour
+ {
+
+ public UnityEvent onSaveStart = new UnityEvent();
+ public UnityEvent onSaveEnd = new UnityEvent();
+ public UnityEvent onLoadStart = new UnityEvent();
+ public UnityEvent onLoadEnd = new UnityEvent();
+ public UnityEvent onSaveDataApplied = new UnityEvent();
+ public UnityEvent onSceneLoad = new UnityEvent();
+
+ private void OnEnable()
+ {
+ UnregisterEvents();
+ RegisterEvents();
+ }
+
+ private void OnDisable()
+ {
+ UnregisterEvents();
+ }
+
+ private void RegisterEvents()
+ {
+ SaveSystem.saveStarted += OnSaveStarted;
+ SaveSystem.saveEnded += OnSaveEnded;
+ SaveSystem.loadStarted += OnLoadStarted;
+ SaveSystem.loadEnded += OnLoadEnded;
+ SaveSystem.saveDataApplied += OnSaveDataApplied;
+ SaveSystem.sceneLoaded += OnSceneLoaded;
+ }
+
+ private void UnregisterEvents()
+ {
+ SaveSystem.saveStarted -= OnSaveStarted;
+ SaveSystem.saveEnded -= OnSaveEnded;
+ SaveSystem.loadStarted -= OnLoadStarted;
+ SaveSystem.loadEnded -= OnLoadEnded;
+ SaveSystem.saveDataApplied -= OnSaveDataApplied;
+ SaveSystem.sceneLoaded -= OnSceneLoaded;
+ }
+
+ private void OnSaveStarted()
+ {
+ onSaveStart.Invoke();
+ }
+
+ private void OnSaveEnded()
+ {
+ onSaveEnd.Invoke();
+ }
+
+ private void OnLoadStarted()
+ {
+ onLoadStart.Invoke();
+ }
+
+ private void OnLoadEnded()
+ {
+ onLoadEnd.Invoke();
+ }
+
+ private void OnSaveDataApplied()
+ {
+ onSaveDataApplied.Invoke();
+ }
+
+ private void OnSceneLoaded(string sceneName, int sceneIndex)
+ {
+ onSceneLoad.Invoke();
+ }
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemEvents.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemEvents.cs.meta
new file mode 100644
index 000000000..cb3df552a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemEvents.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: b39ebd6ef647e9c4f94022c50a93195e
+timeCreated: 1532871680
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemEvents.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemMethods.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemMethods.cs
new file mode 100644
index 000000000..771d900c7
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemMethods.cs
@@ -0,0 +1,124 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Provides inspector-selectable methods to control SaveSystem.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class SaveSystemMethods : MonoBehaviour
+ {
+
+ [Tooltip("Scene to load in LoadOrRestart method if no saved game exists yet.")]
+ public string defaultStartingSceneName;
+
+ ///
+ /// Saves the current game in the specified slot.
+ ///
+ /// slot to save.
+ public virtual void SaveSlot(int slotNumber)
+ {
+ SaveSystem.SaveToSlot(slotNumber);
+ }
+
+ ///
+ /// Loads the game previously-saved in the specified slot.
+ ///
+ /// Slot to load.
+ public virtual void LoadFromSlot(int slotNumber)
+ {
+ SaveSystem.LoadFromSlot(slotNumber);
+ }
+
+ ///
+ /// Changes scenes. You can optionally specify a player spawnpoint by
+ /// adding '@' and the spawnpoint GameObject name.
+ ///
+ /// Scene name followed by an optional at-sign and spawnpoint name.
+ public virtual void LoadScene(string sceneNameAndSpawnpoint)
+ {
+ SaveSystem.LoadScene(sceneNameAndSpawnpoint);
+ }
+
+ ///
+ /// Resets all saved game data.
+ ///
+ public virtual void ResetGameState()
+ {
+ SaveSystem.ResetGameState();
+ }
+
+ ///
+ /// Resets all saved game data and restarts the game at the specified scene.
+ ///
+ /// Scene to restart at.
+ public virtual void RestartGame(string startingSceneName)
+ {
+ SaveSystem.RestartGame(startingSceneName);
+ }
+
+ ///
+ /// Load the specified slot, or restart the game from the default
+ /// starting scene if no save exists yet.
+ ///
+ /// Slot number to load.
+ public virtual void LoadOrRestart(int slotNumber)
+ {
+ if (SaveSystem.HasSavedGameInSlot(slotNumber))
+ {
+ SaveSystem.LoadFromSlot(slotNumber);
+ }
+ else
+ {
+ SaveSystem.RestartGame(defaultStartingSceneName);
+ }
+ }
+
+ ///
+ /// Deletes the saved game in the specified slot.
+ ///
+ public virtual void DeleteSavedGameInSlot(int slotNumber)
+ {
+ SaveSystem.DeleteSavedGameInSlot(slotNumber);
+ }
+
+ ///
+ /// Records the current game state into the Save System.
+ ///
+ public virtual void RecordSavedGameData()
+ {
+ SaveSystem.RecordSavedGameData();
+ }
+
+ ///
+ /// Applies the most recently recorded game state.
+ ///
+ public virtual void ApplySavedGameData()
+ {
+ SaveSystem.ApplySavedGameData();
+ }
+
+ ///
+ /// Additively loads another scene.
+ ///
+ /// Scene to additively load.
+ public virtual void LoadAdditiveScene(string sceneName)
+ {
+ SaveSystem.LoadAdditiveScene(sceneName);
+ }
+
+ ///
+ /// Unloads a previously additively-loaded scene.
+ ///
+ /// Scene to unload
+ public virtual void UnloadAdditiveScene(string sceneName)
+ {
+ SaveSystem.UnloadAdditiveScene(sceneName);
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemMethods.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemMethods.cs.meta
new file mode 100644
index 000000000..018913c40
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemMethods.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 3193517feff4594469dcecce124a26ec
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemMethods.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemTestMenu.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemTestMenu.cs
new file mode 100644
index 000000000..3eab2eb17
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemTestMenu.cs
@@ -0,0 +1,140 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEngine.Events;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Simple menu for testing the Save System.
+ ///
+ [AddComponentMenu("")] // Use wrapper.
+ public class SaveSystemTestMenu : MonoBehaviour
+ {
+ [Tooltip("Unity input button that toggles menu open/closed.")]
+ public string menuInputButton = "Cancel";
+
+ [Tooltip("Optional GUI Skin to provide custom Label, Button, and Box styles.")]
+ public GUISkin guiSkin;
+
+ [Tooltip("Size of menu buttons.")]
+ public Vector2 buttonSize = new Vector2(200, 30);
+
+ [Tooltip("Slot that menu saves game in.")]
+ public int saveSlot = 1;
+
+ [Tooltip("Optional instructions to show when script starts.")]
+ public string instructions = "Press Escape for menu.";
+
+ [Tooltip("How long to show instructions.")]
+ public float instructionsDuration = 5;
+
+ [Tooltip("Pause the game while the menu is open.")]
+ public bool pauseWhileOpen = false;
+
+ [Tooltip("If Input Device Manager mode is mouse, show cursor when opening and hide when closing.")]
+ public bool allowCursorWhileOpen = false;
+
+ public UnityEvent onShow = new UnityEvent();
+ public UnityEvent onHide = new UnityEvent();
+
+ private bool m_isVisible = false;
+ private float m_instructionsDoneTime;
+ private bool m_prevCursorState = false;
+
+ private void Awake()
+ {
+ m_instructionsDoneTime = string.IsNullOrEmpty(instructions) ? 0 : Time.time + instructionsDuration;
+ }
+
+ private void Update()
+ {
+ if (InputDeviceManager.IsButtonDown(menuInputButton)) ToggleMenu();
+ }
+
+ private void OnDestroy()
+ {
+ Time.timeScale = 1;
+ }
+
+ public void ToggleMenu()
+ {
+ m_isVisible = !m_isVisible;
+ if (pauseWhileOpen) Time.timeScale = m_isVisible ? 0 : 1;
+ if (m_isVisible)
+ {
+ HandleCursor(true);
+ onShow.Invoke();
+ }
+ else
+ {
+ HandleCursor(false);
+ onHide.Invoke();
+ }
+ }
+
+ void HandleCursor(bool open)
+ {
+ if (allowCursorWhileOpen && InputDeviceManager.deviceUsesCursor)
+ {
+ if (open)
+ {
+ m_prevCursorState = Cursor.visible;
+ Cursor.visible = true;
+ Cursor.lockState = CursorLockMode.None;
+ }
+ else
+ {
+ Cursor.visible = m_prevCursorState;
+ Cursor.lockState = m_prevCursorState ? CursorLockMode.None : CursorLockMode.Locked;
+ }
+ }
+ }
+
+ void OnGUI()
+ {
+ var originalSkin = GUI.skin;
+ if (guiSkin != null) GUI.skin = guiSkin;
+
+ // Draw instructions if within the timeframe to do so:
+ if (Time.time < m_instructionsDoneTime)
+ {
+ GUILayout.Label(instructions);
+ }
+
+ // Draw menu if visible:
+ if (!m_isVisible) return;
+ var buttonWidth = buttonSize.x;
+ var buttonHeight = buttonSize.y;
+ GUILayout.BeginArea(new Rect((Screen.width - buttonWidth) / 2, (Screen.height - 4 * buttonHeight) / 2, buttonWidth, 4 * (buttonHeight + 10)));
+ if (GUILayout.Button("Resume", GUILayout.Height(buttonHeight)))
+ {
+ ToggleMenu();
+ }
+ if (GUILayout.Button("Save", GUILayout.Height(buttonHeight)))
+ {
+ ToggleMenu();
+ Debug.Log("Saving game to slot " + saveSlot);
+ SaveSystem.SaveToSlot(saveSlot);
+ }
+ if (GUILayout.Button("Load", GUILayout.Height(buttonHeight)))
+ {
+ ToggleMenu();
+ Debug.Log("Loading game from slot " + saveSlot);
+ SaveSystem.LoadFromSlot(saveSlot);
+ }
+ if (GUILayout.Button("Quit", GUILayout.Height(buttonHeight)))
+ {
+ ToggleMenu();
+ Debug.Log("Quitting");
+ Application.Quit();
+#if UNITY_EDITOR
+ UnityEditor.EditorApplication.isPlaying = false;
+#endif
+ }
+ GUILayout.EndArea();
+ if (guiSkin != null) GUI.skin = originalSkin;
+ }
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemTestMenu.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemTestMenu.cs.meta
new file mode 100644
index 000000000..845bbdc18
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemTestMenu.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: c69cc058fcec3df49802bb8216d565cc
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SaveSystemTestMenu.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SavedGameData.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SavedGameData.cs
new file mode 100644
index 000000000..bac0038bc
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SavedGameData.cs
@@ -0,0 +1,158 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Holds the data for a saved game.
+ ///
+ [Serializable]
+ public class SavedGameData : ISerializationCallbackReceiver
+ {
+
+ ///
+ /// Holds the data returned by a Saver along with the Saver's key
+ /// and the index of the scene that the Saver was in.
+ ///
+ [Serializable]
+ public class SaveRecord
+ {
+ public string key;
+ public int sceneIndex;
+ public string data;
+
+ public SaveRecord() { }
+
+ public SaveRecord(string key, int sceneIndex, string data)
+ {
+ this.key = key;
+ this.sceneIndex = sceneIndex;
+ this.data = data;
+ }
+ }
+
+ [SerializeField]
+ private int m_version = 0;
+
+ [SerializeField]
+ private string m_sceneName;
+
+ private Dictionary m_dict = new Dictionary();
+
+ [SerializeField]
+ private List m_list = new List();
+
+ ///
+ /// The save file format version. This is an arbitrary value that you
+ /// can assign by setting SaveSystem.version.
+ ///
+ public int version
+ {
+ get { return m_version; }
+ set { m_version = value; }
+ }
+
+ ///
+ /// The scene in which the game was saved.
+ ///
+ public string sceneName
+ {
+ get { return m_sceneName; }
+ set { m_sceneName = value; }
+ }
+
+ ///
+ /// Provides direct access to the dictionary of save records.
+ /// Use with caution.
+ ///
+ public Dictionary Dict { get { return m_dict; } }
+
+ public void OnBeforeSerialize()
+ {
+ m_list.Clear();
+ foreach (var kvp in m_dict)
+ {
+ m_list.Add(kvp.Value);
+ }
+ }
+
+ public void OnAfterDeserialize()
+ {
+ m_dict = new Dictionary();
+ for (int i = 0; i < m_list.Count; i++)
+ {
+ if (m_list[i] == null) continue;
+ m_dict.Add(m_list[i].key, m_list[i]);
+ }
+ }
+
+ ///
+ /// Retrieves info about the previously-saved data for a Saver.
+ ///
+ /// Saver's unique key.
+ /// Returns the Save Record stored under the Saver's key, or null if no data is stored.
+ public SaveRecord GetDataInfo(string key)
+ {
+ return m_dict.ContainsKey(key) ? m_dict[key] : null;
+ }
+
+ ///
+ /// Retrieves the previously-saved data for a Saver.
+ ///
+ /// Saver's unique key.
+ /// Returns the data stored under the Saver's key, or null if no data is stored.
+ public string GetData(string key)
+ {
+ return m_dict.ContainsKey(key) ? m_dict[key].data : null;
+ }
+
+ ///
+ /// Stores a Saver's data.
+ ///
+ /// Saver's unique key.
+ /// Scene in which Saver exists.
+ /// Data to set.
+ public void SetData(string key, int sceneIndex, string data)
+ {
+ if (m_dict.ContainsKey(key))
+ {
+ m_dict[key].sceneIndex = sceneIndex;
+ m_dict[key].data = data;
+ }
+ else
+ {
+ m_dict.Add(key, new SaveRecord(key, sceneIndex, data));
+ }
+ }
+
+ ///
+ /// Removes a Saver's data.
+ ///
+ /// Saver's unique key.
+ public void DeleteData(string key)
+ {
+ if (m_dict.ContainsKey(key))
+ {
+ m_dict.Remove(key);
+ }
+ }
+
+ ///
+ /// Removes all save records except those in the current scene and those that are
+ /// configured to remember across scene changes.
+ ///
+ /// Don't clear out Savers in this scene.
+ public void DeleteObsoleteSaveData(int currentSceneIndex)
+ {
+ m_dict = m_dict.Where(element => element.Value.sceneIndex == currentSceneIndex || element.Value.sceneIndex == SaveSystem.NoSceneIndex)
+ .ToDictionary(element => element.Key, element => element.Value);
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SavedGameData.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SavedGameData.cs.meta
new file mode 100644
index 000000000..cd551adab
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SavedGameData.cs.meta
@@ -0,0 +1,17 @@
+fileFormatVersion: 2
+guid: d95a9ffa197e8ff4c88116cc137b954e
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SavedGameData.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/ScenePortal.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/ScenePortal.cs
new file mode 100644
index 000000000..40e20221a
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/ScenePortal.cs
@@ -0,0 +1,89 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Changes scenes, either by calling UsePortal() or OnTriggerEnter.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class ScenePortal : MonoBehaviour
+ {
+
+ [Tooltip("Only objects with this tag can use the portal.")]
+ [SerializeField]
+ private string m_requiredTag = "Player";
+
+ [Tooltip("Go to this scene.")]
+ [SerializeField]
+ private string m_destinationSceneName;
+
+ [Tooltip("If not blank, move the player to the GameObject with this name.")]
+ [SerializeField]
+ private string m_spawnpointNameInDestinationScene;
+
+ [SerializeField]
+ private UnityEngine.Events.UnityEvent m_onUsePortal = new UnityEngine.Events.UnityEvent();
+
+ private bool m_isLoadingScene = false;
+
+ public string requiredTag
+ {
+ get { return m_requiredTag; }
+ set { m_requiredTag = value; }
+ }
+
+ public string destinationSceneName
+ {
+ get { return m_destinationSceneName; }
+ set { m_destinationSceneName = value; }
+ }
+
+ public string spawnpointNameInDestinationScene
+ {
+ get { return m_spawnpointNameInDestinationScene; }
+ set { m_spawnpointNameInDestinationScene = value; }
+ }
+
+ public bool isLoadingScene
+ {
+ get { return m_isLoadingScene; }
+ set { m_isLoadingScene = value; }
+ }
+
+ public UnityEngine.Events.UnityEvent onUsePortal { get { return m_onUsePortal; } }
+
+ public virtual void UsePortal()
+ {
+ if (isLoadingScene) return;
+ isLoadingScene = true;
+ onUsePortal.Invoke();
+ LoadScene();
+ }
+
+ protected void LoadScene()
+ {
+ SaveSystem.LoadScene(string.IsNullOrEmpty(spawnpointNameInDestinationScene) ? destinationSceneName : destinationSceneName + "@" + spawnpointNameInDestinationScene);
+ }
+
+ private void OnTriggerEnter(Collider other)
+ {
+ if (!other.CompareTag(requiredTag)) return;
+ UsePortal();
+ }
+
+#if USE_PHYSICS2D || !UNITY_2018_1_OR_NEWER
+
+ private void OnTriggerEnter2D(Collider2D other)
+ {
+ if (!other.CompareTag(requiredTag)) return;
+ UsePortal();
+ }
+
+#endif
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/ScenePortal.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/ScenePortal.cs.meta
new file mode 100644
index 000000000..0bdec45bb
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/ScenePortal.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 065173b574a747d45b2252907f5015fa
+timeCreated: 1485134775
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/ScenePortal.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SceneValidationMode.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SceneValidationMode.cs
new file mode 100644
index 000000000..eb9366c5f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SceneValidationMode.cs
@@ -0,0 +1,31 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Specifies the reason why SaveSystem.validateSceneName is being called.
+ ///
+ public enum SceneValidationMode
+ {
+ ///
+ /// Just loading a new scene.
+ ///
+ LoadingScene,
+
+ ///
+ /// Loading a saved game.
+ ///
+ LoadingSavedGame,
+
+ ///
+ /// Resetting the game state and loading the first gameplay scene.
+ ///
+ RestartingGame
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SceneValidationMode.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SceneValidationMode.cs.meta
new file mode 100644
index 000000000..33de74b9f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SceneValidationMode.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: f0da9da504c03a14aa3e9023eba38ad7
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Misc/SceneValidationMode.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/SaveSystem.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/SaveSystem.cs
new file mode 100644
index 000000000..ee6203edc
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/SaveSystem.cs
@@ -0,0 +1,1181 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using System.Collections;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// This is the main Save System class. It runs as a singleton MonoBehaviour
+ /// and provides static methods to save and load games.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class SaveSystem : MonoBehaviour
+ {
+
+ public const int NoSceneIndex = -1;
+
+ ///
+ /// Stores an int indicating the slot number of the most recently saved game.
+ ///
+ public const string LastSavedGameSlotPlayerPrefsKey = "savedgame_lastSlotNum";
+
+ [Tooltip("Optional saved game version number of your choosing. Version number is included in saved game files.")]
+ [SerializeField]
+ private int m_version = 0;
+
+ [Tooltip("When loading a game, load the scene that the game was saved in.")]
+ [SerializeField]
+ private bool m_saveCurrentScene = true;
+
+ [Tooltip("Highest save slot number allowed.")]
+ [SerializeField]
+ private int m_maxSaveSlot = 99999;
+
+ [Tooltip("When loading a game/scene, wait this many frames before applying saved data to allow other scripts to initialize first.")]
+ [SerializeField]
+ private int m_framesToWaitBeforeApplyData = 0;
+
+ [Tooltip("Log debug info.")]
+ [SerializeField]
+ private bool m_debug = false;
+
+ private bool m_isLoadingAdditiveScene = false;
+
+ private static SaveSystem m_instance = null;
+
+ private static HashSet m_savers = new HashSet();
+
+ private static List m_tmpSavers = new List();
+
+ private static SavedGameData m_savedGameData = new SavedGameData();
+
+ private static DataSerializer m_serializer = null;
+
+ private static SavedGameDataStorer m_storer = null;
+
+ private static SceneTransitionManager m_sceneTransitionManager = null;
+
+ private static bool m_allowNegativeSlotNumbers = false;
+
+ private static GameObject m_playerSpawnpoint = null;
+
+ private static int m_currentSceneIndex = NoSceneIndex;
+
+ private static List m_addedScenes = new List();
+
+ private static bool m_autoUnloadAdditiveScenes = false;
+
+ private static AsyncOperation m_currentAsyncOperation = null;
+
+#if USE_ADDRESSABLES
+ private static UnityEngine.ResourceManagement.AsyncOperations.AsyncOperationHandle m_currentAsyncOperationHandle;
+#endif
+
+ private static int m_framesToWaitBeforeSaveDataAppliedEvent = 0;
+
+ private static bool m_isQuitting = false;
+
+#if UNITY_2019_3_OR_NEWER && UNITY_EDITOR
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
+ static void InitStaticVariables()
+ {
+ m_instance = null;
+ m_savers = new HashSet();
+ m_tmpSavers = new List();
+ m_savedGameData = new SavedGameData();
+ m_serializer = null;
+ m_storer = null;
+ m_sceneTransitionManager = null;
+ m_playerSpawnpoint = null;
+ m_currentSceneIndex = NoSceneIndex;
+ m_addedScenes = new List();
+ m_currentAsyncOperation = null;
+ m_framesToWaitBeforeSaveDataAppliedEvent = 0;
+ m_isQuitting = false;
+ }
+#endif
+
+ ///
+ /// Optional saved game version number of your choosing. Version number is included in saved game files.
+ ///
+ public static int version
+ {
+ get
+ {
+ return (m_instance != null) ? m_instance.m_version : 0;
+ }
+ set
+ {
+ if (m_instance != null) m_instance.m_version = value;
+ }
+ }
+
+ ///
+ /// When loading a game, load the scene that the game was saved in.
+ ///
+ public static bool saveCurrentScene
+ {
+ get
+ {
+ return (m_instance != null) ? m_instance.m_saveCurrentScene : true;
+ }
+ set
+ {
+ if (m_instance != null) m_instance.m_saveCurrentScene = value;
+ }
+ }
+
+ ///
+ /// Highest save slot number allowed.
+ ///
+ public static int maxSaveSlot
+ {
+ get
+ {
+ return (m_instance != null) ? m_instance.m_maxSaveSlot : int.MaxValue;
+ }
+ set
+ {
+ if (m_instance != null) m_instance.m_maxSaveSlot = value;
+ }
+ }
+
+ ///
+ /// When loading a game/scene, wait this many frames before applying saved data to allow other scripts to initialize first.
+ ///
+ public static int framesToWaitBeforeApplyData
+ {
+ get
+ {
+ return (m_instance != null) ? m_instance.m_framesToWaitBeforeApplyData : 1;
+ }
+ set
+ {
+ if (m_instance != null) m_instance.m_framesToWaitBeforeApplyData = value;
+ }
+ }
+
+ ///
+ /// If a saver requires additional frames after ApplyData() before the saveDataApplied() event
+ /// should be called, set this property.
+ ///
+ /// Note: This value is reset to zero after every call to ApplySavedGameData.
+ ///
+ public static int framesToWaitBeforeSaveDataAppliedEvent
+ {
+ get { return m_framesToWaitBeforeSaveDataAppliedEvent; }
+ set { m_framesToWaitBeforeSaveDataAppliedEvent = value; }
+ }
+
+ public static bool debug
+ {
+ get
+ {
+ return (m_instance != null) ? m_instance.m_debug && Debug.isDebugBuild : false;
+ }
+ set
+ {
+ if (m_instance != null) m_instance.m_debug = value;
+ }
+ }
+
+ ///
+ /// Checks if an instance already exists, without also implicitly creating one.
+ ///
+ public static bool hasInstance
+ {
+ get { return m_instance != null; }
+ }
+
+ public static SaveSystem instance
+ {
+ get
+ {
+ if (m_instance == null && !m_isQuitting)
+ {
+ m_instance = GameObjectUtility.FindFirstObjectByType();
+ if (m_instance == null)
+ {
+ m_instance = new GameObject("Save System", typeof(SaveSystem)).GetComponent();
+ }
+ }
+ return m_instance;
+ }
+ }
+
+ ///
+ /// Reference to the DataSerializer in the SaveSystem's hierarchy.
+ /// SaveSystem will use it to serialize and deserialize saved game data.
+ ///
+ public static DataSerializer serializer
+ {
+ get
+ {
+ if (m_serializer == null)
+ {
+ m_serializer = instance.GetComponent();
+ if (m_serializer == null && !m_isQuitting)
+ {
+ Debug.Log("Save System: No DataSerializer found on " + instance.name + ". Adding JsonDataSerializer.", instance);
+ m_serializer = instance.gameObject.AddComponent();
+ }
+ }
+ return m_serializer;
+ }
+ }
+
+ ///
+ /// Reference to the SavedGameDataStorer in the SaveSystem's hierarchy.
+ /// SaveSystem will use it to store and retrieve saved game data.
+ ///
+ public static SavedGameDataStorer storer
+ {
+ get
+ {
+ if (m_storer == null)
+ {
+ m_storer = instance.GetComponent();
+ if (m_storer == null && !m_isQuitting)
+ {
+ Debug.Log("Save System: No SavedGameDataStorer found on " + instance.name + ". Adding PlayerPrefsSavedGameDataStorer.", instance);
+ m_storer = instance.gameObject.AddComponent();
+ }
+ }
+ return m_storer;
+ }
+ }
+
+ ///
+ /// Reference to the SceneTransitionManager in the SaveSystem's hierarchy, if present.
+ ///
+ public static SceneTransitionManager sceneTransitionManager
+ {
+ get
+ {
+ if (m_sceneTransitionManager == null)
+ {
+ m_sceneTransitionManager = instance.GetComponentInChildren();
+ }
+ return m_sceneTransitionManager;
+ }
+ }
+
+ ///
+ /// Allow the use of negative slot numbers.
+ ///
+ public bool allowNegativeSlotNumbers
+ {
+ get { return m_allowNegativeSlotNumbers; }
+ set { m_allowNegativeSlotNumbers = value; }
+ }
+
+ ///
+ /// Scenes that have been loaded additively.
+ ///
+ public static List addedScenes { get { return m_addedScenes; } }
+
+ ///
+ /// When changing scenes, automatically unload all additively-loaded scenes.
+ ///
+ public static bool autoUnloadAdditiveScenes
+ {
+ get { return m_autoUnloadAdditiveScenes; }
+ set { m_autoUnloadAdditiveScenes = value; }
+ }
+
+ ///
+ /// Current asynchronous scene load operation, or null if none. Loading scenes can use this
+ /// value to update a progress bar.
+ ///
+ public static AsyncOperation currentAsyncOperation
+ {
+ get { return m_currentAsyncOperation; }
+ set { m_currentAsyncOperation = value; }
+ }
+
+ ///
+ /// The saved game data recorded by the last call to SaveToSlot,
+ /// LoadScene, or RecordSavedGameData.
+ ///
+ /// Note: This saved game data stays in memory until you clear it by using
+ /// RestartGame() or ResetGameState(), or by loading a saved game.
+ ///
+ public static SavedGameData currentSavedGameData
+ {
+ get { return m_savedGameData; }
+ set { m_savedGameData = value; }
+ }
+
+ ///
+ /// Where the player should spawn in the current scene.
+ ///
+ public static GameObject playerSpawnpoint
+ {
+ get { return m_playerSpawnpoint; }
+ set { m_playerSpawnpoint = value; }
+ }
+
+ ///
+ /// Build index of the current scene.
+ ///
+ public static int currentSceneIndex
+ {
+ get
+ {
+ if (m_currentSceneIndex == NoSceneIndex) m_currentSceneIndex = GetCurrentSceneIndex();
+ return m_currentSceneIndex;
+ }
+ }
+
+ public delegate string ValidateSceneNameDelegate(string sceneName, SceneValidationMode sceneValidationMode);
+
+ ///
+ /// Invoked before loading a scene by name. Should return the sceneName, or a different
+ /// scene if the sceneName isn't valid (e.g., was renamed or removed from build settings),
+ /// or a blank string to not load any scene.
+ ///
+ public static ValidateSceneNameDelegate validateNameScene = null;
+
+ public delegate void SceneLoadedDelegate(string sceneName, int sceneIndex);
+
+ ///
+ /// Invoked after a scene has been loaded.
+ ///
+ public static event SceneLoadedDelegate sceneLoaded = delegate { };
+
+ ///
+ /// Invoked when starting to save a game. If assigned, waits one frame before
+ /// starting the save to allow UIs to update.
+ ///
+ public static event System.Action saveStarted = delegate { };
+
+ ///
+ /// Invoked when finished saving a game.
+ ///
+ public static event System.Action saveEnded = delegate { };
+
+ ///
+ /// Invoked when starting to load a game. If assigned, waits one frame before
+ /// starting the load to allow UIs to update.
+ ///
+ public static event System.Action loadStarted = delegate { };
+
+ ///
+ /// Invoked when finished loading a game.
+ ///
+ public static event System.Action loadEnded = delegate { };
+
+ ///
+ /// Invoked after ApplyData() has been called on all savers.
+ ///
+ public static event System.Action saveDataApplied = delegate { };
+
+ private void Awake()
+ {
+ if (m_instance == null)
+ {
+ m_instance = this;
+#if UNITY_EDITOR
+ if (Application.isPlaying)
+ { // If GameObject is hidden in Scene view, DontDestroyOnLoad will report (harmless) error.
+ UnityEditor.SceneVisibilityManager.instance.Show(gameObject, true);
+ }
+#endif
+ if (transform.parent != null) transform.SetParent(null);
+ DontDestroyOnLoad(gameObject);
+ }
+ else
+ {
+ Destroy(gameObject);
+ }
+ }
+
+ private void OnApplicationQuit()
+ {
+ m_isQuitting = true;
+ BeforeSceneChange();
+ }
+
+#if UNITY_5_4_OR_NEWER
+ private void OnEnable()
+ {
+ UnityEngine.SceneManagement.SceneManager.sceneLoaded -= OnSceneLoaded;
+ UnityEngine.SceneManagement.SceneManager.sceneLoaded += OnSceneLoaded;
+ }
+
+ private void OnDisable()
+ {
+ UnityEngine.SceneManagement.SceneManager.sceneLoaded -= OnSceneLoaded;
+ }
+
+ public void OnSceneLoaded(UnityEngine.SceneManagement.Scene scene, UnityEngine.SceneManagement.LoadSceneMode mode)
+ {
+ FinishedLoadingScene(scene.name, scene.buildIndex);
+ }
+
+#else
+ public void OnLevelWasLoaded(int level)
+ {
+ FinishedLoadingScene(GetCurrentSceneName(), level);
+ }
+#endif
+
+#if UNITY_5_3 || UNITY_5_3_OR_NEWER
+ public static string GetCurrentSceneName()
+ {
+ return UnityEngine.SceneManagement.SceneManager.GetActiveScene().name;
+ }
+
+ public static int GetCurrentSceneIndex()
+ {
+ return UnityEngine.SceneManagement.SceneManager.GetActiveScene().buildIndex;
+ }
+
+ public static bool IsSceneInBuildSettings(string sceneName)
+ {
+ for (var n = 0; n < UnityEngine.SceneManagement.SceneManager.sceneCountInBuildSettings; ++n)
+ {
+ var scenePath = UnityEngine.SceneManagement.SceneUtility.GetScenePathByBuildIndex(n);
+ if (string.IsNullOrEmpty(scenePath)) continue;
+ if (string.Equals(System.IO.Path.GetFileNameWithoutExtension(scenePath), sceneName, System.StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private static void SceneManagerOrAddressablesLoadScene(string sceneName)
+ {
+ if (IsSceneInBuildSettings(sceneName))
+ {
+ UnityEngine.SceneManagement.SceneManager.LoadScene(sceneName);
+ return;
+ }
+#if USE_ADDRESSABLES
+ // If not in build settings, try loading an Addressable scene:
+ m_currentAsyncOperationHandle = UnityEngine.AddressableAssets.Addressables.LoadSceneAsync(sceneName);
+#else
+ Debug.LogError("Can't load scene. Scene is not in build settings: " + sceneName);
+#endif
+ }
+
+ private static void SceneManagerOrAddressablesLoadSceneAsync(string sceneName)
+ {
+ m_currentAsyncOperation = null;
+ if (IsSceneInBuildSettings(sceneName))
+ {
+ m_currentAsyncOperation = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(sceneName);
+ return;
+ }
+#if USE_ADDRESSABLES
+ // If not in build settings, try loading an Addressable scene:
+ m_currentAsyncOperationHandle = UnityEngine.AddressableAssets.Addressables.LoadSceneAsync(sceneName);
+#else
+ Debug.LogError("Can't load scene. Scene is not in build settings: " + sceneName);
+#endif
+ }
+
+ private static IEnumerator SceneManagerOrAddressablesLoadSceneAdditiveAsync(string sceneName)
+ {
+ if (IsSceneInBuildSettings(sceneName))
+ {
+ yield return UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(sceneName, UnityEngine.SceneManagement.LoadSceneMode.Additive);
+ }
+ else
+ {
+#if USE_ADDRESSABLES
+ // If not in build settings, try loading an Addressable scene:
+ m_currentAsyncOperationHandle = UnityEngine.AddressableAssets.Addressables.LoadSceneAsync(sceneName, UnityEngine.SceneManagement.LoadSceneMode.Additive);
+ while (!m_currentAsyncOperation.isDone)
+ {
+ yield return null;
+ }
+#else
+ Debug.LogError("Can't load additive scene. Scene is not in build settings: " + sceneName);
+#endif
+ }
+ }
+
+ private static IEnumerator LoadSceneInternal(string sceneName, SceneValidationMode sceneValidationMode)
+ {
+ m_addedScenes.Clear();
+ if (sceneTransitionManager == null)
+ {
+ if (sceneName.StartsWith("index:"))
+ {
+ var index = SafeConvert.ToInt(sceneName.Substring("index:".Length));
+ UnityEngine.SceneManagement.SceneManager.LoadScene(index);
+ }
+ else
+ {
+ if (validateNameScene != null) sceneName = validateNameScene(sceneName, sceneValidationMode);
+ if (string.IsNullOrEmpty(sceneName))
+ {
+ if (debug) Debug.LogWarning("Scene '" + sceneName + "' is not a valid scene to load.");
+ yield break;
+ }
+ SceneManagerOrAddressablesLoadScene(sceneName);
+ }
+ yield break;
+ }
+ else
+ {
+ yield return instance.StartCoroutine(LoadSceneInternalTransitionCoroutine(sceneName, sceneValidationMode));
+ }
+ }
+
+ private static IEnumerator LoadSceneInternalTransitionCoroutine(string sceneName, SceneValidationMode sceneValidationMode)
+ {
+ m_addedScenes.Clear();
+ yield return instance.StartCoroutine(sceneTransitionManager.LeaveScene());
+ if (sceneName.StartsWith("index:"))
+ {
+ var index = SafeConvert.ToInt(sceneName.Substring("index:".Length));
+ m_currentAsyncOperation = UnityEngine.SceneManagement.SceneManager.LoadSceneAsync(index);
+ }
+ else
+ {
+ if (validateNameScene != null) sceneName = validateNameScene(sceneName, sceneValidationMode);
+ if (string.IsNullOrEmpty(sceneName))
+ {
+ if (debug) Debug.LogWarning("Scene '" + sceneName + "' is not a valid scene to load.");
+ yield break;
+ }
+ SceneManagerOrAddressablesLoadSceneAsync(sceneName);
+ }
+ if (m_currentAsyncOperation != null)
+ {
+ while (m_currentAsyncOperation != null && !m_currentAsyncOperation.isDone)
+ {
+ sceneTransitionManager.OnLoading(m_currentAsyncOperation.progress);
+ yield return null;
+ }
+ }
+#if USE_ADDRESSABLES
+ else
+ {
+ while (!m_currentAsyncOperationHandle.IsDone)
+ {
+ sceneTransitionManager.OnLoading(m_currentAsyncOperationHandle.PercentComplete);
+ yield return null;
+ }
+ }
+#endif
+ sceneTransitionManager.OnLoading(1);
+ m_currentAsyncOperation = null;
+ instance.StartCoroutine(sceneTransitionManager.EnterScene());
+ }
+
+ public static IEnumerator LoadAdditiveSceneInternal(string sceneName, SceneValidationMode sceneValidationMode)
+ {
+ if (validateNameScene != null) sceneName = validateNameScene(sceneName, sceneValidationMode);
+ if (string.IsNullOrEmpty(sceneName)) yield break;
+ yield return SceneManagerOrAddressablesLoadSceneAdditiveAsync(sceneName);
+ var scene = UnityEngine.SceneManagement.SceneManager.GetSceneByName(sceneName);
+ if (!scene.IsValid()) yield break;
+ var rootGOs = scene.GetRootGameObjects();
+ for (int i = 0; i < rootGOs.Length; i++)
+ {
+ RecursivelyApplySavers(rootGOs[i].transform);
+ }
+ }
+
+ public static void UnloadAdditiveSceneInternal(string sceneName)
+ {
+#if UNITY_5_3 || UNITY_5_4
+ UnityEngine.SceneManagement.SceneManager.UnloadScene(sceneName);
+#else
+ var scene = UnityEngine.SceneManagement.SceneManager.GetSceneByName(sceneName);
+ if (scene.IsValid())
+ {
+ var rootGOs = scene.GetRootGameObjects();
+ for (int i = 0; i < rootGOs.Length; i++)
+ {
+ var rootGO = rootGOs[i].transform;
+ RecursivelyRecordSavers(rootGO, scene.buildIndex);
+ RecursivelyInformBeforeSceneChange(rootGO);
+ }
+ }
+ UnityEngine.SceneManagement.SceneManager.UnloadSceneAsync(sceneName);
+#endif
+ }
+
+ ///
+ /// Records the data of all saver components on the transform and its children.
+ ///
+ public static void RecursivelyRecordSavers(Transform t, int sceneIndex)
+ {
+ if (t == null) return;
+ var saver = t.GetComponent();
+ if (saver != null) currentSavedGameData.SetData(saver.key, saver.saveAcrossSceneChanges ? -1 : sceneIndex, saver.RecordData());
+ foreach (Transform child in t)
+ {
+ RecursivelyRecordSavers(child, sceneIndex);
+ }
+ }
+
+ ///
+ /// Tells all saver components on the transform and its children to retrieve their states from the current saved game data.
+ ///
+ ///
+ public static void RecursivelyApplySavers(Transform t)
+ {
+ if (t == null) return;
+ var saver = t.GetComponent();
+ if (saver != null) saver.ApplyData(currentSavedGameData.GetData(saver.key));
+ foreach (Transform child in t)
+ {
+ RecursivelyApplySavers(child);
+ }
+ }
+
+ ///
+ /// Calls BeforeSceneChange on all saver components on the transform and its children.
+ /// Used when unloading an additive scene.
+ ///
+ ///
+ public static void RecursivelyInformBeforeSceneChange(Transform t)
+ {
+ if (t == null) return;
+ var saver = t.GetComponent();
+ if (saver != null) saver.OnBeforeSceneChange();
+ foreach (Transform child in t)
+ {
+ RecursivelyInformBeforeSceneChange(child);
+ }
+ }
+
+#else
+
+ public static string GetCurrentSceneName()
+ {
+ return Application.loadedLevelName;
+ }
+
+ public static int GetCurrentSceneIndex()
+ {
+ return Application.loadedLevel;
+ }
+
+ private static IEnumerator LoadSceneInternal(string sceneName)
+ {
+ Application.LoadLevel(sceneName);
+ yield break;
+ }
+
+ public static IEnumerator LoadAdditiveSceneInternal(string sceneName)
+ {
+ yield return Application.LoadLevelAdditiveAsync(sceneName);
+ }
+
+ public static void UnloadAdditiveSceneInternal(string sceneName)
+ {
+ Application.UnloadLevel(sceneName);
+ }
+#endif
+
+ ///
+ /// If slotNumber is negative and allowNegativeSlotNumbers is false,
+ /// choose an empty positive slot up to maxSlots. If none are empty,
+ /// return false;
+ ///
+ private static bool SanitizeSlotNumberForSave(int slotNumber, out int sanitizedSlotNumber)
+ {
+ if (slotNumber >= 0 || m_instance == null || m_instance.allowNegativeSlotNumbers)
+ {
+ sanitizedSlotNumber = slotNumber;
+ return true;
+ }
+ for (int i = 0; i <= maxSaveSlot; i++)
+ {
+ if (!HasSavedGameInSlot(i))
+ {
+ sanitizedSlotNumber = i;
+ return true;
+ }
+ }
+ sanitizedSlotNumber = 0;
+ return false;
+ }
+
+ ///
+ /// Saves a game into a slot using the storage provider on the
+ /// Save System GameObject.
+ ///
+ /// Slot in which to store saved game data.
+ public void SaveGameToSlot(int slotNumber)
+ {
+ SaveToSlot(slotNumber);
+ }
+
+ ///
+ /// Loads a game from a slot using the storage provider on the
+ /// Save System GameObject.
+ ///
+ ///
+ public void LoadGameFromSlot(int slotNumber)
+ {
+ LoadFromSlot(slotNumber);
+ }
+
+ ///
+ /// Loads a scene, optionally positioning the player at a
+ /// specified spawnpoint.
+ ///
+ ///
+ /// A string containing the name of the scene to load, optionally
+ /// followed by "@spawnpoint" where "spawnpoint" is the name of
+ /// a GameObject in that scene. The player will be spawned at that
+ /// GameObject's position.
+ ///
+ public void LoadSceneAtSpawnpoint(string sceneNameAndSpawnpoint)
+ {
+ LoadScene(sceneNameAndSpawnpoint);
+ }
+
+ ///
+ /// Returns true if there is a saved game in the specified slot.
+ ///
+ public static bool HasSavedGameInSlot(int slotNumber)
+ {
+ return storer.HasDataInSlot(slotNumber);
+ }
+
+ ///
+ /// Deletes the saved game in the specified slot.
+ ///
+ public static void DeleteSavedGameInSlot(int slotNumber)
+ {
+ storer.DeleteSavedGameData(slotNumber);
+ }
+
+ ///
+ /// Saves the current game to a slot.
+ ///
+ public static void SaveToSlot(int slotNumber)
+ {
+ instance.StartCoroutine(SaveToSlotCoroutine(slotNumber));
+ }
+
+ private static IEnumerator SaveToSlotCoroutine(int slotNumber)
+ {
+ if (!SanitizeSlotNumberForSave(slotNumber, out slotNumber))
+ {
+ Debug.LogError("Can't save game. Invalid save slot: " + slotNumber);
+ yield break;
+ }
+ saveStarted();
+ yield return null;
+ PlayerPrefs.SetInt(LastSavedGameSlotPlayerPrefsKey, slotNumber);
+ yield return storer.StoreSavedGameDataAsync(slotNumber, RecordSavedGameData());
+ saveEnded();
+ }
+
+ ///
+ /// Saves the current game to a slot synchronously and immediately.
+ ///
+ public static void SaveToSlotImmediate(int slotNumber)
+ {
+ if (!SanitizeSlotNumberForSave(slotNumber, out slotNumber))
+ {
+ Debug.LogError("Can't save game. Invalid save slot: " + slotNumber);
+ return;
+ }
+ saveStarted();
+ PlayerPrefs.SetInt(LastSavedGameSlotPlayerPrefsKey, slotNumber);
+ storer.StoreSavedGameData(slotNumber, RecordSavedGameData());
+ saveEnded();
+ }
+
+ ///
+ /// Loads a game from a slot.
+ ///
+ public static void LoadFromSlot(int slotNumber)
+ {
+ if (!HasSavedGameInSlot(slotNumber))
+ {
+ if (Debug.isDebugBuild) Debug.LogWarning("Save System: LoadFromSlot(" + slotNumber + ") but there is no saved game in this slot.");
+ return;
+ }
+ if (loadStarted.GetInvocationList().Length > 1)
+ {
+ instance.StartCoroutine(LoadFromSlotCoroutine(slotNumber));
+ }
+ else
+ {
+ LoadFromSlotNow(slotNumber);
+ }
+ }
+
+ private static IEnumerator LoadFromSlotCoroutine(int slotNumber)
+ {
+ loadStarted();
+ yield return null;
+ LoadFromSlotNow(slotNumber);
+ }
+
+ private static void NotifyLoadEndedWhenSceneLoaded(string sceneName, int sceneIndex)
+ {
+ sceneLoaded -= NotifyLoadEndedWhenSceneLoaded;
+ loadEnded();
+ }
+
+ private static void LoadFromSlotNow(int slotNumber)
+ {
+ sceneLoaded += NotifyLoadEndedWhenSceneLoaded;
+ LoadGame(storer.RetrieveSavedGameData(slotNumber));
+ }
+
+ public static void RegisterSaver(Saver saver)
+ {
+ if (saver == null || m_savers.Contains(saver)) return;
+ m_savers.Add(saver);
+ }
+
+ public static void UnregisterSaver(Saver saver)
+ {
+ m_savers.Remove(saver);
+ }
+
+ ///
+ /// Clears the SaveSystem's internal saved game data cache.
+ ///
+ public static void ClearSavedGameData()
+ {
+ m_savedGameData = new SavedGameData();
+ }
+
+ ///
+ /// Records the current scene's savers' data into the SaveSystem's
+ /// internal saved game data cache.
+ ///
+ ///
+ public static SavedGameData RecordSavedGameData()
+ {
+ m_savedGameData.version = version;
+ m_savedGameData.sceneName = GetCurrentSceneName();
+ foreach (var saver in m_savers)
+ {
+ try
+ {
+ m_savedGameData.SetData(saver.key, GetSaverSceneIndex(saver), saver.RecordData());
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogException(e);
+ }
+ }
+ return m_savedGameData;
+ }
+
+ private static int GetSaverSceneIndex(Saver saver)
+ {
+ return (saver == null || !saver.saveAcrossSceneChanges) ? currentSceneIndex : NoSceneIndex;
+ }
+
+ ///
+ /// Updates the SaveSystem's internal saved game data cache with data for a
+ /// specific saver.
+ ///
+ ///
+ ///
+ public static void UpdateSaveData(Saver saver, string data)
+ {
+ m_savedGameData.SetData(saver.key, GetSaverSceneIndex(saver), data);
+ }
+
+ ///
+ /// Applies the saved game data to the savers in the current scene.
+ ///
+ /// Saved game data.
+ public static void ApplySavedGameData(SavedGameData savedGameData)
+ {
+ if (savedGameData != null)
+ {
+ m_savedGameData = savedGameData;
+ if (m_savers.Count > 0)
+ {
+ m_tmpSavers.Clear();
+ m_tmpSavers.AddRange(m_savers); // Make a copy in case a saver ends up removing multiple savers.
+ for (int i = m_tmpSavers.Count - 1; i >= 0; i--) // A saver may remove itself from list during apply.
+ {
+ try
+ {
+ if (0 <= i && i < m_tmpSavers.Count)
+ {
+ var saver = m_tmpSavers[i];
+ if (saver != null) saver.ApplyData(savedGameData.GetData(saver.key));
+ }
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogException(e);
+ }
+ }
+ }
+ }
+ if (framesToWaitBeforeSaveDataAppliedEvent == 0 || instance == null)
+ {
+ saveDataApplied();
+ }
+ else
+ {
+ instance.StartCoroutine(DelayedSaveDataAppliedCoroutine(framesToWaitBeforeSaveDataAppliedEvent));
+ framesToWaitBeforeSaveDataAppliedEvent = 0;
+ }
+ }
+
+ protected static IEnumerator DelayedSaveDataAppliedCoroutine(int frames)
+ {
+ for (int i = 0; i < frames; i++)
+ {
+ yield return null;
+ }
+ yield return CoroutineUtility.endOfFrame;
+ saveDataApplied();
+ }
+
+ ///
+ /// Applies the most recently recorded saved game data.
+ ///
+ public static void ApplySavedGameData()
+ {
+ ApplySavedGameData(m_savedGameData);
+ }
+
+ ///
+ /// If changing scenes manually, calls before changing scenes to inform components
+ /// that listen for OnDestroy messages that they're being destroyed because of the
+ /// scene change.
+ ///
+ public static void BeforeSceneChange()
+ {
+ // Notify savers:
+ foreach (var saver in m_savers)
+ {
+ try
+ {
+ saver.OnBeforeSceneChange();
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogException(e);
+ }
+ }
+ // Notify SceneNotifier:
+ try
+ {
+ SceneNotifier.NotifyWillUnloadScene(m_currentSceneIndex);
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogException(e);
+ }
+ }
+
+ ///
+ /// Loads the scene recorded in the saved game data (if saveCurrentScene is true) and
+ /// applies the saved game data to it.
+ ///
+ ///
+ public static void LoadGame(SavedGameData savedGameData)
+ {
+ if (savedGameData == null)
+ {
+ if (Debug.isDebugBuild) Debug.LogWarning("SaveSystem.LoadGame received null saved game data. Not loading.");
+ }
+ else if (saveCurrentScene)
+ {
+ instance.StartCoroutine(LoadSceneCoroutine(savedGameData, null, SceneValidationMode.LoadingSavedGame));
+ }
+ else
+ {
+ ApplySavedGameData(savedGameData);
+ }
+ }
+
+ ///
+ /// Loads a scene, optionally moving the player to a specified spawnpoint.
+ /// If the scene name starts with "index:" followed by an index number, this
+ /// method loads the scene by build index number.
+ ///
+ /// Scene name, followed by an optional spawnpoint separated by '@'.
+ public static void LoadScene(string sceneNameAndSpawnpoint)
+ {
+ if (string.IsNullOrEmpty(sceneNameAndSpawnpoint)) return;
+ string sceneName = sceneNameAndSpawnpoint;
+ string spawnpointName = string.Empty;
+ if (sceneNameAndSpawnpoint.Contains("@"))
+ {
+ var strings = sceneNameAndSpawnpoint.Split('@');
+ sceneName = strings[0];
+ spawnpointName = (strings.Length > 1) ? strings[1] : null;
+ }
+ var savedGameData = RecordSavedGameData();
+ savedGameData.sceneName = sceneName;
+ instance.StartCoroutine(LoadSceneCoroutine(savedGameData, spawnpointName, SceneValidationMode.LoadingScene));
+ }
+
+ private static IEnumerator LoadSceneCoroutine(SavedGameData savedGameData, string spawnpointName, SceneValidationMode sceneValidationMode)
+ {
+ if (savedGameData == null) yield break;
+ if (debug) Debug.Log("Save System: Loading scene " + savedGameData.sceneName +
+ (string.IsNullOrEmpty(spawnpointName) ? string.Empty : " [spawn at " + spawnpointName + "]"));
+ m_savedGameData = savedGameData;
+ BeforeSceneChange();
+ if (autoUnloadAdditiveScenes) UnloadAllAdditiveScenes();
+ yield return LoadSceneInternal(savedGameData.sceneName, sceneValidationMode);
+ ApplyDataImmediate();
+ // Allow other scripts to spin up scene first:
+ for (int i = 0; i < framesToWaitBeforeApplyData; i++)
+ {
+ yield return null;
+ }
+ yield return CoroutineUtility.endOfFrame;
+ m_playerSpawnpoint = !string.IsNullOrEmpty(spawnpointName) ? GameObject.Find(spawnpointName) : null;
+ if (!string.IsNullOrEmpty(spawnpointName) && m_playerSpawnpoint == null) Debug.LogWarning("Save System: Can't find spawnpoint '" + spawnpointName + "'. Is spelling and capitalization correct?");
+ ApplySavedGameData(savedGameData);
+ }
+
+ // Calls ApplyDataImmediate on all savers.
+ private static void ApplyDataImmediate()
+ {
+ if (m_savers.Count > 0)
+ {
+ m_tmpSavers.Clear();
+ m_tmpSavers.AddRange(m_savers); // Make a copy in case a saver ends up removing multiple savers.
+ for (int i = m_tmpSavers.Count - 1; i >= 0; i--) // A saver may remove itself from list during apply.
+ {
+ try
+ {
+ if (0 <= i && i < m_tmpSavers.Count)
+ {
+ var saver = m_tmpSavers[i];
+ if (saver != null) saver.ApplyDataImmediate();
+ }
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogException(e);
+ }
+ }
+ }
+ }
+
+ private void FinishedLoadingScene(string sceneName, int sceneIndex)
+ {
+ m_currentSceneIndex = sceneIndex;
+ if (!m_isLoadingAdditiveScene)
+ { // Don't delete other non-cross-scene data if loading additive scene:
+ m_savedGameData.DeleteObsoleteSaveData(sceneIndex);
+ }
+ m_isLoadingAdditiveScene = false;
+ sceneLoaded(sceneName, sceneIndex);
+ }
+
+ ///
+ /// Additively loads another scene.
+ ///
+ /// Scene to additively load.
+ public static void LoadAdditiveScene(string sceneName)
+ {
+ if (string.IsNullOrEmpty(sceneName) || m_addedScenes.Contains(sceneName)) return;
+ m_addedScenes.Add(sceneName);
+ instance.m_isLoadingAdditiveScene = true;
+ instance.StartCoroutine(LoadAdditiveSceneInternal(sceneName, SceneValidationMode.LoadingScene));
+ }
+
+ ///
+ /// Unloads a previously additively-loaded scene.
+ ///
+ /// Scene to unload
+ public static void UnloadAdditiveScene(string sceneName)
+ {
+ if (!m_addedScenes.Contains(sceneName)) return;
+ m_addedScenes.Remove(sceneName);
+ UnloadAdditiveSceneInternal(sceneName);
+ }
+
+ ///
+ /// Unloads all previously additively-loaded scenes.
+ ///
+ public static void UnloadAllAdditiveScenes()
+ {
+ for (int i = m_addedScenes.Count - 1; i >= 0; i--)
+ {
+ UnloadAdditiveScene(m_addedScenes[i]);
+ }
+ }
+
+ ///
+ /// Clears the SaveSystem's saved game data cache and loads a
+ /// starting scene. Same as ResetGameState except loads a starting scene.
+ ///
+ ///
+ public static void RestartGame(string startingSceneName)
+ {
+ ResetGameState();
+ instance.StartCoroutine(LoadSceneInternal(startingSceneName, SceneValidationMode.RestartingGame));
+ }
+
+ ///
+ /// Clears the SaveSystem's saved game data cache. Same as
+ /// RestartGame except it doesn't load a scene after resetting.
+ ///
+ ///
+ public static void ResetGameState()
+ {
+ ClearSavedGameData();
+ BeforeSceneChange();
+ SaversRestartGame();
+ }
+
+ ///
+ /// Calls OnRestartGame on all savers.
+ ///
+ public static void SaversRestartGame()
+ {
+ if (m_savers.Count <= 0) return;
+ foreach (var saver in m_savers.ToList()) // A saver may remove itself from list during restart.
+ {
+ try
+ {
+ if (saver != null) saver.OnRestartGame();
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogException(e);
+ }
+ }
+ }
+
+ ///
+ /// Returns a serialized version of an object using whatever serializer is
+ /// assigned to the SaveSystem (JSON by default).
+ ///
+ public static string Serialize(object data)
+ {
+ return serializer.Serialize(data);
+ }
+
+ ///
+ /// Deserializes a previously-serialized string representation of an object
+ /// back into an object. Uses whatever serializer is assigned to the
+ /// SaveSystem (JSON by default).
+ ///
+ /// The type of the object.
+ /// The object's serialized data.
+ /// Optional preallocated object to serialize data into.
+ /// The deserialized object, or null if it couldn't be deserialized.
+ public static T Deserialize(string s, T data = default(T))
+ {
+ return serializer.Deserialize(s, data);
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/SaveSystem.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/SaveSystem.cs.meta
new file mode 100644
index 000000000..858ac2ea6
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/SaveSystem.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: bd92d324c63216247876919e3d26dcd9
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: -6
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/SaveSystem.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers.meta
new file mode 100644
index 000000000..7b3844087
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 452f3d2a69e08604cbb049c1db4c3f25
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/ActiveSaver.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/ActiveSaver.cs
new file mode 100644
index 000000000..0250f8614
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/ActiveSaver.cs
@@ -0,0 +1,71 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Saves the active/inactive state of a GameObject. This component should be
+ /// on a different GameObject that's guaranteed to be active, or it won't
+ /// take effect. When applying data (i.e., setting active/inactive state), if
+ /// it activates an inactive target, it will call ApplyData on the target's
+ /// other savers.
+ ///
+ [AddComponentMenu("")]
+ public class ActiveSaver : Saver
+ {
+
+ [Serializable]
+ public class Data
+ {
+ public bool active;
+ }
+
+ [Tooltip("GameObject to watch.")]
+ [SerializeField]
+ private GameObject m_gameObjectToWatch;
+
+ public GameObject gameObjectToWatch
+ {
+ get { return m_gameObjectToWatch; }
+ set { m_gameObjectToWatch = value; }
+ }
+
+ private Data m_data = new Data();
+
+ public override string RecordData()
+ {
+ var value = (gameObjectToWatch != null) ? gameObjectToWatch.activeSelf : false;
+ m_data.active = value;
+ return SaveSystem.Serialize(m_data);
+ }
+
+ public override void ApplyData(string s)
+ {
+ if (gameObjectToWatch == null || string.IsNullOrEmpty(s)) return;
+ var data = SaveSystem.Deserialize(s, m_data);
+ if (data == null) return;
+ m_data = data;
+ var applyDataToOtherSavers = data.active && !gameObjectToWatch.activeSelf;
+ if (!data.active)
+ {
+ gameObjectToWatch.BroadcastMessage("OnBeforeSceneChange", SendMessageOptions.DontRequireReceiver);
+ gameObjectToWatch.BroadcastMessage("OnLevelWillBeUnloaded", SendMessageOptions.DontRequireReceiver);
+ }
+ gameObjectToWatch.SetActive(data.active);
+ if (applyDataToOtherSavers)
+ {
+ var savers = gameObjectToWatch.GetComponentsInChildren();
+ for (int i = 0; i < savers.Length; i++)
+ {
+ var saver = savers[i];
+ if (saver == this || !saver.enabled) continue;
+ saver.ApplyData(SaveSystem.currentSavedGameData.GetData(saver.key));
+ }
+ }
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/ActiveSaver.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/ActiveSaver.cs.meta
new file mode 100644
index 000000000..d707b9c59
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/ActiveSaver.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 1bc28b9bf84127e4a92af7029673b6e3
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/ActiveSaver.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/AnimatorSaver.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/AnimatorSaver.cs
new file mode 100644
index 000000000..ed034a15b
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/AnimatorSaver.cs
@@ -0,0 +1,199 @@
+using UnityEngine;
+using System;
+using System.Collections.Generic;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Saves an animator's state.
+ ///
+ [AddComponentMenu("")] // Use wrapper.
+ [RequireComponent(typeof(Animator))]
+ public class AnimatorSaver : Saver
+ {
+
+ [Serializable]
+ public class LayerData
+ {
+ public int hash;
+ public float time;
+ }
+
+ // Credit: Trigger saving contributed by Magique.
+ [Serializable]
+ public class TriggerData
+ {
+ public string name;
+ public bool isTriggered;
+ }
+
+ [Serializable]
+ public class Data
+ {
+ public LayerData[] layers = null;
+ public List bools = new List();
+ public List floats = new List();
+ public List ints = new List();
+ public List strings = new List();
+ public List triggers = new List();
+ }
+
+ private Data m_data = new Data();
+ private Animator m_animator;
+ private Animator animator
+ {
+ get
+ {
+ if (m_animator == null) m_animator = GetComponent();
+ return m_animator;
+ }
+ }
+
+ private void CheckAnimator()
+ {
+ if (animator == null) return;
+ if (m_data == null) m_data = new Data();
+ if (m_data.layers == null || m_data.layers.Length != animator.layerCount)
+ {
+ m_data.layers = new LayerData[animator.layerCount];
+ for (int i = 0; i < animator.layerCount; i++)
+ {
+ m_data.layers[i] = new LayerData();
+ }
+ }
+ }
+
+ public override string RecordData()
+ {
+ if (animator == null) return string.Empty;
+ CheckAnimator();
+
+ // Record layer states:
+ for (int i = 0; i < animator.layerCount; i++)
+ {
+ var state = animator.GetCurrentAnimatorStateInfo(i);
+ m_data.layers[i].hash = state.fullPathHash;
+ m_data.layers[i].time = state.normalizedTime;
+ }
+
+ // Record parameter values:
+ int numBools = 0;
+ int numFloats = 0;
+ int numInts = 0;
+ for (int i = 0; i < animator.parameterCount; i++)
+ {
+ var parameter = animator.parameters[i];
+ switch (parameter.type)
+ {
+ case AnimatorControllerParameterType.Bool:
+ var boolValue = animator.GetBool(parameter.name);
+ if (numBools < m_data.bools.Count)
+ {
+ m_data.bools[numBools] = boolValue;
+ }
+ else
+ {
+ m_data.bools.Add(boolValue);
+ }
+ numBools++;
+ break;
+ case AnimatorControllerParameterType.Float:
+ var floatValue = animator.GetFloat(parameter.name);
+ if (numFloats < m_data.floats.Count)
+ {
+ m_data.floats[numFloats] = floatValue;
+ }
+ else
+ {
+ m_data.floats.Add(floatValue);
+ }
+ numFloats++;
+ break;
+ case AnimatorControllerParameterType.Int:
+ var intValue = animator.GetInteger(parameter.name);
+ if (numInts < m_data.ints.Count)
+ {
+ m_data.ints[numInts] = intValue;
+ }
+ else
+ {
+ m_data.ints.Add(intValue);
+ }
+ numInts++;
+ break;
+ case AnimatorControllerParameterType.Trigger:
+ var triggerValue = animator.GetCurrentAnimatorStateInfo(0).IsName(parameter.name);
+ m_data.triggers.Add(new TriggerData() { isTriggered = triggerValue, name = parameter.name });
+ break;
+ }
+ }
+ return SaveSystem.Serialize(m_data);
+ }
+
+ public override void ApplyData(string s)
+ {
+ if (string.IsNullOrEmpty(s) || animator == null) return;
+ m_data = SaveSystem.Deserialize(s, m_data);
+ if (m_data == null)
+ {
+ m_data = new Data();
+ }
+ else if (m_data.layers != null)
+ {
+ // Apply layer states:
+ for (int i = 0; i < animator.layerCount; i++)
+ {
+ if (i < m_data.layers.Length)
+ {
+ animator.Play(m_data.layers[i].hash, i, m_data.layers[i].time);
+ }
+ }
+
+ // Set or Reset triggers
+ foreach (var trigger in m_data.triggers)
+ {
+ if (trigger.isTriggered)
+ {
+ animator.SetTrigger(trigger.name);
+ }
+ else
+ {
+ animator.ResetTrigger(trigger.name);
+ }
+ }
+
+ // Apply parameter values:
+ int numBools = 0;
+ int numFloats = 0;
+ int numInts = 0;
+ for (int i = 0; i < animator.parameterCount; i++)
+ {
+ var parameter = animator.parameters[i];
+ switch (parameter.type)
+ {
+ case AnimatorControllerParameterType.Bool:
+ if (numBools < m_data.bools.Count)
+ {
+ animator.SetBool(parameter.name, m_data.bools[numBools++]);
+ }
+ break;
+ case AnimatorControllerParameterType.Float:
+ if (numFloats < m_data.floats.Count)
+ {
+ animator.SetFloat(parameter.name, m_data.floats[numFloats++]);
+ }
+ break;
+ case AnimatorControllerParameterType.Int:
+ if (numInts < m_data.ints.Count)
+ {
+ animator.SetInteger(parameter.name, m_data.ints[numInts++]);
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/AnimatorSaver.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/AnimatorSaver.cs.meta
new file mode 100644
index 000000000..013e4d19b
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/AnimatorSaver.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 73e91a3a10ccd3348ab26a2dd6ff78d3
+timeCreated: 1534295365
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/AnimatorSaver.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/DestructibleSaver.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/DestructibleSaver.cs
new file mode 100644
index 000000000..201f57b26
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/DestructibleSaver.cs
@@ -0,0 +1,124 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Saves when a GameObject has been destroyed or disabled. The next time the game
+ /// or scene is loaded, if the GameObject has previously been destroyed/disabled, this
+ /// script will destroy/deactivate it again. It will also spawn a replacement destroyed
+ /// version if a prefab is assigned.
+ ///
+ [AddComponentMenu("")] // Use wrapper.
+ public class DestructibleSaver : Saver
+ {
+
+ [Serializable]
+ public class DestructibleData
+ {
+ public bool destroyed = false;
+ public Vector3 position;
+ }
+
+ public enum Mode { OnDisable, OnDestroy }
+
+ public enum DestroyMode { Destroy, Deactivate }
+
+ [Tooltip("Event to watch for.")]
+ [SerializeField]
+ private Mode m_mode = Mode.OnDestroy;
+
+ [Tooltip("How to re-destroy object.")]
+ [SerializeField]
+ private DestroyMode m_destroyMode = DestroyMode.Destroy;
+
+ [Tooltip("Instantiate this if already destroyed when loading game or scene.")]
+ [SerializeField]
+ private GameObject m_destroyedVersionPrefab;
+
+ private DestructibleData m_data = new DestructibleData();
+ private bool m_ignoreOnDestroy = false;
+
+ public Mode mode
+ {
+ get { return m_mode; }
+ set { m_mode = value; }
+ }
+
+ public DestroyMode destroyMode
+ {
+ get { return m_destroyMode; }
+ set { m_destroyMode = value; }
+ }
+
+ public GameObject destroyedVersionPrefab
+ {
+ get { return m_destroyedVersionPrefab; }
+ set { m_destroyedVersionPrefab = value; }
+ }
+
+ public override void OnBeforeSceneChange()
+ {
+ base.OnBeforeSceneChange();
+ m_ignoreOnDestroy = true;
+ }
+
+ public override void OnDisable()
+ {
+ base.OnDisable();
+ if (m_mode != Mode.OnDisable) return;
+ RecordDestruction();
+ }
+
+ public override void OnDestroy()
+ {
+ base.OnDestroy();
+ if (m_mode != Mode.OnDestroy) return;
+ RecordDestruction();
+ }
+
+ public virtual void RecordDestruction()
+ {
+ if (!m_ignoreOnDestroy && SaveSystem.instance != null)
+ {
+ m_data.destroyed = true;
+ m_data.position = transform.position;
+ SaveSystem.UpdateSaveData(this, SaveSystem.Serialize(m_data));
+ }
+ m_ignoreOnDestroy = false;
+ }
+
+ public override string RecordData()
+ {
+ return SaveSystem.Serialize(m_data);
+ }
+
+ public override void ApplyData(string s)
+ {
+ var data = SaveSystem.Deserialize(s, m_data);
+ if (data == null) return;
+ m_data = data;
+ if (data.destroyed)
+ {
+ if (destroyedVersionPrefab != null)
+ {
+ Instantiate(destroyedVersionPrefab, data.position, transform.rotation);
+ }
+ switch (destroyMode)
+ {
+ case DestroyMode.Destroy:
+ Destroy(gameObject);
+ break;
+ case DestroyMode.Deactivate:
+ gameObject.SetActive(false);
+ break;
+ }
+ }
+
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/DestructibleSaver.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/DestructibleSaver.cs.meta
new file mode 100644
index 000000000..90e478756
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/DestructibleSaver.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: d86e4361f56649c48a778fe7a5d81b2f
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/DestructibleSaver.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/EnabledSaver.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/EnabledSaver.cs
new file mode 100644
index 000000000..f699750df
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/EnabledSaver.cs
@@ -0,0 +1,52 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Saves the enabled/disabled state of a component. This component should be
+ /// on a GameObject that's guaranteed to be active, or it won't take effect.
+ ///
+ [AddComponentMenu("")]
+ public class EnabledSaver : Saver
+ {
+
+ [Serializable]
+ public class Data
+ {
+ public bool enabled;
+ }
+
+ [Tooltip("Component to watch.")]
+ [SerializeField]
+ private Component m_componentToWatch;
+
+ public Component componentToWatch
+ {
+ get { return m_componentToWatch; }
+ set { m_componentToWatch = value; }
+ }
+
+ private Data m_data = new Data();
+
+ public override string RecordData()
+ {
+ var value = (componentToWatch != null) ? ComponentUtility.IsComponentEnabled(componentToWatch) : false;
+ m_data.enabled = value;
+ return SaveSystem.Serialize(m_data);
+ }
+
+ public override void ApplyData(string s)
+ {
+ if (componentToWatch == null || string.IsNullOrEmpty(s)) return;
+ var data = SaveSystem.Deserialize(s, m_data);
+ if (data == null) return;
+ m_data = data;
+ ComponentUtility.SetComponentEnabled(componentToWatch, data.enabled);
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/EnabledSaver.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/EnabledSaver.cs.meta
new file mode 100644
index 000000000..06dd8d756
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/EnabledSaver.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: c29a1222f66d16642bc068cfc8713a93
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/EnabledSaver.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiActiveSaver.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiActiveSaver.cs
new file mode 100644
index 000000000..e14571c2f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiActiveSaver.cs
@@ -0,0 +1,88 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Saves the active/inactive state of a list of GameObjects. This component should be
+ /// on a different GameObject that's guaranteed to be active, or it won't
+ /// take effect. When applying data (i.e., setting active/inactive state), if
+ /// it activates an inactive target, it will call ApplyData on the target's
+ /// other savers.
+ ///
+ [AddComponentMenu("")]
+ public class MultiActiveSaver : Saver
+ {
+
+ [Serializable]
+ public class Data
+ {
+ public bool[] active;
+ }
+
+ [Tooltip("GameObjects to watch.")]
+ [SerializeField]
+ private GameObject[] m_gameObjectsToWatch;
+
+ public GameObject[] gameObjectsToWatch
+ {
+ get { return m_gameObjectsToWatch; }
+ set { m_gameObjectsToWatch = value; }
+ }
+
+ private Data m_data = new Data();
+
+ public override string RecordData()
+ {
+ if (gameObjectsToWatch == null) return string.Empty;
+ if (m_data.active == null || m_data.active.Length != gameObjectsToWatch.Length)
+ {
+ m_data.active = new bool[gameObjectsToWatch.Length];
+ }
+ for (int i = 0; i < gameObjectsToWatch.Length; i++)
+ {
+ m_data.active[i] = (gameObjectsToWatch[i] != null) ? gameObjectsToWatch[i].activeSelf : false;
+ }
+ return SaveSystem.Serialize(m_data);
+ }
+
+ public override void ApplyData(string s)
+ {
+ if (gameObjectsToWatch == null || string.IsNullOrEmpty(s)) return;
+ var data = SaveSystem.Deserialize(s, m_data);
+ if (data == null || data.active == null) return;
+ m_data = data;
+ // First issue OnBeforeSceneChange/OnLevelWillBeUnloaded in case targets include nested GOs:
+ for (int i = 0; i < Mathf.Min(data.active.Length, gameObjectsToWatch.Length); i++)
+ {
+ if (gameObjectsToWatch[i] == null) continue;
+ if (!data.active[i])
+ {
+ gameObjectsToWatch[i].BroadcastMessage("OnBeforeSceneChange", SendMessageOptions.DontRequireReceiver);
+ gameObjectsToWatch[i].BroadcastMessage("OnLevelWillBeUnloaded", SendMessageOptions.DontRequireReceiver);
+ }
+ }
+ // Then activate/deactivate:
+ for (int i = 0; i < Mathf.Min(data.active.Length, gameObjectsToWatch.Length); i++)
+ {
+ if (gameObjectsToWatch[i] == null) continue;
+ var applyDataToOtherSavers = data.active[i] && !gameObjectsToWatch[i].activeSelf;
+ gameObjectsToWatch[i].SetActive(data.active[i]);
+ if (applyDataToOtherSavers)
+ {
+ var savers = gameObjectsToWatch[i].GetComponentsInChildren();
+ for (int j = 0; j < savers.Length; j++)
+ {
+ var saver = savers[j];
+ if (saver == this || !saver.enabled) continue;
+ saver.ApplyData(SaveSystem.currentSavedGameData.GetData(saver.key));
+ }
+ }
+ }
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiActiveSaver.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiActiveSaver.cs.meta
new file mode 100644
index 000000000..bf79903e1
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiActiveSaver.cs.meta
@@ -0,0 +1,20 @@
+fileFormatVersion: 2
+guid: ff9fe6172e8ef584ea0ab8b2fa83f244
+timeCreated: 1582118617
+licenseType: Store
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiActiveSaver.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiEnabledSaver.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiEnabledSaver.cs
new file mode 100644
index 000000000..a6e06fd1c
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiEnabledSaver.cs
@@ -0,0 +1,63 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Saves the enabled/disabled state of a list of components. This component
+ /// should not attempt to save the enabled/disabled state of itself.
+ ///
+ [AddComponentMenu("")]
+ public class MultiEnabledSaver : Saver
+ {
+
+ [Serializable]
+ public class Data
+ {
+ public bool[] active;
+ }
+
+ [Tooltip("Components to watch.")]
+ [SerializeField]
+ private Component[] m_componentsToWatch;
+
+ public Component[] componentsToWatch
+ {
+ get { return m_componentsToWatch; }
+ set { m_componentsToWatch = value; }
+ }
+
+ private Data m_data = new Data();
+
+ public override string RecordData()
+ {
+ if (componentsToWatch == null) return string.Empty;
+ if (m_data.active == null || m_data.active.Length != componentsToWatch.Length)
+ {
+ m_data.active = new bool[componentsToWatch.Length];
+ }
+ for (int i = 0; i < componentsToWatch.Length; i++)
+ {
+ m_data.active[i] = (componentsToWatch[i] != null) ? ComponentUtility.IsComponentEnabled(componentsToWatch[i]) : false;
+ }
+ return SaveSystem.Serialize(m_data);
+ }
+
+ public override void ApplyData(string s)
+ {
+ if (componentsToWatch == null || string.IsNullOrEmpty(s)) return;
+ var data = SaveSystem.Deserialize(s, m_data);
+ if (data == null || data.active == null) return;
+ m_data = data;
+ for (int i = 0; i < Mathf.Min(data.active.Length, componentsToWatch.Length); i++)
+ {
+ if (componentsToWatch[i] == null) continue;
+ ComponentUtility.SetComponentEnabled(componentsToWatch[i], data.active[i]);
+ }
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiEnabledSaver.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiEnabledSaver.cs.meta
new file mode 100644
index 000000000..c8800d1fd
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiEnabledSaver.cs.meta
@@ -0,0 +1,20 @@
+fileFormatVersion: 2
+guid: dfdcd3774181ce140b7625a85fc547c1
+timeCreated: 1618082413
+licenseType: Store
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/MultiEnabledSaver.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/PositionSaver.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/PositionSaver.cs
new file mode 100644
index 000000000..5cb80447c
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/PositionSaver.cs
@@ -0,0 +1,174 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEngine.SceneManagement;
+using System;
+using System.Collections.Generic;
+#if USE_NAVMESH
+using UnityEngine.AI;
+#endif
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Saves a GameObject's position.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class PositionSaver : Saver
+ {
+
+ [Tooltip("If set, save position of target. Otherwise save this GameObject's position.")]
+ [SerializeField]
+ private Transform m_target = null;
+
+ [Tooltip("When changing scenes, if a player spawnpoint is specified, move this GameObject to the spawnpoint.")]
+ [SerializeField]
+ private bool m_usePlayerSpawnpoint = false;
+
+ [Tooltip("Record positions in every scene. If unticked, only records position in most recent scene.")]
+ [SerializeField]
+ private bool m_multiscene = false;
+
+ [Serializable]
+ public class PositionData
+ {
+ public int scene = -1;
+ public Vector3 position;
+ public Quaternion rotation;
+ }
+
+ [Serializable]
+ public class ScenePositionData
+ {
+ public int scene;
+ public Vector3 position;
+ public Quaternion rotation;
+ public ScenePositionData(int _scene, Vector3 _position, Quaternion _rotation)
+ {
+ scene = _scene;
+ position = _position;
+ rotation = _rotation;
+ }
+ }
+
+ [Serializable]
+ public class MultiscenePositionData
+ {
+ public List positions = new List();
+ }
+
+ protected PositionData m_data;
+ protected MultiscenePositionData m_multisceneData;
+#if USE_NAVMESH
+ protected NavMeshAgent m_navMeshAgent;
+#endif
+
+ public Transform target
+ {
+ get { return (m_target == null) ? this.transform : m_target; }
+ set { m_target = value; }
+ }
+
+ public bool usePlayerSpawnpoint
+ {
+ get { return m_usePlayerSpawnpoint; }
+ set { m_usePlayerSpawnpoint = value; }
+ }
+
+ protected bool multiscene { get { return m_multiscene; } }
+
+ public override void Awake()
+ {
+ base.Awake();
+ if (m_multiscene) m_multisceneData = new MultiscenePositionData();
+ else m_data = new PositionData();
+#if USE_NAVMESH
+ m_navMeshAgent = target.GetComponent();
+#endif
+ }
+
+ public override string RecordData()
+ {
+ var currentScene = SceneManager.GetActiveScene().buildIndex;
+ if (multiscene)
+ {
+ var found = false;
+ for (int i = 0; i < m_multisceneData.positions.Count; i++)
+ {
+ if (m_multisceneData.positions[i].scene == currentScene)
+ {
+ found = true;
+ m_multisceneData.positions[i].position = target.transform.position;
+ m_multisceneData.positions[i].rotation = target.transform.rotation;
+ break;
+ }
+ }
+ if (!found)
+ {
+ m_multisceneData.positions.Add(new ScenePositionData(currentScene, target.transform.position, target.transform.rotation));
+ }
+ return SaveSystem.Serialize(m_multisceneData);
+ }
+ else
+ {
+ m_data.scene = currentScene;
+ m_data.position = target.transform.position;
+ m_data.rotation = target.transform.rotation;
+ return SaveSystem.Serialize(m_data);
+ }
+ }
+
+ public override void ApplyData(string s)
+ {
+ if (usePlayerSpawnpoint && SaveSystem.playerSpawnpoint != null)
+ {
+ SetPosition(SaveSystem.playerSpawnpoint.transform.position, SaveSystem.playerSpawnpoint.transform.rotation);
+ }
+ else if (!string.IsNullOrEmpty(s))
+ {
+ var currentScene = SceneManager.GetActiveScene().buildIndex;
+ if (multiscene)
+ {
+ var multisceneData = SaveSystem.Deserialize(s, m_multisceneData);
+ if (multisceneData == null) return;
+ m_multisceneData = multisceneData;
+ for (int i = 0; i < m_multisceneData.positions.Count; i++)
+ {
+ if (m_multisceneData.positions[i].scene == currentScene)
+ {
+ SetPosition(m_multisceneData.positions[i].position, m_multisceneData.positions[i].rotation);
+ break;
+ }
+ }
+ }
+ else
+ {
+ var data = SaveSystem.Deserialize(s, m_data);
+ if (data == null) return;
+ m_data = data;
+ if (data.scene == currentScene || data.scene == -1)
+ {
+ SetPosition(data.position, data.rotation);
+ }
+ }
+ }
+ }
+
+ protected virtual void SetPosition(Vector3 position, Quaternion rotation)
+ {
+#if USE_NAVMESH
+ if (m_navMeshAgent != null)
+ {
+ m_navMeshAgent.Warp(position);
+ }
+ else
+#endif
+ {
+ target.transform.position = position;
+ }
+ target.transform.rotation = rotation;
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/PositionSaver.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/PositionSaver.cs.meta
new file mode 100644
index 000000000..4e1e24f8d
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/PositionSaver.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 2d0ae268d8fb2a242ad44eb54aa1a425
+timeCreated: 1485137383
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/PositionSaver.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/Saver.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/Saver.cs
new file mode 100644
index 000000000..dc469baea
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/Saver.cs
@@ -0,0 +1,157 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Abstract base class for a "saver", which is a component that contributes
+ /// to saved game data.
+ ///
+ public abstract class Saver : MonoBehaviour
+ {
+
+ [Tooltip("Save data under this key. If blank, use GameObject name.")]
+ [SerializeField]
+ private string m_key;
+
+ [Tooltip("Append the name of this saver type to the key.")]
+ [SerializeField]
+ private bool m_appendSaverTypeToKey;
+
+ [Tooltip("Save when changing scenes to be able to restore saved state when returning to scene.")]
+ [SerializeField]
+ private bool m_saveAcrossSceneChanges = true;
+
+ [Tooltip("When starting, restore this saver's state from current saved game data. Normally the save system restores state when loading games or changing scenes without this checkbox.")]
+ [SerializeField]
+ private bool m_restoreStateOnStart = false;
+
+ protected string m_runtimeKey = null;
+
+ ///
+ /// Append the name of this saver type to the key.
+ ///
+ public bool appendSaverTypeToKey
+ {
+ get { return m_appendSaverTypeToKey; }
+ set { m_appendSaverTypeToKey = value; }
+ }
+
+ ///
+ /// Save data under this key. If blank, use GameObject name.
+ ///
+ public virtual string key
+ {
+ get
+ {
+ if (string.IsNullOrEmpty(m_runtimeKey))
+ {
+ m_runtimeKey = !string.IsNullOrEmpty(m_key) ? m_key : name;
+ if (appendSaverTypeToKey)
+ {
+ var typeName = GetType().Name;
+ if (typeName.EndsWith("Saver")) typeName.Remove(typeName.Length - "Saver".Length);
+ m_runtimeKey += typeName;
+ }
+ }
+ return m_runtimeKey;
+ }
+ set
+ {
+ m_key = value;
+ m_runtimeKey = value;
+ }
+ }
+
+ ///
+ /// Accesses the internal key value. Normally leave this untouched and
+ /// access the key property instead.
+ ///
+ public string _internalKeyValue
+ {
+ get { return m_key; }
+ set { m_key = value; }
+ }
+
+ ///
+ /// Save when changing scenes to be able to restore saved state when returning to scene.
+ ///
+ public virtual bool saveAcrossSceneChanges
+ {
+ get { return m_saveAcrossSceneChanges; }
+ set { m_saveAcrossSceneChanges = value; }
+ }
+
+ public virtual bool restoreStateOnStart
+ {
+ get { return m_restoreStateOnStart; }
+ set { m_restoreStateOnStart = value; }
+ }
+
+ public virtual void Awake() { }
+
+ public virtual void Start()
+ {
+ if (restoreStateOnStart)
+ {
+ ApplyData(SaveSystem.currentSavedGameData.GetData(key));
+ }
+ }
+
+ public virtual void Reset() { }
+
+ public virtual void OnEnable()
+ {
+ SaveSystem.RegisterSaver(this);
+ }
+
+ public virtual void OnDisable()
+ {
+ SaveSystem.UnregisterSaver(this);
+ }
+
+ public virtual void OnDestroy() { }
+
+ ///
+ /// This method should return a string that represents the data you want to save.
+ /// You can use SaveSystem.Serialize() to serialize a serializable object to a
+ /// string. This will use the serializer component on the Save System GameObject,
+ /// which defaults to JSON serialization.
+ ///
+ public abstract string RecordData();
+
+ ///
+ /// This method should process the string representation of saved data and apply
+ /// it to the current state of the game. You can use SaveSystem.Deserialize()
+ /// to deserialize the string to an object that specifies the state to apply to
+ /// the game.
+ ///
+ public abstract void ApplyData(string s);
+
+ ///
+ /// If the Saver needs to pull data from the Save System immediately after loading a
+ /// scene, instead of waiting for ApplyData to be called at it its normal time, it
+ /// can implement this method. For efficiency, the Save System will not look up the
+ /// Saver's data; your method must look it up manually by calling
+ /// SaveSystem.savedGameData.GetData(key).
+ ///
+ public virtual void ApplyDataImmediate() { }
+
+ ///
+ /// The Save System will call this method before scene changes. If your saver listens for
+ /// OnDisable or OnDestroy messages (see DestructibleSaver for example), it can use this
+ /// method to ignore the next OnDisable or OnDestroy message since they will be called
+ /// because the entire scene is being unloaded.
+ ///
+ public virtual void OnBeforeSceneChange() { }
+
+ ///
+ /// The Save System will call this method when restarting the game. Your saver can
+ /// reset data to a fresh state if necessary.
+ ///
+ public virtual void OnRestartGame() { }
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/Saver.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/Saver.cs.meta
new file mode 100644
index 000000000..e1bf213cb
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/Saver.cs.meta
@@ -0,0 +1,17 @@
+fileFormatVersion: 2
+guid: 0b5534613dc0ce1448f3be87ac7405ce
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Savers/Saver.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers.meta
new file mode 100644
index 000000000..8e8aa02bf
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: f7ecdc8c12f63334c9e2e73acb97e687
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary.meta
new file mode 100644
index 000000000..d536174d4
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 0fe20748d12b2284c965f55bb301cf4a
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/BinaryDataSerializer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/BinaryDataSerializer.cs
new file mode 100644
index 000000000..f48954ab0
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/BinaryDataSerializer.cs
@@ -0,0 +1,62 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+using System.IO;
+using System.Runtime.Serialization;
+using System.Runtime.Serialization.Formatters.Binary;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Implementation of DataSerializer that uses BinaryFormatter.
+ /// Note: For security reasons, BinaryFormatter is no longer recommended.
+ /// Use JsonDataSerializer, or use this class as a model to implement
+ /// your own binary DataSerializer.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class BinaryDataSerializer : DataSerializer
+ {
+
+ protected virtual void AddSurrogateSelectors(SurrogateSelector surrogateSelector)
+ {
+ surrogateSelector.AddSurrogate(typeof(Vector3), new StreamingContext(StreamingContextStates.All), new Vector3SerializationSurrogate());
+ surrogateSelector.AddSurrogate(typeof(Quaternion), new StreamingContext(StreamingContextStates.All), new QuaternionSerializationSurrogate());
+ }
+
+ protected virtual BinaryFormatter CreateBinaryFormatter()
+ {
+ var binaryFormatter = new BinaryFormatter();
+ var surrogateSelector = new SurrogateSelector();
+ AddSurrogateSelectors(surrogateSelector);
+ binaryFormatter.SurrogateSelector = surrogateSelector;
+ return binaryFormatter;
+ }
+
+ public override string Serialize(object data)
+ {
+ if (data == null || !data.GetType().IsSerializable) return string.Empty;
+ using (var stream = new MemoryStream())
+ {
+ var binaryFormatter = CreateBinaryFormatter();
+ binaryFormatter.Serialize(stream, data);
+ return Convert.ToBase64String(stream.ToArray());
+ }
+ }
+
+ public override T Deserialize(string s, T data = default(T))
+ {
+ if (string.IsNullOrEmpty(s)) return default(T);
+ var bytes = Convert.FromBase64String(s);
+ using (var stream = new MemoryStream(bytes))
+ {
+ var binaryFormatter = CreateBinaryFormatter();
+ data = (T)binaryFormatter.Deserialize(stream);
+ return data;
+ }
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/BinaryDataSerializer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/BinaryDataSerializer.cs.meta
new file mode 100644
index 000000000..ecab2215f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/BinaryDataSerializer.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 1c5ea4dcd6bc38d4784cd019d1d6f444
+timeCreated: 1532876310
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/BinaryDataSerializer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/QuaternionSerializationSurrogate.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/QuaternionSerializationSurrogate.cs
new file mode 100644
index 000000000..e636c21c6
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/QuaternionSerializationSurrogate.cs
@@ -0,0 +1,39 @@
+// Based on code by takatok: https://forum.unity.com/threads/vector3-is-not-marked-serializable.435303/#post-2814558
+using UnityEngine;
+using System.Runtime.Serialization;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Adds .NET serialization support for Quaternion.
+ ///
+ public class QuaternionSerializationSurrogate : ISerializationSurrogate
+ {
+
+ // Method called to serialize a Quaternion object
+ public void GetObjectData(System.Object obj, SerializationInfo info, StreamingContext context)
+ {
+
+ Quaternion quarternion = (Quaternion)obj;
+ info.AddValue("w", quarternion.w);
+ info.AddValue("x", quarternion.x);
+ info.AddValue("y", quarternion.y);
+ info.AddValue("z", quarternion.z);
+ }
+
+ // Method called to deserialize a Quaternion object
+ public System.Object SetObjectData(System.Object obj, SerializationInfo info,
+ StreamingContext context, ISurrogateSelector selector)
+ {
+
+ Quaternion quaternion = (Quaternion)obj;
+ quaternion.w = (float)info.GetValue("w", typeof(float));
+ quaternion.x = (float)info.GetValue("x", typeof(float));
+ quaternion.y = (float)info.GetValue("y", typeof(float));
+ quaternion.z = (float)info.GetValue("z", typeof(float));
+ obj = quaternion;
+ return obj;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/QuaternionSerializationSurrogate.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/QuaternionSerializationSurrogate.cs.meta
new file mode 100644
index 000000000..16f5d163f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/QuaternionSerializationSurrogate.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 8b3c9f88c5822534ab660b0f20cf22a7
+timeCreated: 1532876310
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/QuaternionSerializationSurrogate.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/Vector3SerializationSurrogate.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/Vector3SerializationSurrogate.cs
new file mode 100644
index 000000000..650dd90df
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/Vector3SerializationSurrogate.cs
@@ -0,0 +1,37 @@
+// Code by takatok: https://forum.unity.com/threads/vector3-is-not-marked-serializable.435303/#post-2814558
+using UnityEngine;
+using System.Runtime.Serialization;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Adds .NET serialization support for Vector3.
+ ///
+ public class Vector3SerializationSurrogate : ISerializationSurrogate
+ {
+
+ // Method called to serialize a Vector3 object
+ public void GetObjectData(System.Object obj, SerializationInfo info, StreamingContext context)
+ {
+
+ Vector3 v3 = (Vector3)obj;
+ info.AddValue("x", v3.x);
+ info.AddValue("y", v3.y);
+ info.AddValue("z", v3.z);
+ }
+
+ // Method called to deserialize a Vector3 object
+ public System.Object SetObjectData(System.Object obj, SerializationInfo info,
+ StreamingContext context, ISurrogateSelector selector)
+ {
+
+ Vector3 v3 = (Vector3)obj;
+ v3.x = (float)info.GetValue("x", typeof(float));
+ v3.y = (float)info.GetValue("y", typeof(float));
+ v3.z = (float)info.GetValue("z", typeof(float));
+ obj = v3;
+ return obj;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/Vector3SerializationSurrogate.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/Vector3SerializationSurrogate.cs.meta
new file mode 100644
index 000000000..06b0be993
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/Vector3SerializationSurrogate.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: fe824b4dc600fb74ead40b0561256f53
+timeCreated: 1532876310
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/Binary/Vector3SerializationSurrogate.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/DataSerializer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/DataSerializer.cs
new file mode 100644
index 000000000..fc8ab0a2d
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/DataSerializer.cs
@@ -0,0 +1,20 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Abstract base class for serializers that serialize objects to strings and
+ /// deserialize strings back into objects.
+ ///
+ public abstract class DataSerializer : MonoBehaviour
+ {
+
+ public abstract string Serialize(object data);
+ public abstract T Deserialize(string s, T data = default(T));
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/DataSerializer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/DataSerializer.cs.meta
new file mode 100644
index 000000000..175c4be15
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/DataSerializer.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: d35f47dfb708002499621ee8e8b21588
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/DataSerializer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/JsonDataSerializer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/JsonDataSerializer.cs
new file mode 100644
index 000000000..e2abdf088
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/JsonDataSerializer.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Implementation of DataSerializer that uses JsonUtility.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class JsonDataSerializer : DataSerializer
+ {
+
+ [Tooltip("Use larger but more human-readable format.")]
+ [SerializeField]
+ private bool m_prettyPrint;
+
+ public bool prettyPrint
+ {
+ get { return m_prettyPrint; }
+ set { m_prettyPrint = value; }
+ }
+
+ public override string Serialize(object data)
+ {
+#if UNITY_5_3_6 || UNITY_5_3_7 || UNITY_5_4_OR_NEWER
+ return JsonUtility.ToJson(data, m_prettyPrint);
+#else
+ Debug.LogWarning("Save System: JSON Serialization is not supported before Unity 5.3.6.");
+ return string.Empty;
+#endif
+ }
+
+ public override T Deserialize(string s, T data = default(T))
+ {
+#if UNITY_5_3_6 || UNITY_5_3_7 || UNITY_5_4_OR_NEWER
+ if (Equals(data, default(T)))
+ {
+ return JsonUtility.FromJson(s);
+ }
+ else
+ {
+ JsonUtility.FromJsonOverwrite(s, data);
+ return data;
+ }
+#else
+ Debug.LogWarning("Save System: JSON Serialization is not supported before Unity 5.3.6.");
+ return default(T);
+#endif
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/JsonDataSerializer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/JsonDataSerializer.cs.meta
new file mode 100644
index 000000000..9a6565535
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/JsonDataSerializer.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: af012c0ddd89bab439c810d126388d3a
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Serializers/JsonDataSerializer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning.meta
new file mode 100644
index 000000000..c08793eb6
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 68c7e52e7b6839c4ebd75c0af3ccaa93
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObject.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObject.cs
new file mode 100644
index 000000000..10bc22f3c
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObject.cs
@@ -0,0 +1,122 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using System;
+using UnityEngine;
+using UnityEngine.Serialization;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// A spawned object or spawnable prefab.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class SpawnedObject : Saver
+ {
+
+ public enum Mode { OnDisable, OnDestroy }
+
+ [Tooltip("Event to watch for to record that object was despawned.")]
+ [SerializeField]
+ [FormerlySerializedAs("m_mode")]
+ private Mode m_despawnMode = Mode.OnDestroy;
+
+ [Tooltip("Save unique data on this spawned object's Saver components.")]
+ [SerializeField]
+ private bool m_saveUniqueSaverData = true;
+
+ private bool m_ignoreOnDestroy = false; // Scene is being unloaded; don't record as despawn.
+ private string m_guid = string.Empty;
+
+ public Mode despawnMode
+ {
+ get { return m_despawnMode; }
+ set { m_despawnMode = value; }
+ }
+
+ public Mode mode // For backward compatibility.
+ {
+ get { return despawnMode; }
+ set { despawnMode = value; }
+ }
+
+ public bool saveUniqueSaverData
+ {
+ get { return m_saveUniqueSaverData; }
+ set { m_saveUniqueSaverData = value; }
+ }
+
+ public string guid
+ {
+ get { return m_guid; }
+ set { m_guid = value; }
+ }
+
+ public override void Awake()
+ {
+ base.Awake();
+ m_guid = Guid.NewGuid().ToString();
+ }
+
+ public override void Start()
+ {
+ base.Start();
+ AddGuidToSaverKeys();
+ SpawnedObjectManager.AddSpawnedObjectData(this);
+ }
+
+ protected virtual void AddGuidToSaverKeys()
+ {
+ if (!string.IsNullOrEmpty(guid))
+ {
+ foreach (Saver saver in GetComponentsInChildren())
+ {
+ var key = saver.key;
+ if (!key.EndsWith(guid))
+ {
+ saver.key += guid;
+ }
+ }
+ }
+ }
+
+ public override void OnBeforeSceneChange()
+ {
+ base.OnBeforeSceneChange();
+ m_ignoreOnDestroy = true;
+ }
+
+ public override void OnDisable()
+ {
+ base.OnDisable();
+ if (m_despawnMode != Mode.OnDisable) return;
+ RecordDestruction();
+ }
+
+ public override void OnDestroy()
+ {
+ base.OnDestroy();
+ if (m_despawnMode != Mode.OnDestroy) return;
+ RecordDestruction();
+ }
+
+ protected virtual void RecordDestruction()
+ {
+ if (!m_ignoreOnDestroy)
+ {
+ SpawnedObjectManager.RemoveSpawnedObjectData(this);
+ }
+ m_ignoreOnDestroy = false;
+ }
+
+ public override string RecordData()
+ {
+ return string.Empty;
+ }
+
+ public override void ApplyData(string data)
+ {
+ }
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObject.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObject.cs.meta
new file mode 100644
index 000000000..a0b0b394f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObject.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 100e698bf1a9e0648bc7f84866b34fe6
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObject.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectList.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectList.cs
new file mode 100644
index 000000000..87b72a300
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectList.cs
@@ -0,0 +1,27 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Holds a list of references to SpawnedObject prefabs.
+ ///
+ public class SpawnedObjectList : ScriptableObject
+ {
+
+ [Tooltip("Save unique data on this spawned object's Saver components.")]
+ [SerializeField]
+ private List m_spawnedObjectPrefabs;
+
+ public List spawnedObjectPrefabs
+ {
+ get { return m_spawnedObjectPrefabs; }
+ set { m_spawnedObjectPrefabs = value; }
+ }
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectList.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectList.cs.meta
new file mode 100644
index 000000000..50862485f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectList.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: 19c2e391c509b2144b3604741ab85456
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectList.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectManager.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectManager.cs
new file mode 100644
index 000000000..461e35092
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectManager.cs
@@ -0,0 +1,188 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+using System.Collections;
+using System.Collections.Generic;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Manages spawned objects for a scene.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class SpawnedObjectManager : Saver
+ {
+
+ [Serializable]
+ public class SpawnedObjectData
+ {
+ public string prefabName;
+ public Vector3 position;
+ public Quaternion rotation;
+ public string guid;
+
+ public SpawnedObjectData() { }
+ public SpawnedObjectData(string prefabName, Vector3 position, Quaternion rotation, string guid = null)
+ {
+ this.prefabName = prefabName;
+ this.position = position;
+ this.rotation = rotation;
+ this.guid = guid;
+ }
+ }
+
+ [Serializable]
+ public class SpawnedObjectDataList
+ {
+ public List list = new List();
+ }
+
+ [Tooltip("Prefabs for all spawnable objects except those in Spawned Object Prefab Lists below. If your spawnable object isn't in this list or Spawned Object Prefab Lists, Spawned Object Manager won't be able to respawn it when restoring a scene.")]
+ [SerializeField]
+ private List m_spawnedObjectPrefabs = new List();
+
+ [Tooltip("Additional lists of spawnable object prefabs. If your spawnable object isn't in any of these lists or Spawned Object Prefabs above, Spawned Object Manager won't be able to respawn it when restoring a scene.")]
+ [SerializeField]
+ private List m_spawnedObjectPrefabLists = new List();
+
+ [Tooltip("Objects that have currently been spawned.")]
+ [SerializeField]
+ private List m_spawnedObjects = new List();
+
+ [Tooltip("When restoring this Spawned Object Manager, tell respawned objects to restore their saved data also.")]
+ [SerializeField]
+ private bool m_applySaveDataToSpawnedObjectsOnRestore = true;
+
+ private static SpawnedObjectManager m_instance;
+
+ public List spawnedObjectPrefabs
+ {
+ get { return m_spawnedObjectPrefabs; }
+ set { m_spawnedObjectPrefabs = value; }
+ }
+
+ public List spawnedObjectPrefabLists
+ {
+ get { return m_spawnedObjectPrefabLists; }
+ set { m_spawnedObjectPrefabLists = value; }
+ }
+
+ public List spawnedObjects
+ {
+ get { return m_spawnedObjects; }
+ set { m_spawnedObjects = value; }
+ }
+
+ public bool applySaveDataToSpawnedObjectsOnRestore
+ {
+ get { return m_applySaveDataToSpawnedObjectsOnRestore; }
+ set { m_applySaveDataToSpawnedObjectsOnRestore = value; }
+ }
+
+ public override string key
+ {
+ get // Help ensure unique keys by adding scene index if left blank in inspector.
+ {
+ var baseKey = base.key;
+ return string.Equals(baseKey, name) ? name + " Scene " + SaveSystem.currentSceneIndex : baseKey;
+ }
+ set { base.key = value; }
+ }
+
+ public override void Reset()
+ {
+ base.Reset();
+ saveAcrossSceneChanges = true;
+ }
+
+ public override void Awake()
+ {
+ base.Awake();
+ m_instance = this;
+ }
+
+ public override string RecordData()
+ {
+ var spawnedObjectDataList = new SpawnedObjectDataList();
+ for (int i = 0; i < m_spawnedObjects.Count; i++)
+ {
+ var spawnedObject = m_spawnedObjects[i];
+ if (spawnedObject == null) continue;
+ spawnedObjectDataList.list.Add(new SpawnedObjectData(spawnedObject.name.Replace("(Clone)", string.Empty), spawnedObject.transform.position, spawnedObject.transform.rotation, spawnedObject.guid));
+ }
+ return SaveSystem.Serialize(spawnedObjectDataList);
+ }
+
+ public override void ApplyData(string data)
+ {
+ if (string.IsNullOrEmpty(data)) return;
+ var spawnedObjectDataList = SaveSystem.Deserialize(data);
+ if (spawnedObjectDataList == null || spawnedObjectDataList.list == null) return;
+ m_spawnedObjects.Clear();
+ for (int i = 0; i < spawnedObjectDataList.list.Count; i++)
+ {
+ var spawnedObjectData = spawnedObjectDataList.list[i];
+ if (spawnedObjectData == null) continue;
+ var prefab = GetSpawnedObjectPrefab(spawnedObjectData.prefabName);
+ if (prefab == null) continue;
+ var instance = Instantiate(prefab, spawnedObjectData.position, spawnedObjectData.rotation);
+ instance.guid = spawnedObjectData.guid;
+ }
+ if (m_applySaveDataToSpawnedObjectsOnRestore)
+ {
+ StartCoroutine(ApplyDataToRespawnedObjectsAfterFrames(SaveSystem.framesToWaitBeforeApplyData));
+ }
+ }
+
+ protected IEnumerator ApplyDataToRespawnedObjectsAfterFrames(int numFrames)
+ {
+ for (int i = 0; i < numFrames; i++)
+ {
+ yield return null;
+ }
+ yield return new WaitForEndOfFrame();
+ ApplyDataToRespawnedObjects();
+ }
+
+ protected virtual void ApplyDataToRespawnedObjects()
+ {
+ for (int i = 0; i < m_spawnedObjects.Count; i++)
+ {
+ foreach (var saver in m_spawnedObjects[i].GetComponentsInChildren())
+ {
+ saver.ApplyData(SaveSystem.currentSavedGameData.GetData(saver.key));
+ }
+ }
+ }
+
+ protected virtual SpawnedObject GetSpawnedObjectPrefab(string prefabName)
+ {
+ var prefab = m_spawnedObjectPrefabs.Find(x => x != null && string.Equals(x.name, prefabName));
+ if (prefab == null)
+ {
+ foreach (SpawnedObjectList list in spawnedObjectPrefabLists)
+ {
+ prefab = list.spawnedObjectPrefabs.Find(x => x != null && string.Equals(x.name, prefabName));
+ if (prefab != null) break;
+ }
+ }
+ return prefab;
+ }
+
+ public static void AddSpawnedObjectData(SpawnedObject spawnedObject)
+ {
+ if (m_instance == null || spawnedObject == null) return;
+ m_instance.m_spawnedObjects.Add(spawnedObject);
+ }
+
+ public static void RemoveSpawnedObjectData(SpawnedObject spawnedObject)
+ {
+ if (m_instance == null || spawnedObject == null) return;
+ m_instance.m_spawnedObjects.Remove(spawnedObject);
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectManager.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectManager.cs.meta
new file mode 100644
index 000000000..bba7e5e30
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectManager.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: e76283c8057890c43bab2fe32960e8c7
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Spawning/SpawnedObjectManager.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers.meta
new file mode 100644
index 000000000..f26d29f53
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 6884fc9e7abc0b1499d58e5a81e9234e
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/DiskSavedGameDataStorer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/DiskSavedGameDataStorer.cs
new file mode 100644
index 000000000..2a7dc4865
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/DiskSavedGameDataStorer.cs
@@ -0,0 +1,299 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System.Collections.Generic;
+#if !(UNITY_WEBGL || UNITY_WSA)
+using System.IO;
+#endif
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Implements SavedGameDataStorer using local disk files.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class DiskSavedGameDataStorer : SavedGameDataStorer
+ {
+
+#if !(UNITY_WEBGL || UNITY_WSA)
+
+ public enum BasePath { PersistentDataPath, DataPath, Custom }
+
+ [Tooltip("Persistent Data Path: Usual location where Unity stores data to be kept between runs.\nData Path: Game data folder on target device.\nCustom: Set below.")]
+ public BasePath storeSaveFilesIn = BasePath.PersistentDataPath;
+
+ public string customPath;
+
+ [Tooltip("Encrypt saved game files.")]
+ public bool encrypt = true;
+
+ [Tooltip("If encrypting, use this password.")]
+ public string encryptionPassword = "My Password";
+
+ [Tooltip("Log debug info.")]
+ [SerializeField]
+ private bool m_debug;
+
+ protected class SavedGameInfo
+ {
+ public string sceneName;
+
+ public SavedGameInfo(string sceneName)
+ {
+ this.sceneName = sceneName;
+ }
+ }
+
+ protected List m_savedGameInfo = null;
+
+ protected List savedGameInfo
+ {
+ get
+ {
+ if (m_savedGameInfo == null) LoadSavedGameInfoFromFile();
+ return m_savedGameInfo;
+ }
+ }
+
+ public bool debug
+ {
+ get { return m_debug && Debug.isDebugBuild; }
+ set { m_debug = value; }
+ }
+
+ public virtual void Start()
+ {
+ LoadSavedGameInfoFromFile();
+ }
+
+ protected virtual string GetBasePath()
+ {
+ switch (storeSaveFilesIn)
+ {
+ default:
+ case BasePath.PersistentDataPath:
+ return Application.persistentDataPath;
+ case BasePath.DataPath:
+ return Application.dataPath;
+ case BasePath.Custom:
+ return customPath;
+ }
+ }
+
+ public virtual string GetSaveGameFilename(int slotNumber)
+ {
+ return GetBasePath() + "/save_" + slotNumber + ".dat";
+ }
+
+ public virtual string GetSavedGameInfoFilename()
+ {
+ return GetBasePath() + "/saveinfo.dat";
+ }
+
+ public virtual void LoadSavedGameInfoFromFile()
+ {
+ m_savedGameInfo = new List();
+ var filename = GetSavedGameInfoFilename();
+ if (!VerifySavedGameInfoFile(filename)) return;
+ if (debug) Debug.Log("Save System: DiskSavedGameDataStorer loading " + filename);
+ try
+ {
+ using (StreamReader streamReader = new StreamReader(filename))
+ {
+ int safeguard = 0;
+ while (!streamReader.EndOfStream && safeguard < 999)
+ {
+ var sceneName = streamReader.ReadLine().Replace("", "\n");
+ m_savedGameInfo.Add(new SavedGameInfo(sceneName));
+ safeguard++;
+ }
+ }
+ }
+ catch (System.Exception)
+ {
+ Debug.Log("Save System: DiskSavedGameDataStorer - Error reading file: " + filename);
+ }
+ }
+
+ protected virtual bool VerifySavedGameInfoFile(string saveInfoFilename)
+ {
+ if (string.IsNullOrEmpty(saveInfoFilename) || !File.Exists(saveInfoFilename))
+ {
+ // If saved game info file doesn't exist, recreate it from existing save_#.dat files:
+ var path = Path.GetDirectoryName(saveInfoFilename);
+ if (!Directory.Exists(path)) return false;
+
+ // Find the highest-numbered save file:
+ int highestSave = 0;
+ const int MaxSaveSlot = 100;
+ for (int i = 0; i <= MaxSaveSlot; i++)
+ {
+ var saveGameFilename = GetSaveGameFilename(i);
+ if (File.Exists(saveGameFilename)) highestSave = i;
+ }
+
+ // Initialize savedGameInfo and write to save info file:
+ savedGameInfo.Clear();
+ for (int i = 0; i <= highestSave; i++)
+ {
+ savedGameInfo.Add(new SavedGameInfo(string.Empty));
+ }
+ WriteSavedGameInfoToDisk();
+ }
+ return true;
+ }
+
+ public virtual void UpdateSavedGameInfoToFile(int slotNumber, SavedGameData savedGameData)
+ {
+ var slotIndex = slotNumber;
+
+ // Add any missing info elements for slots preceding specified slotNumber:
+ for (int i = savedGameInfo.Count; i <= slotIndex; i++)
+ {
+ savedGameInfo.Add(new SavedGameInfo(string.Empty));
+ }
+
+ if (0 <= slotIndex && slotIndex < savedGameInfo.Count)
+ {
+ savedGameInfo[slotIndex].sceneName = (savedGameData != null) ? savedGameData.sceneName : string.Empty;
+ }
+ WriteSavedGameInfoToDisk();
+ }
+
+ protected virtual void WriteSavedGameInfoToDisk()
+ {
+ var filename = GetSavedGameInfoFilename();
+ if (debug) Debug.Log("Save System: DiskSavedGameDataStorer updating " + filename);
+ try
+ {
+ using (StreamWriter streamWriter = new StreamWriter(filename))
+ {
+ for (int i = 0; i < savedGameInfo.Count; i++)
+ {
+ streamWriter.WriteLine(savedGameInfo[i].sceneName.Replace("\n", ""));
+ }
+ }
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogError("Save System: DiskSavedGameDataStorer - Can't create file: " + filename);
+ throw e;
+ }
+ }
+
+ public override bool HasDataInSlot(int slotNumber)
+ {
+ var slotIndex = slotNumber;
+ return 0 <= slotIndex && slotIndex < savedGameInfo.Count &&
+ !string.IsNullOrEmpty(savedGameInfo[slotIndex].sceneName) &&
+ File.Exists(GetSaveGameFilename(slotNumber));
+ }
+
+ public override void StoreSavedGameData(int slotNumber, SavedGameData savedGameData)
+ {
+ var s = SaveSystem.Serialize(savedGameData);
+ if (debug) Debug.Log("Save System: DiskSavedGameDataStorer - Saving " + GetSaveGameFilename(slotNumber) + ": " + s);
+ WriteStringToFile(GetSaveGameFilename(slotNumber), encrypt ? EncryptionUtility.Encrypt(s, encryptionPassword) : s);
+ UpdateSavedGameInfoToFile(slotNumber, savedGameData);
+ }
+
+ public override SavedGameData RetrieveSavedGameData(int slotNumber)
+ {
+ var s = ReadStringFromFile(GetSaveGameFilename(slotNumber));
+ if (string.IsNullOrEmpty(s)) return null;
+ if (encrypt)
+ {
+ string plainText;
+ s = EncryptionUtility.TryDecrypt(s, encryptionPassword, out plainText) ? plainText : string.Empty;
+ }
+ if (debug) Debug.Log("Save System: DiskSavedGameDataStorer - Loading " + GetSaveGameFilename(slotNumber) + ": " + s);
+ return SaveSystem.Deserialize(s);
+ }
+
+ public override void DeleteSavedGameData(int slotNumber)
+ {
+ try
+ {
+ var filename = GetSaveGameFilename(slotNumber);
+ if (File.Exists(filename)) File.Delete(filename);
+ }
+ catch (System.Exception)
+ {
+ }
+ UpdateSavedGameInfoToFile(slotNumber, null);
+ }
+
+ public static void WriteStringToFile(string filename, string data)
+ {
+ try
+ {
+ // Write to temp file. If successful, overwrite save file:
+ var tmpFilename = filename + ".tmp";
+ using (StreamWriter streamWriter = new StreamWriter(tmpFilename))
+ {
+ streamWriter.WriteLine(data);
+ }
+ if (File.Exists(filename))
+ {
+ File.Delete(filename);
+ }
+ File.Move(tmpFilename, filename);
+ }
+ catch (System.Exception e)
+ {
+ Debug.LogError("Save System: Can't create saved game file: " + filename);
+ throw e;
+ }
+ }
+
+ public static string ReadStringFromFile(string filename)
+ {
+ if (!File.Exists(filename)) return string.Empty;
+ try
+ {
+ using (StreamReader streamReader = new StreamReader(filename))
+ {
+ return streamReader.ReadToEnd();
+ }
+ }
+ catch (System.Exception)
+ {
+ Debug.Log("Save System: Error reading file: " + filename);
+ return string.Empty;
+ }
+ }
+
+#else
+ void Start()
+ {
+ Debug.LogError("DiskSavedGameDataStorer is not supported on this build platform.");
+ }
+
+ public override bool HasDataInSlot(int slotNumber)
+ {
+ Debug.LogError("DiskSavedGameDataStorer is not supported on this build platform.");
+ return false;
+ }
+
+ public override SavedGameData RetrieveSavedGameData(int slotNumber)
+ {
+ Debug.LogError("DiskSavedGameDataStorer is not supported on this build platform.");
+ return null;
+ }
+
+ public override void StoreSavedGameData(int slotNumber, SavedGameData savedGameData)
+ {
+ Debug.LogError("DiskSavedGameDataStorer is not supported on this build platform.");
+ }
+
+ public override void DeleteSavedGameData(int slotNumber)
+ {
+ Debug.LogError("DiskSavedGameDataStorer is not supported on this build platform.");
+ }
+
+#endif
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/DiskSavedGameDataStorer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/DiskSavedGameDataStorer.cs.meta
new file mode 100644
index 000000000..e72f88a9d
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/DiskSavedGameDataStorer.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 54b8571834278174a9372d31752ec788
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/DiskSavedGameDataStorer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/EncryptionUtility.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/EncryptionUtility.cs
new file mode 100644
index 000000000..cf3282bda
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/EncryptionUtility.cs
@@ -0,0 +1,121 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+#if UNITY_EDITOR || UNITY_STANDALONE
+using System;
+using System.Text;
+using System.IO;
+using System.Security.Cryptography;
+#endif
+
+namespace PixelCrushers
+{
+
+ public class EncryptionUtility
+ {
+
+#if UNITY_EDITOR || UNITY_STANDALONE
+
+ // From: https://developingsoftware.com/how-to-securely-store-data-in-unity-player-preferences
+
+ const int Iterations = 1000;
+
+ public static string Encrypt(string plainText, string password)
+ {
+ if (string.IsNullOrEmpty(plainText) || string.IsNullOrEmpty(password)) return string.Empty;
+
+ // create instance of the DES crypto provider
+ var des = new DESCryptoServiceProvider();
+
+ // generate a random IV will be used a salt value for generating key
+ des.GenerateIV();
+
+ // use derive bytes to generate a key from the password and IV
+ var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, des.IV, Iterations);
+
+ // generate a key from the password provided
+ byte[] key = rfc2898DeriveBytes.GetBytes(8);
+
+ // encrypt the plainText
+ using (var memoryStream = new MemoryStream())
+ using (var cryptoStream = new CryptoStream(memoryStream, des.CreateEncryptor(key, des.IV), CryptoStreamMode.Write))
+ {
+ // write the salt first not encrypted
+ memoryStream.Write(des.IV, 0, des.IV.Length);
+
+ // convert the plain text string into a byte array
+ byte[] bytes = Encoding.UTF8.GetBytes(plainText);
+
+ // write the bytes into the crypto stream so that they are encrypted bytes
+ cryptoStream.Write(bytes, 0, bytes.Length);
+ cryptoStream.FlushFinalBlock();
+
+ return Convert.ToBase64String(memoryStream.ToArray());
+ }
+ }
+
+ public static bool TryDecrypt(string cipherText, string password, out string plainText)
+ {
+ // its pointless trying to decrypt if the cipher text
+ // or password has not been supplied
+ if (string.IsNullOrEmpty(cipherText) ||
+ string.IsNullOrEmpty(password))
+ {
+ plainText = string.Empty;
+ return false;
+ }
+
+ try
+ {
+ byte[] cipherBytes = Convert.FromBase64String(cipherText);
+
+ using (var memoryStream = new MemoryStream(cipherBytes))
+ {
+ // create instance of the DES crypto provider
+ var des = new DESCryptoServiceProvider();
+
+ // get the IV
+ byte[] iv = new byte[8];
+ memoryStream.Read(iv, 0, iv.Length);
+
+ // use derive bytes to generate key from password and IV
+ var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, iv, Iterations);
+
+ byte[] key = rfc2898DeriveBytes.GetBytes(8);
+
+ using (var cryptoStream = new CryptoStream(memoryStream, des.CreateDecryptor(key, iv), CryptoStreamMode.Read))
+ using (var streamReader = new StreamReader(cryptoStream))
+ {
+ plainText = streamReader.ReadToEnd();
+ return true;
+ }
+ }
+ }
+ catch (Exception ex)
+ {
+ Debug.LogError("Dialogue System Menus: Can't decrypt data: + " + ex.Message);
+ plainText = string.Empty;
+ return false;
+ }
+ }
+
+#else
+
+ // No encryption on other platforms:
+
+ public static string Encrypt(string plainText, string password)
+ {
+ return plainText;
+ }
+
+ public static bool TryDecrypt(string cipherText, string password, out string plainText)
+ {
+ plainText = cipherText;
+ return true;
+ }
+
+#endif
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/EncryptionUtility.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/EncryptionUtility.cs.meta
new file mode 100644
index 000000000..136f5a4d3
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/EncryptionUtility.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 6271146f407ab714fb3c9480896b68a3
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/EncryptionUtility.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/PlayerPrefsSavedGameDataStorer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/PlayerPrefsSavedGameDataStorer.cs
new file mode 100644
index 000000000..62874e8df
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/PlayerPrefsSavedGameDataStorer.cs
@@ -0,0 +1,86 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Implements SavedGameDataStorer using PlayerPrefs.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class PlayerPrefsSavedGameDataStorer : SavedGameDataStorer
+ {
+
+ [Tooltip("Save games under this PlayerPrefs key")]
+ [SerializeField]
+ private string m_playerPrefsKeyBase = "Save";
+
+#if UNITY_EDITOR || UNITY_STANDALONE
+
+ [Tooltip("Encrypt saved game data.")]
+ public bool encrypt = false;
+
+ [Tooltip("If encrypting, use this password.")]
+ public string encryptionPassword = "My Password";
+
+#else
+ private bool encrypt = false;
+ private string encryptionPassword = "My Password";
+#endif
+
+ [Tooltip("Log debug info.")]
+ [SerializeField]
+ private bool m_debug = false;
+
+ public string playerPrefsKeyBase
+ {
+ get { return m_playerPrefsKeyBase; }
+ set { m_playerPrefsKeyBase = value; }
+ }
+
+ public bool debug
+ {
+ get { return m_debug && Debug.isDebugBuild; }
+ }
+
+ public string GetPlayerPrefsKey(int slotNumber)
+ {
+ return m_playerPrefsKeyBase + slotNumber;
+ }
+
+ public override bool HasDataInSlot(int slotNumber)
+ {
+ return PlayerPrefs.HasKey(GetPlayerPrefsKey(slotNumber));
+ }
+
+ public override void StoreSavedGameData(int slotNumber, SavedGameData savedGameData)
+ {
+ var s = SaveSystem.Serialize(savedGameData);
+ if (debug) Debug.Log("Save System: Storing in PlayerPrefs key " + GetPlayerPrefsKey(slotNumber) + ": " + s);
+ PlayerPrefs.SetString(GetPlayerPrefsKey(slotNumber), encrypt ? EncryptionUtility.Encrypt(s, encryptionPassword) : s);
+ PlayerPrefs.Save();
+ }
+
+ public override SavedGameData RetrieveSavedGameData(int slotNumber)
+ {
+ if (debug && HasDataInSlot(slotNumber)) Debug.Log("Save System: Retrieved from PlayerPrefs key " +
+ GetPlayerPrefsKey(slotNumber) + ": " + PlayerPrefs.GetString(GetPlayerPrefsKey(slotNumber)));
+ var s = PlayerPrefs.GetString(GetPlayerPrefsKey(slotNumber));
+ if (encrypt)
+ {
+ string plainText;
+ s = EncryptionUtility.TryDecrypt(s, encryptionPassword, out plainText) ? plainText : string.Empty;
+ }
+ return HasDataInSlot(slotNumber) ? SaveSystem.Deserialize(s) : new SavedGameData();
+ }
+
+ public override void DeleteSavedGameData(int slotNumber)
+ {
+ PlayerPrefs.DeleteKey(GetPlayerPrefsKey(slotNumber));
+ PlayerPrefs.Save();
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/PlayerPrefsSavedGameDataStorer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/PlayerPrefsSavedGameDataStorer.cs.meta
new file mode 100644
index 000000000..f0d2b6650
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/PlayerPrefsSavedGameDataStorer.cs.meta
@@ -0,0 +1,17 @@
+fileFormatVersion: 2
+guid: d2daaaccb4e2a7c478635c51de24383f
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/PlayerPrefsSavedGameDataStorer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/SavedGameDataStorer.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/SavedGameDataStorer.cs
new file mode 100644
index 000000000..228cc1d36
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/SavedGameDataStorer.cs
@@ -0,0 +1,55 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using System.Collections;
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Abstract base class for "storage providers" that store saved game
+ /// data somewhere, such as PlayerPrefs or a disk file. To save asynchronously,
+ /// override StoreSavedGameDataAsync.
+ ///
+ public abstract class SavedGameDataStorer : MonoBehaviour
+ {
+ ///
+ /// Return the current progress (0-1) of the current async save operation.
+ ///
+ public virtual float progress { get; protected set; }
+
+ ///
+ /// Return true if the specified slot contains a saved game.
+ ///
+ public abstract bool HasDataInSlot(int slotNumber);
+
+ ///
+ /// Store saved game data in the specified slot.
+ ///
+ public abstract void StoreSavedGameData(int slotNumber, SavedGameData savedGameData);
+
+ ///
+ /// Retrieve saved game data from the specified slot, or null if no saved game in the slot.
+ ///
+ public abstract SavedGameData RetrieveSavedGameData(int slotNumber);
+
+ ///
+ /// Delete the saved game from the specified slot if present.
+ ///
+ public abstract void DeleteSavedGameData(int slotNumber);
+
+ ///
+ /// Asynchronously store the saved game data in the specified slot. The base version of
+ /// this method just calls the synchronous version, StoreSavedGameData(). If you override
+ /// it, keep the progress property updated so any watchers will know how far along it is.
+ ///
+ public virtual IEnumerator StoreSavedGameDataAsync(int slotNumber, SavedGameData savedGameData)
+ {
+ StoreSavedGameData(slotNumber, savedGameData);
+ progress = 1;
+ yield break;
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/SavedGameDataStorer.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/SavedGameDataStorer.cs.meta
new file mode 100644
index 000000000..b78364d25
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/SavedGameDataStorer.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 31e0a176945f13c4190f1e12404f5a59
+timeCreated: 1485133071
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Storers/SavedGameDataStorer.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions.meta
new file mode 100644
index 000000000..9455abaec
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 9135c2cb1b8e8bc4a9ed899b27f52cc2
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/LoadingScreenProgressBar.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/LoadingScreenProgressBar.cs
new file mode 100644
index 000000000..87d0cf7d3
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/LoadingScreenProgressBar.cs
@@ -0,0 +1,23 @@
+using UnityEngine;
+using UnityEngine.UI;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Manages a loading screen progress bar.
+ ///
+ [AddComponentMenu("")] // Use wrapper.
+ public class LoadingScreenProgressBar : MonoBehaviour
+ {
+
+ [Tooltip("Progress bar slider. Value should should be 0-1.")]
+ public Slider slider;
+
+ private void Update()
+ {
+ if (slider == null) return;
+ slider.value = (SaveSystem.currentAsyncOperation != null) ? SaveSystem.currentAsyncOperation.progress : 1;
+ }
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/LoadingScreenProgressBar.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/LoadingScreenProgressBar.cs.meta
new file mode 100644
index 000000000..05cf24c77
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/LoadingScreenProgressBar.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: 49eaafaefb9267d45857d21dbfff8149
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/LoadingScreenProgressBar.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/SceneTransitionManager.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/SceneTransitionManager.cs
new file mode 100644
index 000000000..985750606
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/SceneTransitionManager.cs
@@ -0,0 +1,28 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System.Collections;
+
+namespace PixelCrushers
+{
+
+ public abstract class SceneTransitionManager : MonoBehaviour
+ {
+
+ public virtual IEnumerator LeaveScene()
+ {
+ yield break;
+ }
+
+ public virtual IEnumerator EnterScene()
+ {
+ yield break;
+ }
+
+ ///
+ /// Called while loading a scene. You can override this method to update a progress indicator.
+ ///
+ public virtual void OnLoading(float progress) { }
+
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/SceneTransitionManager.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/SceneTransitionManager.cs.meta
new file mode 100644
index 000000000..aeaa72f62
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/SceneTransitionManager.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 5440095343589ab48afef11b771f8647
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/SceneTransitionManager.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/StandardSceneTransitionManager.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/StandardSceneTransitionManager.cs
new file mode 100644
index 000000000..1ef69eb78
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/StandardSceneTransitionManager.cs
@@ -0,0 +1,106 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+#if UNITY_5_3_OR_NEWER
+using UnityEngine;
+using UnityEngine.Events;
+using UnityEngine.SceneManagement;
+using System;
+using System.Collections;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// This implementation of SceneTransitionManager plays optional outro and
+ /// intro animations, and optionally loads a loading scene.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class StandardSceneTransitionManager : SceneTransitionManager
+ {
+
+ [Tooltip("Pause time during the transition.")]
+ public bool pauseDuringTransition = true;
+
+ [Serializable]
+ public class TransitionInfo
+ {
+ [Tooltip("Animator for this transition.")]
+ public Animator animator;
+ [Tooltip("Trigger parameter to set.")]
+ public string trigger;
+ [Tooltip("Duration to wait for the animation.")]
+ public float animationDuration;
+ [Tooltip("Total duration to wait for the transition.")]
+ public float minTransitionDuration;
+ public UnityEvent onTransitionStart = new UnityEvent();
+ public UnityEvent onTransitionEnd = new UnityEvent();
+ public void TriggerAnimation()
+ {
+ if (animator == null || string.IsNullOrEmpty(trigger)) return;
+ animator.SetTrigger(trigger);
+ }
+ }
+
+ [Tooltip("Transition to play before leaving the current scene.")]
+ public TransitionInfo leaveSceneTransition = new TransitionInfo();
+
+ [Tooltip("If set, show this loading scene while loading the real destination scene asynchronously.")]
+ public string loadingSceneName;
+
+ [Tooltip("Transition to play after entering the new scene.")]
+ public TransitionInfo enterSceneTransition = new TransitionInfo();
+
+ protected WaitForEndOfFrame endOfFrame = new WaitForEndOfFrame();
+
+ public override IEnumerator LeaveScene()
+ {
+ leaveSceneTransition.onTransitionStart.Invoke();
+ var startTime = Time.realtimeSinceStartup;
+ var minAnimationTime = startTime + leaveSceneTransition.animationDuration;
+ var minEndTime = startTime + Mathf.Max(leaveSceneTransition.minTransitionDuration, leaveSceneTransition.animationDuration);
+ if (pauseDuringTransition)
+ {
+ Time.timeScale = 0;
+ }
+ leaveSceneTransition.TriggerAnimation();
+ while (Time.realtimeSinceStartup < minAnimationTime)
+ {
+ yield return null;
+ }
+ if (!string.IsNullOrEmpty(loadingSceneName))
+ {
+ yield return SceneManager.LoadSceneAsync(loadingSceneName);
+ }
+ while (Time.realtimeSinceStartup < minEndTime)
+ {
+ yield return null;
+ }
+ leaveSceneTransition.onTransitionEnd.Invoke();
+ }
+
+ public override IEnumerator EnterScene()
+ {
+ if (string.IsNullOrEmpty(loadingSceneName)) yield return endOfFrame;
+ enterSceneTransition.onTransitionStart.Invoke();
+ var startTime = Time.realtimeSinceStartup;
+ var minAnimationTime = startTime + enterSceneTransition.animationDuration;
+ var minEndTime = startTime + Mathf.Max(enterSceneTransition.minTransitionDuration, enterSceneTransition.animationDuration);
+ enterSceneTransition.TriggerAnimation();
+ while (Time.realtimeSinceStartup < minAnimationTime)
+ {
+ yield return null;
+ }
+ while (Time.realtimeSinceStartup < minEndTime)
+ {
+ yield return null;
+ }
+ if (pauseDuringTransition)
+ {
+ Time.timeScale = 1; //---Always reset to normal time.
+ }
+ enterSceneTransition.onTransitionEnd.Invoke();
+ }
+
+ }
+}
+#endif
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/StandardSceneTransitionManager.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/StandardSceneTransitionManager.cs.meta
new file mode 100644
index 000000000..cf7993948
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/StandardSceneTransitionManager.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 6b12058b240484049bc528f506e5ff74
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Save System/Transitions/StandardSceneTransitionManager.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text.meta
new file mode 100644
index 000000000..b29ff788f
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: 241ea8b53eff5764bb78d5f5c0f62969
+folderAsset: yes
+timeCreated: 1549415913
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/EncodingType.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/EncodingType.cs
new file mode 100644
index 000000000..cc1ff6843
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/EncodingType.cs
@@ -0,0 +1,38 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using System.Text;
+
+namespace PixelCrushers
+{
+
+ public enum EncodingType
+ {
+ Default,
+ ASCII,
+ Unicode,
+ UTF7,
+ UTF8,
+ UTF32,
+ ISO_8859_1
+ }
+
+ public static class EncodingTypeTools
+ {
+
+ public static Encoding GetEncoding(EncodingType encodingType)
+ {
+ switch (encodingType)
+ { // Return values modified for WinRT compatibility:
+ case EncodingType.ASCII: return Encoding.UTF8; //Encoding.ASCII;
+ case EncodingType.Unicode: return Encoding.Unicode;
+ case EncodingType.UTF32: return Encoding.Unicode; //Encoding.UTF32;
+ case EncodingType.UTF7: return Encoding.Unicode; //Encoding.UTF7;
+ case EncodingType.UTF8: return Encoding.UTF8;
+ case EncodingType.ISO_8859_1: return Encoding.GetEncoding("iso-8859-1");
+ default: return Encoding.UTF8; //Encoding.Default;
+ }
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/EncodingType.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/EncodingType.cs.meta
new file mode 100644
index 000000000..911ec9e25
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/EncodingType.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 19ae5047b2d72644d8fa53aceb4fe647
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Text/EncodingType.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/GlobalTextTable.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/GlobalTextTable.cs
new file mode 100644
index 000000000..b9973b226
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/GlobalTextTable.cs
@@ -0,0 +1,96 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Maintains a reference to a global TextTable that other scripts can use.
+ ///
+ [AddComponentMenu("")] // Use wrapper instead.
+ public class GlobalTextTable : MonoBehaviour
+ {
+
+ [Tooltip("The global TextTable.")]
+ [SerializeField]
+ private TextTable m_textTable = null;
+
+ protected static GlobalTextTable s_instance = null;
+
+#if UNITY_2019_3_OR_NEWER && UNITY_EDITOR
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
+ static void InitStaticVariables()
+ {
+ s_instance = null;
+ }
+#endif
+
+ protected virtual void Awake()
+ {
+ if (s_instance == null) s_instance = this;
+ }
+
+ protected virtual void OnDestroy()
+ {
+ if (s_instance == this) s_instance = null;
+ }
+
+ ///
+ /// Current instance of GlobalTextTable.
+ ///
+ public static GlobalTextTable instance { get { return s_instance; } }
+
+ ///
+ /// Current global text table.
+ ///
+ public static TextTable textTable
+ {
+ get
+ {
+ return (instance != null) ? instance.m_textTable : null;
+ }
+ set
+ {
+ if (instance != null)
+ {
+ instance.m_textTable = value;
+ if (UILocalizationManager.instance != null) UILocalizationManager.instance.UpdateUIs(currentLanguage);
+ }
+ }
+ }
+
+ ///
+ /// The current language to use.
+ ///
+ public static string currentLanguage
+ {
+ get { return (UILocalizationManager.instance != null) ? UILocalizationManager.instance.currentLanguage : string.Empty; }
+ set { if (UILocalizationManager.instance != null) UILocalizationManager.instance.currentLanguage = value; }
+ }
+
+ ///
+ /// Looks up a field value in the global text table.
+ ///
+ /// Field name.
+ /// The field value in the global text table for the current language.
+ public static string Lookup(StringField fieldName)
+ {
+ if (fieldName == null) return string.Empty;
+ return Lookup(fieldName.value);
+ }
+
+ ///
+ /// Looks up a field value in the global text table.
+ ///
+ /// Field name.
+ /// The field value in the global text table for the current language.
+ public static string Lookup(string fieldName)
+ {
+ if (string.IsNullOrEmpty(fieldName)) return string.Empty;
+ if (textTable == null) return fieldName;
+ return textTable.GetFieldTextForLanguage(fieldName, currentLanguage);
+ }
+
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/GlobalTextTable.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/GlobalTextTable.cs.meta
new file mode 100644
index 000000000..3952cdbd6
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/GlobalTextTable.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 89cd79381bb08a84b93f87fd3eaa667a
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Text/GlobalTextTable.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringAsset.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringAsset.cs
new file mode 100644
index 000000000..803c9a318
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringAsset.cs
@@ -0,0 +1,32 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// A StringAsset is a ScriptableObject that encapsulates a string. It's useful
+ /// to share references to a string, where the value of that string can change.
+ ///
+ public class StringAsset : ScriptableObject
+ {
+
+ [TextArea(minLines:3, maxLines:20)]
+ [SerializeField]
+ private string m_text;
+
+ public string text
+ {
+ get { return m_text; }
+ set { m_text = value; }
+ }
+
+ public override string ToString()
+ {
+ return text;
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringAsset.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringAsset.cs.meta
new file mode 100644
index 000000000..302eff75c
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringAsset.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: fffcb85a9fc7e36429faf9ed73ffb5d0
+timeCreated: 1529972386
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: 4f2936fe28db54943a07b18c4366f96d, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringAsset.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringField.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringField.cs
new file mode 100644
index 000000000..f72f3b2aa
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringField.cs
@@ -0,0 +1,250 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using System;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// A StringField is an object that can refer to a string, StringAsset, or
+ /// field in a TextTable.
+ ///
+ [Serializable]
+ public class StringField : IEquatable
+ {
+
+ [Tooltip("The string that holds the value of this string field. Unused if String Asset or Text Table is assigned.")]
+ [SerializeField]
+ private string m_text;
+
+ [Tooltip("The String Asset that holds the value of this string field. Unused if Text or Text Table is assigned.")]
+ [SerializeField]
+ private StringAsset m_stringAsset;
+
+ [Tooltip("The Text Table that holds the value of this string field. Unused if Text or String Asset is assigned.")]
+ [SerializeField]
+ private TextTable m_textTable;
+
+ [Tooltip("The field ID in the Text Table.")]
+ [SerializeField]
+ private int m_textTableFieldID;
+
+ ///
+ /// The string that holds the value of this string field. Unused if String Asset or Text Table is assigned.
+ ///
+ public string text
+ {
+ get { return m_text; }
+ set { m_text = value; }
+ }
+
+ ///
+ /// The String Asset that holds the value of this string field. Unused if Text or Text Table is assigned.
+ ///
+ public StringAsset stringAsset
+ {
+ get { return m_stringAsset; }
+ set { m_stringAsset = value; }
+ }
+
+ ///
+ /// The Text Table that holds the value of this string field. Unused if Text or String Asset is assigned.
+ ///
+ public TextTable textTable
+ {
+ get { return m_textTable; }
+ set { m_textTable = value; }
+ }
+
+ ///
+ /// The field ID in the Text Table.
+ ///
+ public int textTableFieldID
+ {
+ get { return m_textTableFieldID; }
+ set { m_textTableFieldID = value; }
+ }
+
+ ///
+ /// Gets or sets the value of the String Field. If setting, you can only set the text; this property doesn't
+ /// change String Assets or Text Tables.
+ ///
+ public string value
+ {
+ get
+ {
+ if (textTable != null)
+ {
+ return Application.isPlaying
+ ? textTable.GetFieldTextForLanguage(textTableFieldID, UILocalizationManager.instance.currentLanguage)
+ : textTable.GetFieldText(textTableFieldID);
+ }
+ else if (stringAsset != null)
+ {
+ return stringAsset.text;
+ }
+ else
+ {
+ return text;
+ }
+ }
+ set
+ {
+ if (textTable != null)
+ {
+ // Do nothing. Don't change assets.
+ }
+ else if (stringAsset != null)
+ {
+ // Do nothing. Don't change assets.
+ }
+ else
+ {
+ text = value;
+ }
+ }
+ }
+
+ public override string ToString()
+ {
+ return value;
+ }
+
+ public StringField()
+ {
+ this.text = string.Empty;
+ this.stringAsset = null;
+ this.textTable = null;
+ this.textTableFieldID = 0;
+ }
+
+ public StringField(string text)
+ {
+ this.text = text;
+ this.stringAsset = null;
+ this.textTable = null;
+ this.textTableFieldID = 0;
+ }
+
+ public StringField(StringAsset stringAsset)
+ {
+ this.text = string.Empty;
+ this.stringAsset = stringAsset;
+ this.textTable = null;
+ this.textTableFieldID = 0;
+ }
+
+ public StringField(TextTable textTable, int fieldID)
+ {
+ this.text = string.Empty;
+ this.stringAsset = null;
+ this.textTable = textTable;
+ this.textTableFieldID = fieldID;
+ }
+
+ public StringField(StringField source)
+ {
+ this.text = string.Empty;
+ this.stringAsset = null;
+ this.textTable = null;
+ this.textTableFieldID = 0;
+ if (source == null) return;
+ if (!string.IsNullOrEmpty(source.text))
+ {
+ this.text = source.text;
+ }
+ else if (source.stringAsset != null)
+ {
+ this.stringAsset = source.stringAsset;
+ }
+ else if (source.textTable != null)
+ {
+ this.textTable = source.textTable;
+ this.textTableFieldID = source.textTableFieldID;
+ }
+ }
+
+ public void SetDefaultTextTable(TextTable textTable)
+ {
+ if (string.IsNullOrEmpty(this.text) && this.stringAsset == null && this.textTable == null)
+ {
+ this.textTable = textTable;
+ }
+ }
+
+ public static bool operator ==(StringField obj1, StringField obj2)
+ {
+ if (ReferenceEquals(obj1, obj2)) return true;
+ if (ReferenceEquals(obj1, null) && ReferenceEquals(obj2, null)) return true;
+ if (ReferenceEquals(obj1, null) || ReferenceEquals(obj2, null)) return false;
+ return string.Equals(obj1.value, obj2.value);
+ }
+
+ public static bool operator !=(StringField obj1, StringField obj2)
+ {
+ if (ReferenceEquals(obj1, obj2)) return false;
+ if (ReferenceEquals(obj1, null) && ReferenceEquals(obj2, null)) return false;
+ if (ReferenceEquals(obj1, null) || ReferenceEquals(obj2, null)) return true;
+ return !string.Equals(obj1.value, obj2.value);
+ }
+
+ public bool Equals(StringField other)
+ {
+ return (other != null) ? string.Equals(other.value, value) : false;
+ }
+
+ public override bool Equals(object obj)
+ {
+ if (obj is StringField)
+ {
+ return string.Equals(value, (obj as StringField).value);
+ }
+ else if (obj is StringAsset)
+ {
+ return string.Equals(value, (obj as StringAsset).text);
+ }
+ else if (obj is string)
+ {
+ return string.Equals(value, (obj as string));
+ }
+ else
+ {
+ return base.Equals(obj);
+ }
+ }
+
+ public override int GetHashCode()
+ {
+ return value.GetHashCode();
+ }
+
+ ///
+ /// An empty StringField, similar to string.Empty.
+ ///
+ public static readonly StringField empty = new StringField();
+
+ ///
+ /// Similar to string.IsNullOrEmpty.
+ ///
+ /// The StringField to check.
+ /// true if the StringField is null or empty; otherwise false.
+ public static bool IsNullOrEmpty(StringField stringField)
+ {
+ return (stringField == null || string.IsNullOrEmpty(stringField.value));
+ }
+
+ ///
+ /// Returns the string value of a StringField. This function is null safe.
+ /// If the StringField parameter is null, it returns an empty string.
+ ///
+ /// The StringField whose value to return.
+ /// The string value.
+ public static string GetStringValue(StringField stringField)
+ {
+ return (stringField == null) ? string.Empty : stringField.value;
+ }
+
+ }
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringField.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringField.cs.meta
new file mode 100644
index 000000000..4689b9ff2
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringField.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 7def9f90cc67dd648bb74c78c1938ab0
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringField.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringFieldTextAreaAttribute.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringFieldTextAreaAttribute.cs
new file mode 100644
index 000000000..06ca5e5b4
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringFieldTextAreaAttribute.cs
@@ -0,0 +1,21 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// Attribute to draw a StringField's text value as a multi-line text area.
+ ///
+ public class StringFieldTextAreaAttribute : PropertyAttribute
+ {
+
+ public bool expandHeight;
+
+ public StringFieldTextAreaAttribute(bool expandHeight = true)
+ {
+ this.expandHeight = expandHeight;
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringFieldTextAreaAttribute.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringFieldTextAreaAttribute.cs.meta
new file mode 100644
index 000000000..3014723eb
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringFieldTextAreaAttribute.cs.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: 04593812f8db9a744b7dc84e908624b7
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Text/StringFieldTextAreaAttribute.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/TextTable.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/TextTable.cs
new file mode 100644
index 000000000..f64dffaee
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/TextTable.cs
@@ -0,0 +1,723 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using System;
+using System.Collections.Generic;
+using UnityEngine;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// A TextTable is a 2D table of languages and fields.
+ ///
+ public class TextTable : ScriptableObject, ISerializationCallbackReceiver
+ {
+
+ private static int s_currentLanguageID = 0;
+
+ ///
+ /// If a language's field value is blank, use the default language's field value.
+ ///
+ public static bool useDefaultLanguageForBlankTranslations
+ {
+ get { return m_useDefaultLanguageForBlankTranslations; }
+ set { m_useDefaultLanguageForBlankTranslations = value; }
+ }
+ private static bool m_useDefaultLanguageForBlankTranslations = true;
+
+ private Dictionary m_languages = new Dictionary(); //
+
+ private Dictionary m_fields = new Dictionary(); //
+
+ ///
+ /// ID of the current language.
+ ///
+ public static int currentLanguageID
+ {
+ get { return s_currentLanguageID; }
+ set { s_currentLanguageID = value; }
+ }
+
+ public Dictionary languages //
+ {
+ get { return m_languages; }
+ set { m_languages = value; }
+ }
+
+ public Dictionary fields //
+ {
+ get { return m_fields; }
+ set { m_fields = value; }
+ }
+
+ [SerializeField]
+ private List m_languageKeys = new List();
+
+ [SerializeField]
+ private List m_languageValues = new List();
+
+ [SerializeField]
+ private List m_fieldKeys = new List();
+
+ [SerializeField]
+ private List m_fieldValues = new List();
+
+ [SerializeField]
+ private int m_nextLanguageID = 0;
+
+ [SerializeField]
+ private int m_nextFieldID = 1;
+
+ public int nextLanguageID { get { return m_nextLanguageID; } }
+
+ public int nextFieldID { get { return m_nextFieldID; } }
+
+ #region Serialization
+
+ public void OnBeforeSerialize()
+ {
+ m_languageKeys.Clear();
+ m_languageValues.Clear();
+ foreach (var kvp in languages)
+ {
+ m_languageKeys.Add(kvp.Key);
+ m_languageValues.Add(kvp.Value);
+ }
+ m_fieldKeys.Clear();
+ m_fieldValues.Clear();
+ foreach (var kvp in fields)
+ {
+ m_fieldKeys.Add(kvp.Key);
+ m_fieldValues.Add(kvp.Value);
+ }
+ }
+
+ public void OnAfterDeserialize()
+ {
+ languages = new Dictionary();
+ for (int i = 0; i != Math.Min(m_languageKeys.Count, m_languageValues.Count); i++)
+ {
+ languages.Add(m_languageKeys[i], m_languageValues[i]);
+ }
+ fields = new Dictionary();
+ for (int i = 0; i != Math.Min(m_fieldKeys.Count, m_fieldValues.Count); i++)
+ {
+ fields.Add(m_fieldKeys[i], m_fieldValues[i]);
+ }
+ }
+
+ #endregion
+
+ #region Languages
+
+ ///
+ /// Returns true if the text table has the named language.
+ ///
+ public bool HasLanguage(string languageName)
+ {
+ return string.IsNullOrEmpty(languageName) || languages.ContainsKey(languageName);
+ }
+
+ ///
+ /// Returns true if the text table has a language with the specified ID.
+ ///
+ public bool HasLanguage(int languageID)
+ {
+ return languageID == 0 || languages.ContainsValue(languageID);
+ }
+
+ ///
+ /// Returns the name of the language with the specified ID.
+ ///
+ public string GetLanguageName(int languageID)
+ {
+ // Enumerate manually to avoid garbage:
+ var enumerator = languages.GetEnumerator();
+ while (enumerator.MoveNext())
+ {
+ if (enumerator.Current.Value == languageID) return enumerator.Current.Key;
+ }
+ return string.Empty;
+ }
+
+ ///
+ /// Returns the ID of the named language.
+ ///
+ public int GetLanguageID(string languageName)
+ {
+ return languages.ContainsKey(languageName) ? languages[languageName] : 0;
+ }
+
+ ///
+ /// Returns the names of all languages in the text table.
+ ///
+ public string[] GetLanguageNames()
+ {
+ var names = new string[languages.Count];
+ languages.Keys.CopyTo(names, 0);
+ return names;
+ }
+
+ ///
+ /// Gets the IDs of all languages in the text table.
+ ///
+ public int[] GetLanguageIDs()
+ {
+ var ids = new int[languages.Count];
+ languages.Values.CopyTo(ids, 0);
+ return ids;
+ }
+
+ ///
+ /// Adds a language to the text table. The language will be assigned
+ /// a unique ID.
+ ///
+ public void AddLanguage(string languageName)
+ {
+ if (languages.ContainsKey(languageName)) return;
+ languages.Add(languageName, m_nextLanguageID++);
+ }
+
+ ///
+ /// Removes a language from the text table, including all of its fields.
+ ///
+ public void RemoveLanguage(string languageName)
+ {
+ if (!languages.ContainsKey(languageName)) return;
+ RemoveLanguageFromFields(languages[languageName]);
+ languages.Remove(languageName);
+ }
+
+ ///
+ /// Removes a language from the text table, including all of its fields.
+ ///
+ public void RemoveLanguage(int languageID)
+ {
+ RemoveLanguage(GetLanguageName(languageID));
+ }
+
+ ///
+ /// Removes all languages and fields.
+ ///
+ public void RemoveAll()
+ {
+ fields.Clear();
+ languages.Clear();
+ languages.Add("Default", 0);
+ m_nextLanguageID = 1;
+ m_nextFieldID = 1;
+ OnBeforeSerialize();
+ }
+
+ protected struct LanguageKeyValuePair // Temp object used to sort languages.
+ {
+ public string key;
+ public int value;
+ public LanguageKeyValuePair(string key, int value)
+ {
+ this.key = key;
+ this.value = value;
+ }
+ }
+
+ ///
+ /// Sort languages alphabetically, always keeping Default first.
+ ///
+ public void SortLanguages()
+ {
+ if (m_languageKeys.Count == 0) return;
+
+ // Extract Default language; always stays on top:
+ var defaultKey = m_languageKeys[0];
+ m_languageKeys.RemoveAt(0);
+ var defaultValue = m_languageValues[0];
+ m_languageValues.RemoveAt(0);
+
+ // Need to keep keys and values both in the same order.
+ // First create a single list:
+ var list = new List();
+ for (int i = 0; i < m_languageKeys.Count; i++)
+ {
+ list.Add(new LanguageKeyValuePair(m_languageKeys[i], m_languageValues[i]));
+ }
+
+ list.Sort(delegate (LanguageKeyValuePair a, LanguageKeyValuePair b) { return a.key.CompareTo(b.key); });
+
+ // Then update keys and values:
+ m_languageKeys.Clear();
+ m_languageValues.Clear();
+ m_languageKeys.Add(defaultKey); // Always keep Default first.
+ m_languageValues.Add(defaultValue);
+
+ for (int i = 0; i < list.Count; i++)
+ {
+ m_languageKeys.Add(list[i].key);
+ m_languageValues.Add(list[i].value);
+ }
+ OnAfterDeserialize();
+ }
+
+ #endregion
+
+ #region Fields
+
+ ///
+ /// Returns true if the text table has a field with the specified field ID.
+ ///
+ public bool HasField(int fieldID)
+ {
+ return fields.ContainsKey(fieldID);
+ }
+
+ ///
+ /// Returns true if the text table has a field with the specified name.
+ ///
+ public bool HasField(string fieldName)
+ {
+ return GetField(fieldName) != null;
+ }
+
+ ///
+ /// Looks up a field by ID.
+ ///
+ public TextTableField GetField(int fieldID)
+ {
+ return fields.ContainsKey(fieldID) ? fields[fieldID] : null;
+ }
+
+ ///
+ /// Looks up a field by name.
+ ///
+ public TextTableField GetField(string fieldName)
+ {
+ return GetField(GetFieldID(fieldName));
+ }
+
+ ///
+ /// Returns the ID associated with a field name.
+ ///
+ public int GetFieldID(string fieldName)
+ {
+ var enumerator = fields.GetEnumerator();
+ while (enumerator.MoveNext())
+ {
+ if (enumerator.Current.Value != null && string.Equals(enumerator.Current.Value.fieldName, fieldName)) return enumerator.Current.Key;
+ }
+ return 0;
+ }
+
+ ///
+ /// Returns the name of the field with the specified ID.
+ ///
+ public string GetFieldName(int fieldID)
+ {
+ return fields.ContainsKey(fieldID) ? fields[fieldID].fieldName : string.Empty;
+ }
+
+ ///
+ /// Returns true if the field has text for a specified language.
+ ///
+ public bool HasFieldTextForLanguage(int fieldID, int languageID)
+ {
+ var field = GetField(fieldID);
+ return (field != null) ? field.HasTextForLanguage(languageID) : false;
+ }
+
+ ///
+ /// Returns true if the field has text for a specified language.
+ ///
+ public bool HasFieldTextForLanguage(int fieldID, string languageName)
+ {
+ return HasFieldTextForLanguage(fieldID, GetLanguageID(languageName));
+ }
+
+ ///
+ /// Returns true if the field has text for a specified language.
+ ///
+ public bool HasFieldTextForLanguage(string fieldName, int languageID)
+ {
+ return HasFieldTextForLanguage(GetFieldID(fieldName), languageID);
+ }
+
+ ///
+ /// Returns true if the field has text for a specified language.
+ ///
+ public bool HasFieldTextForLanguage(string fieldName, string languageName)
+ {
+ return HasFieldTextForLanguage(GetFieldID(fieldName), GetLanguageID(languageName));
+ }
+
+ ///
+ /// Looks up a field's localized text for a specified language.
+ ///
+ public string GetFieldTextForLanguage(int fieldID, int languageID)
+ {
+ var field = GetField(fieldID);
+ if (field == null) return GetFieldName(fieldID);
+ string result;
+ if (field.HasTextForLanguage(languageID))
+ {
+ result = field.GetTextForLanguage(languageID).Replace(@"\n", "\n");
+ if (!string.IsNullOrEmpty(result)) return result;
+ }
+ var defaultText = field.GetTextForLanguage(0);
+ result = (!string.IsNullOrEmpty(defaultText) && useDefaultLanguageForBlankTranslations) ? defaultText : GetFieldName(fieldID);
+ return result.Replace(@"\n", "\n");
+ }
+
+ ///
+ /// Looks up a field's localized text for a specified language.
+ ///
+ public string GetFieldTextForLanguage(int fieldID, string languageName)
+ {
+ return GetFieldTextForLanguage(fieldID, GetLanguageID(languageName));
+ }
+
+ ///
+ /// Looks up a field's localized text for a specified language.
+ ///
+ public string GetFieldTextForLanguage(string fieldName, int languageID)
+ {
+ var field = GetField(fieldName);
+ if (field == null) return fieldName;
+ string result;
+ if (field.HasTextForLanguage(languageID))
+ {
+ result = field.GetTextForLanguage(languageID).Replace(@"\n", "\n");
+ if (!string.IsNullOrEmpty(result)) return result;
+ }
+ var defaultText = field.GetTextForLanguage(0);
+ result = (!string.IsNullOrEmpty(defaultText) && useDefaultLanguageForBlankTranslations) ? defaultText : fieldName;
+ return result.Replace(@"\n", "\n");
+ }
+ ///
+ /// Looks up a field's localized text for a specified language.
+ ///
+ public string GetFieldTextForLanguage(string fieldName, string languageName)
+ {
+ return GetFieldTextForLanguage(fieldName, GetLanguageID(languageName));
+ }
+
+ ///
+ /// Looks up a fields localized text for the current language specified by TextTable.currentLanguageID.
+ ///
+ public string GetFieldText(int fieldID)
+ {
+ return GetFieldTextForLanguage(fieldID, TextTable.currentLanguageID);
+ }
+
+ ///
+ /// Looks up a fields localized text for the current language specified by TextTable.currentLanguageID.
+ ///
+ public string GetFieldText(string fieldName)
+ {
+ return GetFieldTextForLanguage(fieldName, TextTable.currentLanguageID);
+ }
+
+ ///
+ /// Returns all field IDs in the text table.
+ ///
+ ///
+ public int[] GetFieldIDs()
+ {
+ var ids = new int[fields.Count];
+ fields.Keys.CopyTo(ids, 0);
+ return ids;
+ }
+
+ ///
+ /// Returns all field names in the text table.
+ ///
+ ///
+ public string[] GetFieldNames()
+ {
+ var names = new string[fields.Count];
+ int i = 0;
+ var enumerator = fields.GetEnumerator();
+ while (enumerator.MoveNext())
+ {
+ names[i++] = (enumerator.Current.Value != null) ? enumerator.Current.Value.fieldName : string.Empty;
+ }
+ return names;
+ }
+
+ ///
+ /// Adds a field to the text table.
+ ///
+ public void AddField(string fieldName)
+ {
+ if (HasField(fieldName)) return;
+ fields.Add(m_nextFieldID++, new TextTableField(fieldName));
+ }
+
+ ///
+ /// Sets a field's localized text for a specified language.
+ ///
+ public void SetFieldTextForLanguage(int fieldID, int languageID, string text)
+ {
+ if (!HasLanguage(languageID))
+ {
+ if (Debug.isDebugBuild) Debug.LogWarning("TextTable.SetLanguageText(" + fieldID + ", " + languageID + ", \"" + text + "\") failed: Language doesn't exist. Use Text Table Editor or AddLanguage() to add the language first.", this);
+ return;
+ }
+ var field = GetField(fieldID);
+ if (field == null)
+ {
+ if (Debug.isDebugBuild) Debug.LogWarning("TextTable.SetLanguageText(" + fieldID + ", " + languageID + ", \"" + text + "\") failed: Field doesn't exist. Use Text Table Editor or AddField() to add the field first.", this);
+ return;
+ }
+ field.SetTextForLanguage(languageID, text);
+ }
+
+ ///
+ /// Sets a field's localized text for a specified language.
+ ///
+ public void SetFieldTextForLanguage(string fieldName, int languageID, string text)
+ {
+ SetFieldTextForLanguage(GetFieldID(fieldName), languageID, text);
+ }
+
+ ///
+ /// Sets a field's localized text for a specified language.
+ ///
+ public void SetFieldTextForLanguage(int fieldID, string languageName, string text)
+ {
+ SetFieldTextForLanguage(fieldID, GetLanguageID(languageName), text);
+ }
+
+ ///
+ /// Sets a field's localized text for a specified language.
+ ///
+ public void SetFieldTextForLanguage(string fieldName, string languageName, string text)
+ {
+ SetFieldTextForLanguage(GetFieldID(fieldName), GetLanguageID(languageName), text);
+ }
+
+ ///
+ /// Removes a field from the text table.
+ ///
+ public void RemoveField(int fieldID)
+ {
+ fields.Remove(fieldID);
+ }
+
+ ///
+ /// Removes a field from the text table.
+ ///
+ public void RemoveField(string fieldName)
+ {
+ fields.Remove(GetFieldID(fieldName));
+ }
+
+ ///
+ /// Removes a language from all fields in the table.
+ ///
+ private void RemoveLanguageFromFields(int languageID)
+ {
+ var enumerator = fields.GetEnumerator();
+ while (enumerator.MoveNext())
+ {
+ if (enumerator.Current.Value != null) enumerator.Current.Value.RemoveLanguage(languageID);
+ }
+ }
+
+ ///
+ /// Removes all fields.
+ ///
+ public void RemoveAllFields()
+ {
+ fields.Clear();
+ m_nextFieldID = 1;
+ OnBeforeSerialize();
+ }
+
+ protected struct FieldKeyValuePair // Temp object used to sort fields.
+ {
+ public int key;
+ public TextTableField value;
+ public FieldKeyValuePair(int key, TextTableField value)
+ {
+ this.key = key;
+ this.value = value;
+ }
+ }
+
+ ///
+ /// Inserts a field to the text table.
+ ///
+ public void InsertField(int index, string fieldName)
+ {
+ if (HasField(fieldName)) return;
+ OnBeforeSerialize();
+ var id = m_nextFieldID++;
+ m_fieldKeys.Insert(index, id);
+ var field = new TextTableField(fieldName);
+ field.texts.Add(0, string.Empty);
+ m_fieldValues.Insert(index, field);
+ OnAfterDeserialize();
+ }
+
+ ///
+ /// Sort fields alphabetically.
+ ///
+ public void SortFields()
+ {
+ // Need to keep keys and values both in the same order.
+ // First create a single list:
+ var list = new List();
+ for (int i = 0; i < m_fieldKeys.Count; i++)
+ {
+ list.Add(new FieldKeyValuePair(m_fieldKeys[i], m_fieldValues[i]));
+ }
+
+ list.Sort(delegate (FieldKeyValuePair a, FieldKeyValuePair b) { return a.value.fieldName.CompareTo(b.value.fieldName); });
+
+ // Then update keys and values:
+ m_fieldKeys.Clear();
+ m_fieldValues.Clear();
+ for (int i = 0; i < list.Count; i++)
+ {
+ m_fieldKeys.Add(list[i].key);
+ m_fieldValues.Add(list[i].value);
+ }
+ OnAfterDeserialize();
+ }
+
+ public void ReorderFields(List order)
+ {
+ if (order == null) return;
+ OnBeforeSerialize();
+ var newKeys = new List();
+ var newValues = new List();
+ for (int i = 0; i < order.Count; i++)
+ {
+ var index = m_fieldValues.FindIndex(x => string.Equals(x.fieldName, order[i]));
+ if (index == -1) continue;
+ newKeys.Add(m_fieldKeys[index]);
+ newValues.Add(m_fieldValues[index]);
+ m_fieldKeys.RemoveAt(index);
+ m_fieldValues.RemoveAt(index);
+ }
+ newKeys.AddRange(m_fieldKeys);
+ newValues.AddRange(m_fieldValues);
+ m_fieldKeys = newKeys;
+ m_fieldValues = newValues;
+ OnAfterDeserialize();
+ }
+
+ #endregion
+
+ #region Merge
+
+ public void ImportOtherTextTable(TextTable other)
+ {
+ if (other == null || other == this) return;
+ foreach (var language in other.languages.Keys)
+ {
+ if (!HasLanguage(language)) AddLanguage(language);
+ }
+ foreach (var field in other.fields.Values)
+ {
+ AddField(field.fieldName);
+ foreach (var language in other.languages.Keys)
+ {
+ SetFieldTextForLanguage(field.fieldName, language, other.GetFieldTextForLanguage(field.fieldName, language));
+ }
+ }
+ }
+
+ #endregion
+
+ }
+
+ #region TextTableField
+
+ ///
+ /// A field in a TextTable.
+ ///
+ [Serializable]
+ public class TextTableField : ISerializationCallbackReceiver
+ {
+
+ [SerializeField]
+ private string m_fieldName;
+
+ private Dictionary m_texts = new Dictionary(); //
+
+ public string fieldName
+ {
+ get { return m_fieldName; }
+ set { m_fieldName = value; }
+ }
+
+ public Dictionary texts //
+ {
+ get { return m_texts; }
+ set { m_texts = value; }
+ }
+
+ [SerializeField]
+ private List m_keys = new List();
+
+ [SerializeField]
+ private List m_values = new List();
+
+ public TextTableField() { }
+
+ public TextTableField(string fieldName)
+ {
+ this.m_fieldName = fieldName;
+ }
+
+ public void OnBeforeSerialize()
+ {
+ m_keys.Clear();
+ m_values.Clear();
+ foreach (var kvp in texts)
+ {
+ m_keys.Add(kvp.Key);
+ m_values.Add(kvp.Value);
+ }
+ }
+
+ public void OnAfterDeserialize()
+ {
+ texts = new Dictionary();
+ for (int i = 0; i != Math.Min(m_keys.Count, m_values.Count); i++)
+ {
+ texts.Add(m_keys[i], m_values[i]);
+ }
+ }
+
+ public bool HasTextForLanguage(int languageID)
+ {
+ return texts.ContainsKey(languageID) && !string.IsNullOrEmpty(texts[languageID]);
+ }
+
+ public string GetTextForLanguage(int languageID)
+ {
+ return texts.ContainsKey(languageID) ? texts[languageID] : string.Empty;
+ }
+
+ public void SetTextForLanguage(int languageID, string text)
+ {
+ if (texts.ContainsKey(languageID))
+ {
+ texts[languageID] = text;
+ }
+ else
+ {
+ texts.Add(languageID, text);
+ }
+ }
+
+ public void RemoveLanguage(int languageID)
+ {
+ texts.Remove(languageID);
+ }
+
+ }
+
+ #endregion
+
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/TextTable.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/TextTable.cs.meta
new file mode 100644
index 000000000..5d080e4c6
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/Text/TextTable.cs.meta
@@ -0,0 +1,19 @@
+fileFormatVersion: 2
+guid: 749122ca687073649af3faaf57ce2dd5
+timeCreated: 1529972379
+licenseType: Store
+MonoImporter:
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {fileID: 2800000, guid: f043073205c249748ae651e1518ced37, type: 3}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/Text/TextTable.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/UI.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI.meta
new file mode 100644
index 000000000..fb2c50128
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI.meta
@@ -0,0 +1,9 @@
+fileFormatVersion: 2
+guid: c3c03844d240e714bb420f8a3052db1e
+folderAsset: yes
+timeCreated: 1549415914
+licenseType: Store
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/DeselectPreviousOnPointerEnter.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/DeselectPreviousOnPointerEnter.cs
new file mode 100644
index 000000000..1f95afa7d
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/DeselectPreviousOnPointerEnter.cs
@@ -0,0 +1,42 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEngine.EventSystems;
+using UnityEngine.UI;
+
+namespace PixelCrushers
+{
+
+ ///
+ /// This script deselects the previous selectable when the pointer enters this one.
+ ///
+ [AddComponentMenu("")] // Use wrapper.
+ [RequireComponent(typeof(Selectable))]
+ public class DeselectPreviousOnPointerEnter : MonoBehaviour, IPointerEnterHandler, IDeselectHandler, IEventSystemUser
+ {
+
+ private UnityEngine.EventSystems.EventSystem m_eventSystem = null;
+ public UnityEngine.EventSystems.EventSystem eventSystem
+ {
+ get
+ {
+ if (m_eventSystem != null) return m_eventSystem;
+ return UnityEngine.EventSystems.EventSystem.current;
+ }
+ set { m_eventSystem = value; }
+ }
+
+ public void OnPointerEnter(PointerEventData eventData)
+ {
+ if (!eventSystem.alreadySelecting)
+ {
+ eventSystem.SetSelectedGameObject(this.gameObject);
+ }
+ }
+
+ public void OnDeselect(BaseEventData eventData)
+ {
+ GetComponent().OnPointerExit(null);
+ }
+ }
+}
\ No newline at end of file
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/DeselectPreviousOnPointerEnter.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/DeselectPreviousOnPointerEnter.cs.meta
new file mode 100644
index 000000000..475e32bf6
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/DeselectPreviousOnPointerEnter.cs.meta
@@ -0,0 +1,20 @@
+fileFormatVersion: 2
+guid: a3ebede900a269b4ebfea0c87f255761
+timeCreated: 1590110083
+licenseType: Store
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/UI/DeselectPreviousOnPointerEnter.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/IEventSystemUser.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/IEventSystemUser.cs
new file mode 100644
index 000000000..bea3ebafc
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/IEventSystemUser.cs
@@ -0,0 +1,13 @@
+namespace PixelCrushers
+{
+
+ ///
+ /// Allows classes to have a reference to an EventSystem.
+ /// Useful for local multiplayer games that have more than one EventSystem.
+ ///
+ public interface IEventSystemUser
+ {
+
+ UnityEngine.EventSystems.EventSystem eventSystem { get; set; }
+ }
+}
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/IEventSystemUser.cs.meta b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/IEventSystemUser.cs.meta
new file mode 100644
index 000000000..c2e734414
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/IEventSystemUser.cs.meta
@@ -0,0 +1,18 @@
+fileFormatVersion: 2
+guid: 0dadd41a761dea8409d1e75b40f827ce
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
+AssetOrigin:
+ serializedVersion: 1
+ productId: 11672
+ packageName: Dialogue System for Unity
+ packageVersion: 2.2.46.1
+ assetPath: Assets/Plugins/Pixel Crushers/Common/Scripts/UI/IEventSystemUser.cs
+ uploadId: 667566
diff --git a/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/InputDeviceManager.cs b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/InputDeviceManager.cs
new file mode 100644
index 000000000..21684a824
--- /dev/null
+++ b/Assets/Plugins/Pixel Crushers/Common/Scripts/UI/InputDeviceManager.cs
@@ -0,0 +1,655 @@
+// Copyright (c) Pixel Crushers. All rights reserved.
+
+using UnityEngine;
+using UnityEngine.Events;
+using UnityEngine.SceneManagement;
+using System.Collections;
+using System.Collections.Generic;
+using System;
+
+#if USE_NEW_INPUT
+using UnityEngine.InputSystem;
+using UnityEngine.InputSystem.Controls;
+#endif
+
+namespace PixelCrushers
+{
+
+ public enum InputDevice { Joystick, Keyboard, Mouse, Touch }
+
+ ///
+ /// This script checks for joystick and keyboard input. If the player uses a joystick,
+ /// it enables autofocus. If the player uses the mouse or keyboard, it disables autofocus.
+ ///
+ [AddComponentMenu("")] // Use wrapper.
+ public class InputDeviceManager : MonoBehaviour
+ {
+
+ [Tooltip("Current input mode.")]
+ public InputDevice inputDevice = InputDevice.Joystick;
+
+ [Tooltip("If any of these keycodes are pressed, current device is joystick.")]
+ public KeyCode[] joystickKeyCodesToCheck = new KeyCode[] { KeyCode.JoystickButton0, KeyCode.JoystickButton1, KeyCode.JoystickButton2, KeyCode.JoystickButton7 };
+
+ [Tooltip("If any of these buttons are pressed, current device is joystick. Must be defined in Input Manager.")]
+ public string[] joystickButtonsToCheck = new string[0];
+
+ [Tooltip("If any of these axes are greater than Joystick Axis Threshold, current device is joystick. Must be defined in Input Manager.")]
+ public string[] joystickAxesToCheck = new string[0];
+ //--- Changed to prevent errors in new projects if user hasn't clicked "Add Input Definitions" yet.
+ //--- Added "Add Default Joystick Axes Check" button instead.
+ //public string[] joystickAxesToCheck = new string[] { "JoystickAxis1", "JoystickAxis2", "JoystickAxis3", "JoystickAxis4", "JoystickAxis6", "JoystickAxis7" };
+
+ [Tooltip("Joystick axis values must be above this threshold to switch to joystick mode.")]
+ public float joystickAxisThreshold = 0.5f;
+
+ [Tooltip("If any of these buttons are pressed, current device is keyboard (unless device is currently mouse).")]
+ public string[] keyButtonsToCheck = new string[0];
+
+ [Tooltip("If any of these keys are pressed, current device is keyboard (unless device is currently mouse).")]
+ public KeyCode[] keyCodesToCheck = new KeyCode[] { KeyCode.Escape };
+
+ public enum KeyInputSwitchesModeTo { Keyboard, Mouse }
+
+ [Tooltip("Which mode to switch to if user presses Key Buttons/Codes To Check.")]
+ public KeyInputSwitchesModeTo keyInputSwitchesModeTo = KeyInputSwitchesModeTo.Mouse;
+
+ [Tooltip("Always enable joystick/keyboard navigation even in Mouse mode.")]
+ public bool alwaysAutoFocus = false;
+
+ [Tooltip("Switch to mouse control if player clicks mouse buttons or moves mouse.")]
+ public bool detectMouseControl = true;
+
+ [Tooltip("If mouse moves more than this, current device is mouse.")]
+ public float mouseMoveThreshold = 0.1f;
+
+ [Tooltip("Hide cursor in joystick/key mode, show in mouse mode.")]
+ public bool controlCursorState = true;
+
+ [Tooltip("When paused and device is mouse, make sure cursor is visible.")]
+ public bool enforceCursorOnPause = false;
+
+ [Tooltip("Enable GraphicRaycasters (which detect cursor clicks on UI elements) only when device is mouse.")]
+ public bool controlGraphicRaycasters = false;
+
+ [Tooltip("If any of these keycodes are pressed, go back to the previous menu.")]
+ public KeyCode[] backKeyCodes = new KeyCode[] { KeyCode.JoystickButton1 };
+
+ [Tooltip("If any of these buttons are pressed, go back to the previous menu.")]
+ public string[] backButtons = new string[] { "Cancel" };
+
+ [Tooltip("'Submit' input button defined on Event System.")]
+ public string submitButton = "Submit";
+
+ [Tooltip("Survive scene changes and only allow one instance.")]
+ public bool singleton = true;
+
+ public UnityEvent onUseKeyboard = new UnityEvent();
+ public UnityEvent onUseJoystick = new UnityEvent();
+ public UnityEvent onUseMouse = new UnityEvent();
+ public UnityEvent onUseTouch = new UnityEvent();
+
+ public delegate bool GetButtonDownDelegate(string buttonName);
+ public delegate float GetAxisDelegate(string axisName);
+
+ public GetButtonDownDelegate GetButtonDown = null;
+ public GetButtonDownDelegate GetButtonUp = null;
+ public GetAxisDelegate GetInputAxis = null;
+
+ private Vector3 m_lastMousePosition;
+ private bool m_ignoreMouse = false;
+ private CursorLockMode m_cursorLockMode = CursorLockMode.Locked;
+ private bool m_inputAllowed = true;
+
+ private static InputDeviceManager m_instance = null;
+ public static InputDeviceManager instance
+ {
+ get { return m_instance; }
+ set { m_instance = value; }
+ }
+
+ ///
+ /// Current input device detected by InputDeviceManager. May changed based on
+ /// input from other devices.
+ ///
+ public static InputDevice currentInputDevice
+ {
+ get
+ {
+ return (m_instance != null) ? m_instance.inputDevice : InputDevice.Joystick;
+ }
+ }
+
+ ///
+ /// Returns true if current input device uses mouse cursor.
+ ///
+ public static bool deviceUsesCursor
+ {
+ get { return currentInputDevice == InputDevice.Mouse; }
+ }
+
+ ///
+ /// Lock mode to use when locking cursor.
+ ///
+ public static CursorLockMode cursorLockMode
+ {
+ get { return (m_instance != null) ? m_instance.m_cursorLockMode : CursorLockMode.Locked; }
+ set { if (m_instance != null) m_instance.m_cursorLockMode = value; }
+ }
+
+ ///
+ /// Automatically select (and keep selected) a selectable on the current UIPanel.
+ ///
+ public static bool autoFocus
+ {
+ get { return (m_instance != null && instance.alwaysAutoFocus) || currentInputDevice == InputDevice.Joystick || currentInputDevice == InputDevice.Keyboard; }
+ }
+
+ public static bool isBackButtonDown
+ {
+ get { return (m_instance != null) ? m_instance.IsBackButtonDown() : false; }
+ }
+
+ ///
+ /// Allow user input?
+ ///
+ public static bool isInputAllowed
+ {
+ get { return (m_instance != null) ? m_instance.m_inputAllowed : true; }
+ set { if (m_instance != null) m_instance.m_inputAllowed = value; }
+ }
+
+ public static bool IsButtonDown(string buttonName)
+ {
+ if (!isInputAllowed) return false;
+ return (m_instance != null && m_instance.GetButtonDown != null) ? m_instance.GetButtonDown(buttonName) : DefaultGetButtonDown(buttonName);
+ }
+
+ public static bool IsButtonUp(string buttonName)
+ {
+ if (!isInputAllowed) return false;
+ return (m_instance != null && m_instance.GetButtonUp != null) ? m_instance.GetButtonUp(buttonName) : DefaultGetButtonUp(buttonName);
+ }
+
+ public static bool IsKeyDown(KeyCode keyCode)
+ {
+ if (!isInputAllowed) return false;
+ return DefaultGetKeyDown(keyCode);
+ }
+
+ public static bool IsAnyKeyDown()
+ {
+ if (!isInputAllowed) return false;
+ return DefaultGetAnyKeyDown();
+ }
+
+ public static float GetAxis(string axisName)
+ {
+ if (!isInputAllowed) return 0;
+ return (m_instance != null && m_instance.GetInputAxis != null) ? m_instance.GetInputAxis(axisName) : DefaultGetAxis(axisName);
+ }
+
+ public static Vector3 GetMousePosition()
+ {
+ if (!isInputAllowed) return Vector3.zero;
+ return DefaultGetMousePosition();
+ }
+
+#if UNITY_2019_3_OR_NEWER && UNITY_EDITOR
+ [RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.SubsystemRegistration)]
+ static void InitStaticVariables()
+ {
+ m_instance = null;
+#if USE_NEW_INPUT
+ inputActionDict = new Dictionary();
+ m_specialKeyCodeDict = null;
+#endif
+ }
+#endif
+
+ public void Awake()
+ {
+ if (m_instance != null && singleton)
+ {
+ Destroy(gameObject);
+ }
+ else
+ {
+ m_instance = this;
+ GetButtonDown = DefaultGetButtonDown;
+ GetButtonUp = DefaultGetButtonUp;
+ GetInputAxis = DefaultGetAxis;
+ if (singleton)
+ {
+#if UNITY_EDITOR
+ if (Application.isPlaying)
+ { // If GameObject is hidden in Scene view, DontDestroyOnLoad will report (harmless) error.
+ UnityEditor.SceneVisibilityManager.instance.Show(gameObject, true);
+ }
+#endif
+ transform.SetParent(null);
+ DontDestroyOnLoad(gameObject);
+ }
+ }
+ }
+
+ public void OnDestroy()
+ {
+ SceneManager.sceneLoaded -= OnSceneLoaded;
+ }
+
+ public void Start()
+ {
+ m_lastMousePosition = GetMousePosition();
+ SetInputDevice(inputDevice);
+ BrieflyIgnoreMouseMovement();
+ SceneManager.sceneLoaded += OnSceneLoaded;
+#if USE_NEW_INPUT
+ InputSystem.onDeviceChange += OnInputSystemDeviceChange;
+#endif
+ }
+
+#if USE_NEW_INPUT
+ private void OnInputSystemDeviceChange(UnityEngine.InputSystem.InputDevice device, InputDeviceChange change)
+ {
+ if (change == InputDeviceChange.Added ||
+ (change == InputDeviceChange.UsageChanged && device.lastUpdateTime >= Time.time - 1))
+ {
+ if (device is Joystick || device is Gamepad)
+ {
+ SetInputDevice(InputDevice.Joystick);
+ }
+ else if (device is Keyboard)
+ {
+ SetInputDevice((keyInputSwitchesModeTo == KeyInputSwitchesModeTo.Mouse) ? InputDevice.Mouse : InputDevice.Keyboard);
+ }
+ else
+ {
+ SetInputDevice(InputDevice.Mouse);
+ }
+ }
+ }
+#endif
+
+ private void OnSceneLoaded(UnityEngine.SceneManagement.Scene scene, LoadSceneMode mode)
+ {
+ BrieflyIgnoreMouseMovement();
+ }
+
+ public void SetInputDevice(InputDevice newDevice)
+ {
+ inputDevice = newDevice;
+ m_lastMousePosition = GetMousePosition();
+ SetCursor(deviceUsesCursor);
+ SetGraphicRaycasters(deviceUsesCursor);
+ switch (inputDevice)
+ {
+ case InputDevice.Joystick:
+ onUseJoystick.Invoke();
+ break;
+ case InputDevice.Keyboard:
+ onUseKeyboard.Invoke();
+ break;
+ case InputDevice.Mouse:
+ var eventSystem = UnityEngine.EventSystems.EventSystem.current;
+ var currentSelectable = (eventSystem != null && eventSystem.currentSelectedGameObject != null) ? eventSystem.currentSelectedGameObject.GetComponent