From faf829348b73b5b936d46e94288ce872b28f3964 Mon Sep 17 00:00:00 2001 From: Alex Huddleston Date: Wed, 9 May 2018 20:42:22 -0500 Subject: [PATCH] Project started, GitMerge added. --- Angels and Demons/Assets/Editor.meta | 5 + .../Assets/Editor/GameObjectExtensions.cs | 67 ++++ .../Editor/GameObjectExtensions.cs.meta | 8 + .../Assets/Editor/GameObjectMergeActions.cs | 362 ++++++++++++++++++ .../Editor/GameObjectMergeActions.cs.meta | 8 + .../Assets/Editor/GitMergeWindow.cs | 255 ++++++++++++ .../Assets/Editor/GitMergeWindow.cs.meta | 8 + .../Assets/Editor/MergeAction.cs | 154 ++++++++ .../Assets/Editor/MergeAction.cs.meta | 8 + .../Assets/Editor/MergeActionChangeValues.cs | 268 +++++++++++++ .../Editor/MergeActionChangeValues.cs.meta | 8 + .../Editor/MergeActionDeleteComponent.cs | 71 ++++ .../Editor/MergeActionDeleteComponent.cs.meta | 8 + .../Editor/MergeActionDeleteGameObject.cs | 57 +++ .../MergeActionDeleteGameObject.cs.meta | 8 + .../Assets/Editor/MergeActionExistence.cs | 23 ++ .../Editor/MergeActionExistence.cs.meta | 8 + .../Assets/Editor/MergeActionNewComponent.cs | 66 ++++ .../Editor/MergeActionNewComponent.cs.meta | 8 + .../Assets/Editor/MergeActionNewGameObject.cs | 59 +++ .../Editor/MergeActionNewGameObject.cs.meta | 8 + .../Assets/Editor/MergeActionParenting.cs | 97 +++++ .../Editor/MergeActionParenting.cs.meta | 8 + .../Assets/Editor/MergeManager.cs | 123 ++++++ .../Assets/Editor/MergeManager.cs.meta | 8 + .../Assets/Editor/MergeManagerPrefab.cs | 149 +++++++ .../Assets/Editor/MergeManagerPrefab.cs.meta | 8 + .../Assets/Editor/MergeManagerScene.cs | 118 ++++++ .../Assets/Editor/MergeManagerScene.cs.meta | 8 + .../Assets/Editor/ObjectDictionaries.cs | 262 +++++++++++++ .../Assets/Editor/ObjectDictionaries.cs.meta | 8 + .../Assets/Editor/ObjectExtensions.cs | 24 ++ .../Assets/Editor/ObjectExtensions.cs.meta | 8 + .../Assets/Editor/ObjectIDFinder.cs | 31 ++ .../Assets/Editor/ObjectIDFinder.cs.meta | 8 + Angels and Demons/Assets/Editor/Resources.cs | 35 ++ .../Assets/Editor/Resources.cs.meta | 8 + .../Assets/Editor/Resources.meta | 5 + .../Assets/Editor/Resources/GitMergeBox.psd | Bin 0 -> 23498 bytes .../Editor/Resources/GitMergeBox.psd.meta | 46 +++ .../Assets/Editor/Resources/GitMergeLogo.psd | Bin 0 -> 88199 bytes .../Editor/Resources/GitMergeLogo.psd.meta | 46 +++ .../Editor/Resources/GitMergeStyles.asset | Bin 0 -> 10660 bytes .../Resources/GitMergeStyles.asset.meta | 4 + .../Assets/Editor/Resources/Licenses.txt | 1 + .../Assets/Editor/Resources/Licenses.txt.meta | 4 + .../Editor/SerializedPropertyExtensions.cs | 181 +++++++++ .../SerializedPropertyExtensions.cs.meta | 8 + Angels and Demons/Assets/Editor/VCS.cs | 71 ++++ Angels and Demons/Assets/Editor/VCS.cs.meta | 8 + .../Assets/Editor/VCSException.cs | 10 + .../Assets/Editor/VCSException.cs.meta | 8 + Angels and Demons/Assets/Editor/VCSGit.cs | 57 +++ .../Assets/Editor/VCSGit.cs.meta | 8 + Angels and Demons/Assets/LICENSE.md | 339 ++++++++++++++++ Angels and Demons/Assets/LICENSE.md.meta | 4 + Angels and Demons/Assets/README.md | 17 + Angels and Demons/Assets/README.md.meta | 4 + Angels and Demons/Assets/Scenes.meta | 8 + Angels and Demons/Assets/Scenes/Main.unity | Bin 0 -> 13024 bytes .../Assets/Scenes/Main.unity.meta | 7 + Angels and Demons/Packages/manifest.json | 4 + .../ProjectSettings/AudioManager.asset | Bin 0 -> 4144 bytes .../ProjectSettings/ClusterInputManager.asset | Bin 0 -> 4104 bytes .../ProjectSettings/DynamicsManager.asset | Bin 0 -> 4332 bytes .../ProjectSettings/EditorBuildSettings.asset | Bin 0 -> 4164 bytes .../ProjectSettings/EditorSettings.asset | Bin 0 -> 4192 bytes .../ProjectSettings/GraphicsSettings.asset | Bin 0 -> 4406 bytes .../ProjectSettings/InputManager.asset | Bin 0 -> 5520 bytes .../ProjectSettings/NavMeshAreas.asset | Bin 0 -> 4464 bytes .../ProjectSettings/NetworkManager.asset | Bin 0 -> 4112 bytes .../ProjectSettings/Physics2DSettings.asset | Bin 0 -> 4448 bytes .../ProjectSettings/PresetManager.asset | Bin 0 -> 4212 bytes .../ProjectSettings/ProjectSettings.asset | Bin 0 -> 56267 bytes .../ProjectSettings/ProjectVersion.txt | 1 + .../ProjectSettings/QualitySettings.asset | Bin 0 -> 5000 bytes .../ProjectSettings/TagManager.asset | Bin 0 -> 4324 bytes .../ProjectSettings/TimeManager.asset | Bin 0 -> 4116 bytes .../UnityConnectSettings.asset | Bin 0 -> 4276 bytes 79 files changed, 3211 insertions(+) create mode 100644 Angels and Demons/Assets/Editor.meta create mode 100644 Angels and Demons/Assets/Editor/GameObjectExtensions.cs create mode 100644 Angels and Demons/Assets/Editor/GameObjectExtensions.cs.meta create mode 100644 Angels and Demons/Assets/Editor/GameObjectMergeActions.cs create mode 100644 Angels and Demons/Assets/Editor/GameObjectMergeActions.cs.meta create mode 100644 Angels and Demons/Assets/Editor/GitMergeWindow.cs create mode 100644 Angels and Demons/Assets/Editor/GitMergeWindow.cs.meta create mode 100644 Angels and Demons/Assets/Editor/MergeAction.cs create mode 100644 Angels and Demons/Assets/Editor/MergeAction.cs.meta create mode 100644 Angels and Demons/Assets/Editor/MergeActionChangeValues.cs create mode 100644 Angels and Demons/Assets/Editor/MergeActionChangeValues.cs.meta create mode 100644 Angels and Demons/Assets/Editor/MergeActionDeleteComponent.cs create mode 100644 Angels and Demons/Assets/Editor/MergeActionDeleteComponent.cs.meta create mode 100644 Angels and Demons/Assets/Editor/MergeActionDeleteGameObject.cs create mode 100644 Angels and Demons/Assets/Editor/MergeActionDeleteGameObject.cs.meta create mode 100644 Angels and Demons/Assets/Editor/MergeActionExistence.cs create mode 100644 Angels and Demons/Assets/Editor/MergeActionExistence.cs.meta create mode 100644 Angels and Demons/Assets/Editor/MergeActionNewComponent.cs create mode 100644 Angels and Demons/Assets/Editor/MergeActionNewComponent.cs.meta create mode 100644 Angels and Demons/Assets/Editor/MergeActionNewGameObject.cs create mode 100644 Angels and Demons/Assets/Editor/MergeActionNewGameObject.cs.meta create mode 100644 Angels and Demons/Assets/Editor/MergeActionParenting.cs create mode 100644 Angels and Demons/Assets/Editor/MergeActionParenting.cs.meta create mode 100644 Angels and Demons/Assets/Editor/MergeManager.cs create mode 100644 Angels and Demons/Assets/Editor/MergeManager.cs.meta create mode 100644 Angels and Demons/Assets/Editor/MergeManagerPrefab.cs create mode 100644 Angels and Demons/Assets/Editor/MergeManagerPrefab.cs.meta create mode 100644 Angels and Demons/Assets/Editor/MergeManagerScene.cs create mode 100644 Angels and Demons/Assets/Editor/MergeManagerScene.cs.meta create mode 100644 Angels and Demons/Assets/Editor/ObjectDictionaries.cs create mode 100644 Angels and Demons/Assets/Editor/ObjectDictionaries.cs.meta create mode 100644 Angels and Demons/Assets/Editor/ObjectExtensions.cs create mode 100644 Angels and Demons/Assets/Editor/ObjectExtensions.cs.meta create mode 100644 Angels and Demons/Assets/Editor/ObjectIDFinder.cs create mode 100644 Angels and Demons/Assets/Editor/ObjectIDFinder.cs.meta create mode 100644 Angels and Demons/Assets/Editor/Resources.cs create mode 100644 Angels and Demons/Assets/Editor/Resources.cs.meta create mode 100644 Angels and Demons/Assets/Editor/Resources.meta create mode 100644 Angels and Demons/Assets/Editor/Resources/GitMergeBox.psd create mode 100644 Angels and Demons/Assets/Editor/Resources/GitMergeBox.psd.meta create mode 100644 Angels and Demons/Assets/Editor/Resources/GitMergeLogo.psd create mode 100644 Angels and Demons/Assets/Editor/Resources/GitMergeLogo.psd.meta create mode 100644 Angels and Demons/Assets/Editor/Resources/GitMergeStyles.asset create mode 100644 Angels and Demons/Assets/Editor/Resources/GitMergeStyles.asset.meta create mode 100644 Angels and Demons/Assets/Editor/Resources/Licenses.txt create mode 100644 Angels and Demons/Assets/Editor/Resources/Licenses.txt.meta create mode 100644 Angels and Demons/Assets/Editor/SerializedPropertyExtensions.cs create mode 100644 Angels and Demons/Assets/Editor/SerializedPropertyExtensions.cs.meta create mode 100644 Angels and Demons/Assets/Editor/VCS.cs create mode 100644 Angels and Demons/Assets/Editor/VCS.cs.meta create mode 100644 Angels and Demons/Assets/Editor/VCSException.cs create mode 100644 Angels and Demons/Assets/Editor/VCSException.cs.meta create mode 100644 Angels and Demons/Assets/Editor/VCSGit.cs create mode 100644 Angels and Demons/Assets/Editor/VCSGit.cs.meta create mode 100644 Angels and Demons/Assets/LICENSE.md create mode 100644 Angels and Demons/Assets/LICENSE.md.meta create mode 100644 Angels and Demons/Assets/README.md create mode 100644 Angels and Demons/Assets/README.md.meta create mode 100644 Angels and Demons/Assets/Scenes.meta create mode 100644 Angels and Demons/Assets/Scenes/Main.unity create mode 100644 Angels and Demons/Assets/Scenes/Main.unity.meta create mode 100644 Angels and Demons/Packages/manifest.json create mode 100644 Angels and Demons/ProjectSettings/AudioManager.asset create mode 100644 Angels and Demons/ProjectSettings/ClusterInputManager.asset create mode 100644 Angels and Demons/ProjectSettings/DynamicsManager.asset create mode 100644 Angels and Demons/ProjectSettings/EditorBuildSettings.asset create mode 100644 Angels and Demons/ProjectSettings/EditorSettings.asset create mode 100644 Angels and Demons/ProjectSettings/GraphicsSettings.asset create mode 100644 Angels and Demons/ProjectSettings/InputManager.asset create mode 100644 Angels and Demons/ProjectSettings/NavMeshAreas.asset create mode 100644 Angels and Demons/ProjectSettings/NetworkManager.asset create mode 100644 Angels and Demons/ProjectSettings/Physics2DSettings.asset create mode 100644 Angels and Demons/ProjectSettings/PresetManager.asset create mode 100644 Angels and Demons/ProjectSettings/ProjectSettings.asset create mode 100644 Angels and Demons/ProjectSettings/ProjectVersion.txt create mode 100644 Angels and Demons/ProjectSettings/QualitySettings.asset create mode 100644 Angels and Demons/ProjectSettings/TagManager.asset create mode 100644 Angels and Demons/ProjectSettings/TimeManager.asset create mode 100644 Angels and Demons/ProjectSettings/UnityConnectSettings.asset diff --git a/Angels and Demons/Assets/Editor.meta b/Angels and Demons/Assets/Editor.meta new file mode 100644 index 0000000..4a7481e --- /dev/null +++ b/Angels and Demons/Assets/Editor.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 13c149b5679992c4aad3260153c5f7ae +folderAsset: yes +DefaultImporter: + userData: diff --git a/Angels and Demons/Assets/Editor/GameObjectExtensions.cs b/Angels and Demons/Assets/Editor/GameObjectExtensions.cs new file mode 100644 index 0000000..dc2e74a --- /dev/null +++ b/Angels and Demons/Assets/Editor/GameObjectExtensions.cs @@ -0,0 +1,67 @@ +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; + +namespace GitMerge +{ + public static class GameObjectExtensions + { + /// + /// Adds the copy of a Component to a GameObject. + /// + /// The GameObject that will get the new Component + /// The original component to copy + /// The reference to the newly added Component copy + public static Component AddComponent(this GameObject go, Component original) + { + var c = go.AddComponent(original.GetType()); + + var originalSerialized = new SerializedObject(original).GetIterator(); + var nso = new SerializedObject(c); + var newSerialized = nso.GetIterator(); + + if(originalSerialized.Next(true)) + { + newSerialized.Next(true); + + while(originalSerialized.NextVisible(true)) + { + newSerialized.NextVisible(true); + newSerialized.SetValue(originalSerialized.GetValue()); + } + } + + nso.ApplyModifiedProperties(); + + return c; + } + + /// + /// Activates/deactivates the GameObjct, and hides it when it is disabled. + /// This is used for "their" objects to hide them while merging. + /// + /// The object do enable/disable + /// Enable or disable the object? + public static void SetActiveForMerging(this GameObject go, bool active) + { + go.SetActive(active); + go.hideFlags = active ? HideFlags.None : HideFlags.HideAndDontSave; + } + + /// + /// Ping the GameObject in the hierarchy, select it, and center it in the scene view. + /// + /// The GameObject of interest + public static void Highlight(this GameObject go) + { + Selection.activeGameObject = go; + EditorGUIUtility.PingObject(go); + + var view = SceneView.lastActiveSceneView; + if(view) + { + view.FrameSelected(); + } + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/GameObjectExtensions.cs.meta b/Angels and Demons/Assets/Editor/GameObjectExtensions.cs.meta new file mode 100644 index 0000000..cdab8fe --- /dev/null +++ b/Angels and Demons/Assets/Editor/GameObjectExtensions.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 327c0f7ed6330514791026c2d1ba5a5c +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/GameObjectMergeActions.cs b/Angels and Demons/Assets/Editor/GameObjectMergeActions.cs new file mode 100644 index 0000000..3c079aa --- /dev/null +++ b/Angels and Demons/Assets/Editor/GameObjectMergeActions.cs @@ -0,0 +1,362 @@ +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace GitMerge +{ + /// + /// One instance of this class represents one GameObject with relevance to the merge process. + /// Holds all MergeActions that can be applied to the GameObject or its Components. + /// Is considered as "merged" when all its MergeActions are "merged". + /// + public class GameObjectMergeActions + { + /// + /// Reference to "our" version of the GameObject. + /// + public GameObject ours { private set; get; } + /// + /// Reference to "their" versoin of the GameObject. + /// + public GameObject theirs { private set; get; } + + private string name; + public bool merged { private set; get; } + public bool hasActions + { + get { return actions.Count > 0; } + } + /// + /// All actions available for solving specific conflicts on the GameObject. + /// + private List actions; + + + public GameObjectMergeActions(GameObject ours, GameObject theirs) + { + actions = new List(); + + this.ours = ours; + this.theirs = theirs; + GenerateName(); + + if(theirs && !ours) + { + actions.Add(new MergeActionNewGameObject(ours, theirs)); + } + if(ours && !theirs) + { + actions.Add(new MergeActionDeleteGameObject(ours, theirs)); + } + if(ours && theirs) + { + FindPropertyDifferences(); + FindComponentDifferences(); + } + + //Some Actions have a default and are merged from the beginning. + //If all the others did was to add GameObjects, we're done with merging from the start. + CheckIfMerged(); + } + + /// + /// Generate a title for this object + /// + private void GenerateName() + { + name = ""; + if(ours) + { + name = "Your[" + GetPath(ours) + "]"; + } + if(theirs) + { + if(ours) + { + name += " vs. "; + } + name += "Their[" + GetPath(theirs) + "]"; + } + } + + /// + /// Finds the differences between properties of the two GameObjects. + /// That means the name, layer, tag... everything that's not part of a Component. Also, the parent. + /// + private void FindPropertyDifferences() + { + CheckForDifferentParents(); + FindPropertyDifferences(ours, theirs); + } + + /// + /// Since parenting is quite special, here's some dedicated handling. + /// + private void CheckForDifferentParents() + { + var transform = ours.GetComponent(); + var ourParent = transform.parent; + var theirParent = theirs.GetComponent().parent; + if(ourParent != theirParent) + { + actions.Add(new MergeActionParenting(transform, ourParent, theirParent)); + } + } + + /// + /// Check for Components that one of the sides doesn't have, and/or for defferent values + /// on Components. + /// + private void FindComponentDifferences() + { + var ourComponents = ours.GetComponents(); + var theirComponents = theirs.GetComponents(); + + //Map "their" Components to their respective ids + var theirDict = new Dictionary(); + foreach(var theirComponent in theirComponents) + { + //Ignore null components + if(theirComponent != null) + { + theirDict.Add(ObjectIDFinder.GetIdentifierFor(theirComponent), theirComponent); + } + } + + foreach(var ourComponent in ourComponents) + { + //Ignore null components + if(ourComponent == null) continue; + + //Try to find "their" equivalent to our Components + var id = ObjectIDFinder.GetIdentifierFor(ourComponent); + Component theirComponent; + theirDict.TryGetValue(id, out theirComponent); + + if(theirComponent) //Both Components exist + { + FindPropertyDifferences(ourComponent, theirComponent); + //Remove "their" Component from the dict to only keep those new to us + theirDict.Remove(id); + } + else //Component doesn't exist in their version, offer a deletion + { + actions.Add(new MergeActionDeleteComponent(ours, ourComponent)); + } + } + + //Everything left in the dict is a... + foreach(var theirComponent in theirDict.Values) + { + //...new Component from them + actions.Add(new MergeActionNewComponent(ours, theirComponent)); + } + } + + /// + /// Find all the values different in "our" and "their" version of a component. + /// + private void FindPropertyDifferences(Object ourObject, Object theirObject) + { + var ourSerialized = new SerializedObject(ourObject); + var theirSerialized = new SerializedObject(theirObject); + + var ourProperty = ourSerialized.GetIterator(); + if(ourProperty.NextVisible(true)) + { + var theirProperty = theirSerialized.GetIterator(); + theirProperty.NextVisible(true); + while(ourProperty.NextVisible(false)) + { + theirProperty.NextVisible(false); + + if(ourObject is GameObject) + { + if(MergeManager.isMergingPrefab) + { + //If merging a prefab, ignore the gameobject name. + if(ourProperty.GetPlainName() == "Name") + { + continue; + } + } + } + + if(DifferentValues(ourProperty, theirProperty)) + { + //We found a difference, accordingly add a MergeAction + actions.Add(new MergeActionChangeValues(ours, ourObject, ourProperty.Copy(), theirProperty.Copy())); + } + } + } + } + + /// + /// Returns true when the two properties have different values, false otherwise. + /// + private static bool DifferentValues(SerializedProperty ourProperty, SerializedProperty theirProperty) + { + if(!ourProperty.IsRealArray()) + { + //Regular single-value property + if(DifferentValuesFlat(ourProperty, theirProperty)) + { + return true; + } + } + else + { + //Array property + if(ourProperty.arraySize != theirProperty.arraySize) + { + return true; + } + + var op = ourProperty.Copy(); + var tp = theirProperty.Copy(); + + op.Next(true); + op.Next(true); + tp.Next(true); + tp.Next(true); + + for(int i = 0; i < ourProperty.arraySize; ++i) + { + op.Next(false); + tp.Next(false); + + if(DifferentValuesFlat(op, tp)) + { + return true; + } + } + } + + return false; + } + + private static bool DifferentValuesFlat(SerializedProperty ourProperty, SerializedProperty theirProperty) + { + var our = ourProperty.GetValue(true); + var their = theirProperty.GetValue(true); + + return !object.Equals(our, their); + } + + /// + /// Get the path of a GameObject in the hierarchy. + /// + private static string GetPath(GameObject g) + { + var t = g.transform; + var sb = new StringBuilder(t.name); + while(t.parent != null) + { + t = t.parent; + sb.Insert(0, t.name + "/"); + } + return sb.ToString(); + } + + private void CheckIfMerged() + { + merged = actions.TrueForAll(action => action.merged); + } + + /// + /// Use "our" version for all conflicts. + /// This is used on all GameObjectMergeActions objects when the merge is aborted. + /// + public void UseOurs() + { + foreach(var action in actions) + { + action.UseOurs(); + } + merged = true; + } + + /// + /// Use "their" version for all conflicts. + /// + public void UseTheirs() + { + foreach(var action in actions) + { + action.UseTheirs(); + } + merged = true; + } + + //If the foldout is open + private bool open; + public void OnGUI() + { + if(open) + { + GUI.backgroundColor = new Color(0, 0, 0, .8f); + } + else + { + GUI.backgroundColor = merged ? new Color(0, .5f, 0, .8f) : new Color(.5f, 0, 0, .8f); + } + GUILayout.BeginVertical(Resources.styles.mergeActions); + GUI.backgroundColor = Color.white; + + GUILayout.BeginHorizontal(); + open = EditorGUILayout.Foldout(open, new GUIContent(name)); + + if(ours && GUILayout.Button("Focus", EditorStyles.miniButton, GUILayout.Width(100))) + { + //Highlight the instance of the prefab, not the prefab itself + //Otherwise, "ours". + var objectToHighlight = MergeManager.isMergingPrefab ? MergeManagerPrefab.ourPrefabInstance : ours; + objectToHighlight.Highlight(); + } + GUILayout.EndHorizontal(); + + if(open) + { + //Display all merge actions. + foreach(var action in actions) + { + if(action.OnGUIMerge()) + { + CheckIfMerged(); + } + } + } + else + { + GUILayout.BeginHorizontal(); + if(GUILayout.Button("Use ours >>>", EditorStyles.miniButton)) + { + UseOurs(); + } + + if(GUILayout.Button("<<< Use theirs", EditorStyles.miniButton)) + { + UseTheirs(); + } + GUILayout.EndHorizontal(); + } + + //If "ours" is null, the GameObject doesn't exist in one of the versions. + //Try to get a reference if the object exists in the current merging state. + //If it exists, the new/gelete MergeAction will have a reference. + if(!ours) + { + foreach(var action in actions) + { + ours = action.ours; + } + } + + GUILayout.EndVertical(); + + GUI.backgroundColor = Color.white; + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/GameObjectMergeActions.cs.meta b/Angels and Demons/Assets/Editor/GameObjectMergeActions.cs.meta new file mode 100644 index 0000000..d5271c8 --- /dev/null +++ b/Angels and Demons/Assets/Editor/GameObjectMergeActions.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cfa76c0e71adc0f439476432ffe37fa3 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/GitMergeWindow.cs b/Angels and Demons/Assets/Editor/GitMergeWindow.cs new file mode 100644 index 0000000..d200368 --- /dev/null +++ b/Angels and Demons/Assets/Editor/GitMergeWindow.cs @@ -0,0 +1,255 @@ +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; + +namespace GitMerge +{ + /// + /// The window that lets you perform merges on scenes and prefabs. + /// + public class GitMergeWindow : EditorWindow + { + private VCS vcs = new VCSGit(); + + //EditorPrefs keys for settings + private const string epAutomerge = "GitMerge_automerge"; + private const string epAutofocus = "GitMerge_autofocus"; + + //Settings + public static bool automerge { private set; get; } + public static bool autofocus { private set; get; } + + //The MergeManager that has the actual merging logic + private MergeManager manager; + + public bool mergeInProgress + { + get + { + return manager != null; + } + } + + private Vector2 scrollPosition = Vector2.zero; + private int tab = 0; + + + [MenuItem("Window/GitMerge")] + static void OpenEditor() + { + var window = EditorWindow.GetWindow(typeof(GitMergeWindow), false, "GitMerge"); + //In case we're merging and the scene becomes edited, + //the shown SerializedProperties should be repainted + window.autoRepaintOnSceneChange = true; + window.minSize = new Vector2(500, 100); + } + + void OnEnable() + { + LoadSettings(); + } + + private static void LoadSettings() + { + if(EditorPrefs.HasKey(epAutomerge)) + { + automerge = EditorPrefs.GetBool(epAutomerge); + } + else + { + automerge = true; + } + if(EditorPrefs.HasKey(epAutofocus)) + { + autofocus = EditorPrefs.GetBool(epAutofocus); + } + else + { + autofocus = true; + } + } + + void OnHierarchyChange() + { + //Repaint if we changed the scene + this.Repaint(); + } + + //Always check for editor state changes, and abort the active merge process if needed + void Update() + { + if(MergeAction.inMergePhase + && (EditorApplication.isCompiling + || EditorApplication.isPlayingOrWillChangePlaymode)) + { + ShowNotification(new GUIContent("Aborting merge due to editor state change.")); + AbortMerge(); + } + } + + private void AbortMerge() + { + manager.AbortMerge(); + manager = null; + } + + void OnGUI() + { + Resources.DrawLogo(); + DrawTabButtons(); + + switch(tab) + { + case 0: + OnGUISceneTab(); + break; + + case 1: + OnGUIPrefabTab(); + break; + + default: + OnGUISettingsTab(); + break; + } + } + + /// + /// Tab that offers scene merging. + /// + private void OnGUISceneTab() + { + GUILayout.Label("Open Scene: " + EditorApplication.currentScene); + if(EditorApplication.currentScene != "" + && !mergeInProgress + && GUILayout.Button("Start merging this scene", GUILayout.Height(80))) + { + var mm = new MergeManagerScene(this, vcs); + if(mm.InitializeMerge()) + { + manager = mm; + } + } + + DisplayMergeProcess(); + } + + /// + /// Tab that offers prefab merging. + /// + private void OnGUIPrefabTab() + { + GameObject prefab; + if(!mergeInProgress) + { + GUILayout.Label("Drag your prefab here to start merging:"); + if(prefab = EditorGUILayout.ObjectField(null, typeof(GameObject), false, GUILayout.Height(60)) as GameObject) + { + var mm = new MergeManagerPrefab(this, vcs); + if(mm.InitializeMerge(prefab)) + { + manager = mm; + } + } + } + + DisplayMergeProcess(); + } + + /// + /// Tab that offers various settings for the tool. + /// + private void OnGUISettingsTab() + { + var vcsPath = vcs.exe(); + var vcsPathNew = EditorGUILayout.TextField("Path to git.exe", vcsPath); + if(vcsPath != vcsPathNew) + { + vcs.SetPath(vcsPathNew); + } + + var amNew = EditorGUILayout.Toggle("Automerge", automerge); + if(automerge != amNew) + { + automerge = amNew; + EditorPrefs.SetBool(epAutomerge, automerge); + } + GUILayout.Label("(Automerge new/deleted GameObjects/Components upon merge start)"); + + var afNew = EditorGUILayout.Toggle("Auto Highlight", autofocus); + if(autofocus != afNew) + { + autofocus = afNew; + EditorPrefs.SetBool(epAutofocus, autofocus); + } + GUILayout.Label("(Highlight GameObjects when applying a MergeAction to it)"); + } + + /// + /// If no merge is in progress, draws the buttons to switch between tabs. + /// Otherwise, draws the "abort merge" button. + /// + private void DrawTabButtons() + { + if(!mergeInProgress) + { + string[] tabs = { "Merge Scene", "Merge Prefab", "Settings" }; + tab = GUI.SelectionGrid(new Rect(72, 36, 300, 22), tab, tabs, 3); + } + else + { + GUI.backgroundColor = new Color(1,0.4f,0.4f,1); + if(GUI.Button(new Rect(72, 36, 300, 22), "Abort merge")) + { + manager.AbortMerge(); + manager = null; + } + GUI.backgroundColor = Color.white; + } + } + + /// + /// Displays all MergeActions and the "apply merge" button if a merge is in progress. + /// + private void DisplayMergeProcess() + { + if(mergeInProgress) + { + var done = DisplayMergeActions(); + GUILayout.BeginHorizontal(); + if(done && GUILayout.Button("Apply merge")) + { + manager.CompleteMerge(); + manager = null; + } + GUILayout.EndHorizontal(); + } + } + + /// + /// Displays all GameObjectMergeActions. + /// + /// True, if all MergeActions are flagged as "merged". + private bool DisplayMergeActions() + { + scrollPosition = GUILayout.BeginScrollView(scrollPosition, false, true); + GUILayout.BeginVertical(GUILayout.MinWidth(480)); + + var textColor = GUI.skin.label.normal.textColor; + GUI.skin.label.normal.textColor = Color.black; + + var done = true; + foreach(var actions in manager.allMergeActions) + { + actions.OnGUI(); + done = done && actions.merged; + } + + GUI.skin.label.normal.textColor = textColor; + + GUILayout.EndVertical(); + GUILayout.EndScrollView(); + return done; + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/GitMergeWindow.cs.meta b/Angels and Demons/Assets/Editor/GitMergeWindow.cs.meta new file mode 100644 index 0000000..53ddc40 --- /dev/null +++ b/Angels and Demons/Assets/Editor/GitMergeWindow.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: aaa504546605b3a479405bee3c11cd04 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/MergeAction.cs b/Angels and Demons/Assets/Editor/MergeAction.cs new file mode 100644 index 0000000..4df45a4 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeAction.cs @@ -0,0 +1,154 @@ +using UnityEngine; +using UnityEditor; +using System.Collections; + +namespace GitMerge +{ + /// + /// Each MergeAction represents a single, specific merge conflict. + /// This can be a GameObject added or deleted in one of the versions, + /// a Component added or deleted on a GameObject, + /// or a single property changed on a Component. + /// + public abstract class MergeAction + { + //Don't highlight objects if not in merge phase. + //Prevents highlighting while automerging. + public static bool inMergePhase; + + //A MergeAction is considere "merged" when, at some point, + //"our", "their" or a new version has been applied. + public bool merged { protected set; get; } + + public GameObject ours { protected set; get; } + public GameObject theirs { protected set; get; } + + //Flags that indicate how this MergeAction has been resolved. + protected bool usingOurs; + protected bool usingTheirs; + protected bool usingNew; + //True when this action has been automatically resolved + protected bool automatic; + + + public MergeAction(GameObject ours, GameObject theirs) + { + this.ours = ours; + this.theirs = theirs; + } + + public void UseOurs() + { + try + { + ApplyOurs(); + } + catch + { + return; + } + merged = true; + usingOurs = true; + usingTheirs = false; + usingNew = false; + + automatic = !inMergePhase; + + if(GitMergeWindow.autofocus) + { + HighlightObject(); + } + + RefreshPrefabInstance(); + } + public void UseTheirs() + { + try + { + ApplyTheirs(); + } + catch + { + return; + } + merged = true; + usingOurs = false; + usingTheirs = true; + usingNew = false; + + automatic = !inMergePhase; + + if(GitMergeWindow.autofocus) + { + HighlightObject(); + } + + RefreshPrefabInstance(); + } + public void UsedNew() + { + merged = true; + usingOurs = false; + usingTheirs = false; + usingNew = true; + + automatic = !inMergePhase; + + RefreshPrefabInstance(); + } + + /// + /// Refreshes the prefab instance, if there is any. + /// We change the prefab directly, so we have to do this to see the changes in the scene view. + /// + private static void RefreshPrefabInstance() + { + if(MergeManager.isMergingPrefab) + { + PrefabUtility.ResetToPrefabState(MergeManagerPrefab.ourPrefabInstance); + } + } + + //The implementations of these methods conatain the actual merging steps + protected abstract void ApplyOurs(); + protected abstract void ApplyTheirs(); + + /// + /// Displays the MergeAction. + /// + /// True when the represented conflict has now been merged. + public bool OnGUIMerge() + { + var wasMerged = merged; + if(merged) + { + GUI.backgroundColor = automatic ? new Color(.9f, .9f, .3f, 1) : new Color(.2f, .8f, .2f, 1); + } + else + { + GUI.backgroundColor = new Color(1f, .25f, .25f, 1); + } + GUILayout.BeginHorizontal(Resources.styles.mergeAction); + GUI.backgroundColor = Color.white; + OnGUI(); + GUI.color = Color.white; + GUILayout.EndHorizontal(); + return merged && !wasMerged; + } + + //The actual UI of the MergeAction depends on the actual type + public abstract void OnGUI(); + + private void HighlightObject() + { + //Highlight the instance of the prefab, not the prefab itself + //Otherwise, "ours". + var objectToHighlight = MergeManager.isMergingPrefab ? MergeManagerPrefab.ourPrefabInstance : ours; + + if(objectToHighlight && inMergePhase && objectToHighlight.hideFlags == HideFlags.None) + { + objectToHighlight.Highlight(); + } + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/MergeAction.cs.meta b/Angels and Demons/Assets/Editor/MergeAction.cs.meta new file mode 100644 index 0000000..8befe59 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeAction.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: d9583c639208c1e49b6cc77e128f7ccb +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/MergeActionChangeValues.cs b/Angels and Demons/Assets/Editor/MergeActionChangeValues.cs new file mode 100644 index 0000000..84f217a --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionChangeValues.cs @@ -0,0 +1,268 @@ +using UnityEngine; +using UnityEditor; +using System.Collections; +using System.Linq; + +namespace GitMerge +{ + /// + /// The MergeAction allowing to merge the value of a single property of a Component. + /// + public class MergeActionChangeValues : MergeAction + { + protected SerializedProperty ourProperty; + protected SerializedProperty theirProperty; + protected object ourInitialValue; + protected object theirInitialValue; + protected readonly string ourString; + protected readonly string theirString; + protected readonly string fieldname; + protected Object ourObject; + + public MergeActionChangeValues(GameObject ours, Object ourObject, SerializedProperty ourProperty, SerializedProperty theirProperty) + : base(ours, null) + { + this.ourObject = ourObject; + + this.ourProperty = ourProperty; + this.theirProperty = theirProperty; + + fieldname = ourObject.GetPlainType() + "." + ourProperty.GetPlainName(); + + ourInitialValue = ourProperty.GetValue(); + theirInitialValue = theirProperty.GetValue(); + + ourString = SerializedValueString(ourProperty); + theirString = SerializedValueString(theirProperty); + } + + protected override void ApplyOurs() + { + ourProperty.SetValue(ourInitialValue); + ourProperty.serializedObject.ApplyModifiedProperties(); + } + + protected override void ApplyTheirs() + { + var value = theirInitialValue; + + //If we're about references here, get "our" version of the object. + if(ourProperty.propertyType == SerializedPropertyType.ObjectReference) + { + var id = ObjectIDFinder.GetIdentifierFor(theirInitialValue as Object); + var obj = ObjectDictionaries.GetOurObject(id); + + //If we didn't have our own version of the object before, it must be new + if(!obj) + { + //Get our copy of the new object if it exists + obj = ObjectDictionaries.GetOurInstanceOfCopy(value as Object); + } + + value = obj; + } + + ourProperty.SetValue(value); + ourProperty.serializedObject.ApplyModifiedProperties(); + } + + public override void OnGUI() + { + GUILayout.BeginVertical(); + GUILayout.Label(fieldname + ": " + ourProperty.propertyType); + + GUILayout.BeginHorizontal(); + + GUILayout.BeginVertical(); + GUILayout.Label(ourString, GUILayout.Width(100)); + DisplayArray(ourInitialValue); + GUILayout.EndVertical(); + + if(MergeButton(">>>", usingOurs)) + { + UseOurs(); + } + + var c = GUI.backgroundColor; + GUI.backgroundColor = Color.white; + + //GUILayout.Label(ourProperty.propertyType + "/" + ourProperty.type + ": " + ourProperty.GetValue()); + PropertyField(ourProperty); + + GUI.backgroundColor = c; + + if(MergeButton("<<<", usingTheirs)) + { + UseTheirs(); + } + + GUILayout.BeginVertical(); + GUILayout.Label(theirString, GUILayout.Width(100)); + DisplayArray(theirInitialValue); + GUILayout.EndVertical(); + + GUILayout.EndHorizontal(); + GUILayout.EndVertical(); + } + + private void DisplayArray(object value) + { + if(ourProperty.IsRealArray() && ourProperty.isExpanded) + { + var values = (object[])value; + for(int i = 0; i < values.Length; ++i) + { + GUILayout.Label(ValueString(values[i]), GUILayout.Width(100)); + } + } + } + + /// + /// Displays the property field in the center of the window. + /// This method distinguishes between certain properties. + /// The GameObject tag, for example, shouldn't be displayed with a regular string field. + /// + /// The SerializedProerty to display + /// The width of the whole thing in the ui + private void PropertyField(SerializedProperty p, float width = 170) + { + if(p.IsRealArray()) + { + DisplayArrayProperty(p, width); + } + else + { + var oldValue = p.GetValue(); + if(fieldname == "GameObject.TagString") + { + var oldTag = oldValue as string; + var newTag = EditorGUILayout.TagField("", oldTag, GUILayout.Width(width)); + if(newTag != oldTag) + { + p.SetValue(newTag); + } + } + else if(fieldname == "GameObject.StaticEditorFlags") + { + DisplayStaticFlagChooser(p, width); + } + else + { + EditorGUILayout.PropertyField(p, new GUIContent(""), GUILayout.Width(width)); + } + if(!object.Equals(p.GetValue(), oldValue)) + { + p.serializedObject.ApplyModifiedProperties(); + UsedNew(); + } + } + } + + private void DisplayArrayProperty(SerializedProperty p, float width) + { + GUILayout.BeginVertical(); + GUILayout.BeginHorizontal(GUILayout.Width(170)); + EditorGUILayout.PropertyField(p, new GUIContent("Array"), GUILayout.Width(80)); + if(p.isExpanded) + { + var copy = p.Copy(); + var size = copy.arraySize; + + copy.Next(true); + copy.Next(true); + + PropertyField(copy, 70); + GUILayout.EndHorizontal(); + + for(int i = 0; i < size; ++i) + { + copy.Next(false); + PropertyField(copy); + } + } + else + { + GUILayout.EndHorizontal(); + } + GUILayout.EndVertical(); + } + + /// + /// Displays Toggles that let the user set the static flags of the object. + /// + /// The StaticEditorFlags SerializedProperty to display + /// The width of the whole thing in the ui + private void DisplayStaticFlagChooser(SerializedProperty p, float width) + { + var flags = (StaticEditorFlags)p.intValue; + GUILayout.BeginVertical(GUILayout.Width(width)); + + p.isExpanded = EditorGUILayout.Foldout(p.isExpanded, SerializedValueString(p)); + var allOn = true; + if(p.isExpanded) + { + foreach(var flag in System.Enum.GetValues(typeof(StaticEditorFlags)).Cast()) + { + var wasOn = (flags & flag) != 0; + var on = EditorGUILayout.Toggle(flag + "", wasOn); + if(wasOn != on) + { + flags = flags ^ flag; + } + if(!on) + { + allOn = false; + } + } + } + if(allOn) + { + flags = (StaticEditorFlags)(-1); + } + p.intValue = (int)flags; + + GUILayout.EndVertical(); + } + + private string SerializedValueString(SerializedProperty p) + { + if(fieldname == "GameObject.StaticEditorFlags") + { + switch(p.intValue) + { + case 0: + return "Not static"; + case -1: + return "Static"; + default: + return "Mixed static"; + } + } + else if(p.IsRealArray()) + { + return "Array[" + p.arraySize + "]"; + } + return ValueString(p.GetValue()); + } + + private static string ValueString(object o) + { + if(o == null) + { + return "[none]"; + } + return o.ToString(); + } + + private static bool MergeButton(string text, bool green) + { + if(green) + { + GUI.color = Color.green; + } + bool result = GUILayout.Button(text, GUILayout.ExpandWidth(false)); + GUI.color = Color.white; + return result; + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/MergeActionChangeValues.cs.meta b/Angels and Demons/Assets/Editor/MergeActionChangeValues.cs.meta new file mode 100644 index 0000000..7c65940 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionChangeValues.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 9a327ed62a5572a459e54869df134f42 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/MergeActionDeleteComponent.cs b/Angels and Demons/Assets/Editor/MergeActionDeleteComponent.cs new file mode 100644 index 0000000..d080ee1 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionDeleteComponent.cs @@ -0,0 +1,71 @@ +using UnityEngine; +using UnityEditor; + +namespace GitMerge +{ + /// + /// The MergeAction that handles a Component which exists in "their" version but not "ours". + /// + public class MergeActionDeleteComponent : MergeActionExistence + { + protected Component ourComponent; + protected Component copy; + + public MergeActionDeleteComponent(GameObject ours, Component ourComponent) + : base(ours, null) + { + this.ourComponent = ourComponent; + + var go = new GameObject("GitMerge Object"); + go.SetActiveForMerging(false); + + copy = go.AddComponent(ourComponent); + + if(GitMergeWindow.automerge) + { + UseOurs(); + } + } + + protected override void ApplyOurs() + { + if(ourComponent == null) + { + ourComponent = ours.AddComponent(copy); + ObjectDictionaries.SetAsOurObject(ourComponent); + } + } + + protected override void ApplyTheirs() + { + if(ourComponent != null) + { + ObjectDictionaries.RemoveOurObject(ourComponent); + Object.DestroyImmediate(ourComponent, true); + } + } + + public override void EnsureExistence() + { + UseOurs(); + } + + public override void OnGUI() + { + GUILayout.Label(copy.GetPlainType()); + + var defaultOptionColor = merged ? Color.gray : Color.white; + + GUI.color = usingOurs ? Color.green : defaultOptionColor; + if(GUILayout.Button("Keep Component")) + { + UseOurs(); + } + GUI.color = usingTheirs ? Color.green : defaultOptionColor; + if(GUILayout.Button("Delete Component")) + { + UseTheirs(); + } + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/MergeActionDeleteComponent.cs.meta b/Angels and Demons/Assets/Editor/MergeActionDeleteComponent.cs.meta new file mode 100644 index 0000000..b8a5cb9 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionDeleteComponent.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6414ac2f8bebc8b4bb33597977332630 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/MergeActionDeleteGameObject.cs b/Angels and Demons/Assets/Editor/MergeActionDeleteGameObject.cs new file mode 100644 index 0000000..316cc9b --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionDeleteGameObject.cs @@ -0,0 +1,57 @@ +using UnityEngine; +using UnityEditor; + +namespace GitMerge +{ + /// + /// The MergeAction that handles a GameObject which exists in "our" version but not "theirs". + /// + public class MergeActionDeleteGameObject : MergeActionExistence + { + private bool oursWasActive; + + public MergeActionDeleteGameObject(GameObject ours, GameObject theirs) + : base(ours, theirs) + { + oursWasActive = ours.activeSelf; + + if(GitMergeWindow.automerge) + { + UseOurs(); + } + } + + protected override void ApplyOurs() + { + ours.SetActiveForMerging(true); + ours.SetActive(oursWasActive); + } + + protected override void ApplyTheirs() + { + ours.SetActiveForMerging(false); + SceneView.currentDrawingSceneView.Repaint(); + } + + public override void EnsureExistence() + { + UseOurs(); + } + + public override void OnGUI() + { + var defaultOptionColor = merged ? Color.gray : Color.white; + + GUI.color = usingOurs ? Color.green : defaultOptionColor; + if(GUILayout.Button("Keep GameObject")) + { + UseOurs(); + } + GUI.color = usingTheirs ? Color.green : defaultOptionColor; + if(GUILayout.Button("Delete GameObject")) + { + UseTheirs(); + } + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/MergeActionDeleteGameObject.cs.meta b/Angels and Demons/Assets/Editor/MergeActionDeleteGameObject.cs.meta new file mode 100644 index 0000000..24955ab --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionDeleteGameObject.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 627176a0f25069f46919f6e3489eb8c3 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/MergeActionExistence.cs b/Angels and Demons/Assets/Editor/MergeActionExistence.cs new file mode 100644 index 0000000..7c3a4ba --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionExistence.cs @@ -0,0 +1,23 @@ +using UnityEngine; +using UnityEditor; + +namespace GitMerge +{ + /// + /// The abstract base MergeAction for all MergeActions that manage whether or not an object exists + /// + public abstract class MergeActionExistence : MergeAction + { + public MergeActionExistence(GameObject ours, GameObject theirs) + : base(ours, theirs) + { + ObjectDictionaries.AddToSchroedingersObjects(ours ?? theirs, this); + } + + /// + /// Apply whatever version that has the object existing, since it might be needed somewhere. + /// When overriding, call either UseOurs or UseTheirs to make sure to trigger the side effects. + /// + public abstract void EnsureExistence(); + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/MergeActionExistence.cs.meta b/Angels and Demons/Assets/Editor/MergeActionExistence.cs.meta new file mode 100644 index 0000000..bb9395f --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionExistence.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f94b44d1572d0d14ea949190ef678a3a +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/MergeActionNewComponent.cs b/Angels and Demons/Assets/Editor/MergeActionNewComponent.cs new file mode 100644 index 0000000..ca6c760 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionNewComponent.cs @@ -0,0 +1,66 @@ +using UnityEngine; +using UnityEditor; + +namespace GitMerge +{ + /// + /// The MergeAction that handles Components that exist in "our" version but not in "theirs". + /// + public class MergeActionNewComponent : MergeActionExistence + { + protected Component ourComponent; + protected Component theirComponent; + + public MergeActionNewComponent(GameObject ours, Component theirComponent) + : base(ours, null) + { + this.theirComponent = theirComponent; + + if(GitMergeWindow.automerge) + { + UseOurs(); + } + } + + protected override void ApplyOurs() + { + if(ourComponent) + { + ObjectDictionaries.RemoveCopyOf(theirComponent); + Object.DestroyImmediate(ourComponent, true); + } + } + + protected override void ApplyTheirs() + { + if(!ourComponent) + { + ourComponent = ours.AddComponent(theirComponent); + ObjectDictionaries.SetAsCopy(ourComponent, theirComponent); + } + } + + public override void EnsureExistence() + { + UseTheirs(); + } + + public override void OnGUI() + { + GUILayout.Label(theirComponent.GetPlainType()); + + var defaultOptionColor = merged ? Color.gray : Color.white; + + GUI.color = usingOurs ? Color.green : defaultOptionColor; + if(GUILayout.Button("Don't add Component")) + { + UseOurs(); + } + GUI.color = usingTheirs ? Color.green : defaultOptionColor; + if(GUILayout.Button("Add new Component")) + { + UseTheirs(); + } + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/MergeActionNewComponent.cs.meta b/Angels and Demons/Assets/Editor/MergeActionNewComponent.cs.meta new file mode 100644 index 0000000..69410cd --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionNewComponent.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 6c13706c0946b934a95c41957ce759f5 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/MergeActionNewGameObject.cs b/Angels and Demons/Assets/Editor/MergeActionNewGameObject.cs new file mode 100644 index 0000000..1459aab --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionNewGameObject.cs @@ -0,0 +1,59 @@ +using UnityEngine; +using UnityEditor; + +namespace GitMerge +{ + /// + /// The MergeAction that handles GameObjects that exist in "their" version but not in "ours". + /// + public class MergeActionNewGameObject : MergeActionExistence + { + public MergeActionNewGameObject(GameObject ours, GameObject theirs) + : base(ours, theirs) + { + if(GitMergeWindow.automerge) + { + UseTheirs(); + } + } + + protected override void ApplyOurs() + { + if(ours) + { + ObjectDictionaries.RemoveCopyOf(theirs); + GameObject.DestroyImmediate(ours, true); + } + } + + protected override void ApplyTheirs() + { + if(!ours) + { + ours = ObjectDictionaries.InstantiateFromMerging(theirs); + ObjectDictionaries.SetAsCopy(ours, theirs); + } + } + + public override void EnsureExistence() + { + UseTheirs(); + } + + public override void OnGUI() + { + var defaultOptionColor = merged ? Color.gray : Color.white; + + GUI.color = usingOurs ? Color.green : defaultOptionColor; + if(GUILayout.Button("Don't add GameObject")) + { + UseOurs(); + } + GUI.color = usingTheirs ? Color.green : defaultOptionColor; + if(GUILayout.Button("Add new GameObject")) + { + UseTheirs(); + } + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/MergeActionNewGameObject.cs.meta b/Angels and Demons/Assets/Editor/MergeActionNewGameObject.cs.meta new file mode 100644 index 0000000..012ff36 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionNewGameObject.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: fb60feef4712bcc4a9f2d266c289a2d7 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/MergeActionParenting.cs b/Angels and Demons/Assets/Editor/MergeActionParenting.cs new file mode 100644 index 0000000..eef3bea --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionParenting.cs @@ -0,0 +1,97 @@ +using UnityEngine; +using UnityEditor; + +namespace GitMerge +{ + /// + /// The MergeAction that handles a differing parents for a Transform. + /// + public class MergeActionParenting : MergeAction + { + private Transform transform; + private Transform ourParent; + private Transform theirParent; + + public MergeActionParenting(Transform transform, Transform ourParent, Transform theirParent) + : base(transform.gameObject, null) + { + this.transform = transform; + this.ourParent = ourParent; + this.theirParent = theirParent; + } + + protected override void ApplyOurs() + { + transform.parent = ourParent; + } + + protected override void ApplyTheirs() + { + var ourVersion = ObjectDictionaries.GetOurCounterpartFor(theirParent) as Transform; + if(theirParent && !ourVersion) + { + if(EditorUtility.DisplayDialog("The chosen parent currently does not exist.", "Do you want do add it?", "Yes", "No")) + { + ObjectDictionaries.EnsureExistence(theirParent.gameObject); + ourVersion = ObjectDictionaries.GetOurCounterpartFor(theirParent) as Transform; + + transform.parent = ourVersion; + } + else + { + throw new System.Exception("User Abort."); + } + } + else + { + transform.parent = ourVersion; + } + } + + public override void OnGUI() + { + GUILayout.BeginVertical(); + GUILayout.Label("Parent"); + + GUILayout.BeginHorizontal(); + + GUILayout.Label(ourParent ? ourParent.ToString() : "None", GUILayout.Width(100)); + + if(MergeButton(">>>", usingOurs)) + { + UseOurs(); + } + + var c = GUI.backgroundColor; + GUI.backgroundColor = Color.white; + var newParent = EditorGUILayout.ObjectField(transform.parent, typeof(Transform), true, GUILayout.Width(170)) as Transform; + if(newParent != transform.parent) + { + transform.parent = newParent; + UsedNew(); + } + GUI.backgroundColor = c; + + if(MergeButton("<<<", usingTheirs)) + { + UseTheirs(); + } + + GUILayout.Label(theirParent ? theirParent.ToString() : "None", GUILayout.Width(100)); + + GUILayout.EndHorizontal(); + GUILayout.EndVertical(); + } + + private static bool MergeButton(string text, bool green) + { + if(green) + { + GUI.color = Color.green; + } + bool result = GUILayout.Button(text, GUILayout.ExpandWidth(false)); + GUI.color = Color.white; + return result; + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/MergeActionParenting.cs.meta b/Angels and Demons/Assets/Editor/MergeActionParenting.cs.meta new file mode 100644 index 0000000..6e45544 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeActionParenting.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 29139dc665a42db45b67df932a04c810 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/MergeManager.cs b/Angels and Demons/Assets/Editor/MergeManager.cs new file mode 100644 index 0000000..edefe88 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeManager.cs @@ -0,0 +1,123 @@ +using UnityEngine; +using System.IO; +using System.Diagnostics; +using System.ComponentModel; +using System.Collections.Generic; + +namespace GitMerge +{ + public abstract class MergeManager + { + protected VCS vcs { private set; get; } + protected GitMergeWindow window { private set; get; } + + internal List allMergeActions; + + protected static string fileName; + protected static string theirFilename; + + public static bool isMergingScene { protected set; get; } + public static bool isMergingPrefab { get { return !isMergingScene; } } + + + public MergeManager(GitMergeWindow window, VCS vcs) + { + this.window = window; + this.vcs = vcs; + allMergeActions = new List(); + } + + /// + /// Creates "their" version of the file at the given path, + /// named filename--THEIRS.unity. + /// + /// The path of the file, relative to the project folder. + protected void GetTheirVersionOf(string path) + { + fileName = path; + + string basepath = Path.GetDirectoryName(path); + string sname = Path.GetFileNameWithoutExtension(path); + string extension = Path.GetExtension(path); + + string ours = Path.Combine(basepath, sname + "--OURS" + extension); + theirFilename = Path.Combine(basepath, sname + "--THEIRS" + extension); + + File.Copy(path, ours); + try + { + vcs.GetTheirs(path); + } + catch(VCSException e) + { + File.Delete(ours); + throw e; + } + File.Move(path, theirFilename); + File.Move(ours, path); + } + + /// + /// Finds all specific merge conflicts between two sets of GameObjects, + /// representing "our" scene and "their" scene. + /// + /// The GameObjects of "our" version of the scene. + /// The GameObjects of "their" version of the scene. + protected void BuildAllMergeActions(List ourObjects, List theirObjects) + { + allMergeActions = new List(); + + //Map "their" GameObjects to their respective ids + var theirObjectsDict = new Dictionary(); + foreach(var theirs in theirObjects) + { + theirObjectsDict.Add(ObjectIDFinder.GetIdentifierFor(theirs), theirs); + } + + foreach(var ours in ourObjects) + { + //Try to find "their" equivalent to "our" GameObjects + var id = ObjectIDFinder.GetIdentifierFor(ours); + GameObject theirs; + theirObjectsDict.TryGetValue(id, out theirs); + + //If theirs is null, mergeActions.hasActions will be false + var mergeActions = new GameObjectMergeActions(ours, theirs); + if(mergeActions.hasActions) + { + allMergeActions.Add(mergeActions); + } + //Remove "their" GameObject from the dict to only keep those new to us + theirObjectsDict.Remove(id); + } + + //Every GameObject left in the dict is a... + foreach(var theirs in theirObjectsDict.Values) + { + //...new GameObject from them + var mergeActions = new GameObjectMergeActions(null, theirs); + if(mergeActions.hasActions) + { + allMergeActions.Add(mergeActions); + } + } + } + + public abstract void CompleteMerge(); + + public virtual void AbortMerge() + { + MergeAction.inMergePhase = false; + + foreach(var actions in allMergeActions) + { + actions.UseOurs(); + } + ObjectDictionaries.DestroyTheirObjects(); + ObjectDictionaries.Clear(); + allMergeActions = null; + + window.ShowNotification(new GUIContent("Merge aborted.")); + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/MergeManager.cs.meta b/Angels and Demons/Assets/Editor/MergeManager.cs.meta new file mode 100644 index 0000000..9ccaa2a --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeManager.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b7759967878514e4aace05e726704e8f +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/MergeManagerPrefab.cs b/Angels and Demons/Assets/Editor/MergeManagerPrefab.cs new file mode 100644 index 0000000..8b614d5 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeManagerPrefab.cs @@ -0,0 +1,149 @@ +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; + +namespace GitMerge +{ + public class MergeManagerPrefab : MergeManager + { + //Stuff needed for prefab merging + public static GameObject ourPrefab { private set; get; } + private static GameObject theirPrefab; + public static GameObject ourPrefabInstance { private set; get; } + private static string previouslyOpenedScene; + + + public MergeManagerPrefab(GitMergeWindow window, VCS vcs) + : base(window, vcs) + { + + } + + public bool InitializeMerge(GameObject prefab) + { + if(!EditorApplication.SaveCurrentSceneIfUserWantsTo()) + { + return false; + } + + isMergingScene = false; + MergeAction.inMergePhase = false; + + ObjectDictionaries.Clear(); + + //checkout "their" version + GetTheirVersionOf(AssetDatabase.GetAssetOrScenePath(prefab)); + AssetDatabase.Refresh(); + + ourPrefab = prefab; + + //Open a new Scene that will only display the prefab + previouslyOpenedScene = EditorApplication.currentScene; + EditorApplication.NewScene(); + + //make the new scene empty + Object.DestroyImmediate(Camera.main.gameObject); + + //instantiate our object in order to view it while merging + ourPrefabInstance = PrefabUtility.InstantiatePrefab(prefab) as GameObject; + + //find all of "our" objects in the prefab + var ourObjects = GetAllObjects(prefab); + + theirPrefab = AssetDatabase.LoadAssetAtPath(theirFilename, typeof(GameObject)) as GameObject; + theirPrefab.hideFlags = HideFlags.HideAndDontSave; + var theirObjects = GetAllObjects(theirPrefab); + + //create list of differences that have to be merged + BuildAllMergeActions(ourObjects, theirObjects); + + if(allMergeActions.Count == 0) + { + AssetDatabase.DeleteAsset(theirFilename); + OpenPreviousScene(); + window.ShowNotification(new GUIContent("No conflict found for this prefab.")); + return false; + } + MergeAction.inMergePhase = true; + ourPrefabInstance.Highlight(); + return true; + } + + /// + /// Recursively find all GameObjects that are part of the prefab + /// + /// The prefab to analyze + /// The list with all the objects already found. Pass null in the beginning. + /// The list with all the objects + private static List GetAllObjects(GameObject prefab, List list = null) + { + if(list == null) + { + list = new List(); + } + + list.Add(prefab); + foreach(Transform t in prefab.transform) + { + GetAllObjects(t.gameObject, list); + } + return list; + } + + /// + /// Completes the merge process after solving all conflicts. + /// Cleans up the scene by deleting "their" GameObjects, clears merge related data structures, + /// executes git add scene_name. + /// + public override void CompleteMerge() + { + MergeAction.inMergePhase = false; + + //ObjectDictionaries.Clear(); + + allMergeActions = null; + + //TODO: Could we explicitly just save the prefab? + AssetDatabase.SaveAssets(); + + //Mark as merged for git + vcs.MarkAsMerged(fileName); + + //directly committing here might not be that smart, since there might be more conflicts + + ourPrefab = null; + + //delete their prefab file + AssetDatabase.DeleteAsset(theirFilename); + + OpenPreviousScene(); + window.ShowNotification(new GUIContent("Prefab successfully merged.")); + } + + /// + /// Aborts merge by using "our" version in all conflicts. + /// Cleans up merge related data. + /// + public override void AbortMerge() + { + base.AbortMerge(); + + //delete prefab file + AssetDatabase.DeleteAsset(theirFilename); + OpenPreviousScene(); + ourPrefab = null; + } + + /// + /// Opens the previously opened scene, if there was any. + /// + private static void OpenPreviousScene() + { + if(!string.IsNullOrEmpty(previouslyOpenedScene)) + { + EditorApplication.OpenScene(previouslyOpenedScene); + previouslyOpenedScene = ""; + } + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/MergeManagerPrefab.cs.meta b/Angels and Demons/Assets/Editor/MergeManagerPrefab.cs.meta new file mode 100644 index 0000000..74223b5 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeManagerPrefab.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: f8db1a549c01168468790b325c85f3c1 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/MergeManagerScene.cs b/Angels and Demons/Assets/Editor/MergeManagerScene.cs new file mode 100644 index 0000000..5ebc825 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeManagerScene.cs @@ -0,0 +1,118 @@ +using UnityEngine; +using UnityEditor; +using System.Collections.Generic; + +namespace GitMerge +{ + public class MergeManagerScene : MergeManager + { + public MergeManagerScene(GitMergeWindow window, VCS vcs) + : base(window, vcs) + { + + } + + public bool InitializeMerge() + { + isMergingScene = true; + + //Ask if the scene should be saved, because... + if(!EditorApplication.SaveCurrentSceneIfUserWantsTo()) + { + return false; + } + //...we are reloading it to prevent objects from not having a scene id. + EditorApplication.OpenScene(EditorApplication.currentScene); + + MergeAction.inMergePhase = false; + + ObjectDictionaries.Clear(); + + //checkout "their" version + GetTheirVersionOf(EditorApplication.currentScene); + AssetDatabase.Refresh(); + + //find all of "our" objects + var ourObjects = GetAllSceneObjects(); + ObjectDictionaries.SetAsOurObjects(ourObjects); + + //add "their" objects + EditorApplication.OpenSceneAdditive(theirFilename); + + //delete scene file + AssetDatabase.DeleteAsset(theirFilename); + + //find all of "their" objects + var addedObjects = GetAllNewSceneObjects(ourObjects); + ObjectDictionaries.SetAsTheirObjects(addedObjects); + + //create list of differences that have to be merged + BuildAllMergeActions(ourObjects, addedObjects); + + if(allMergeActions.Count == 0) + { + window.ShowNotification(new GUIContent("No conflict found for this scene.")); + return false; + } + MergeAction.inMergePhase = true; + return true; + } + + private static List GetAllSceneObjects() + { + var objects = (GameObject[])Object.FindObjectsOfType(typeof(GameObject)); + return new List(objects); + } + + /// + /// Finds all GameObjects in the scene, minus the ones passed. + /// + private static List GetAllNewSceneObjects(List oldObjects) + { + var all = GetAllSceneObjects(); + var old = oldObjects; + + foreach(var obj in old) + { + all.Remove(obj); + } + + return all; + } + + /// + /// Completes the merge process after solving all conflicts. + /// Cleans up the scene by deleting "their" GameObjects, clears merge related data structures, + /// executes git add scene_name. + /// + public override void CompleteMerge() + { + MergeAction.inMergePhase = false; + + ObjectDictionaries.DestroyTheirObjects(); + ObjectDictionaries.Clear(); + EditorApplication.SaveScene(); + + allMergeActions = null; + + //Mark as merged for git + vcs.MarkAsMerged(fileName); + + //directly committing here might not be that smart, since there might be more conflicts + + window.ShowNotification(new GUIContent("Scene successfully merged.")); + } + + /// + /// Aborts merge by using "our" version in all conflicts. + /// Cleans up merge related data. + /// + public override void AbortMerge() + { + base.AbortMerge(); + + //Save scene + EditorApplication.SaveScene(); + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/MergeManagerScene.cs.meta b/Angels and Demons/Assets/Editor/MergeManagerScene.cs.meta new file mode 100644 index 0000000..37afd02 --- /dev/null +++ b/Angels and Demons/Assets/Editor/MergeManagerScene.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 2143b0a739488bb4698c51777c4863fc +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/ObjectDictionaries.cs b/Angels and Demons/Assets/Editor/ObjectDictionaries.cs new file mode 100644 index 0000000..f76fa08 --- /dev/null +++ b/Angels and Demons/Assets/Editor/ObjectDictionaries.cs @@ -0,0 +1,262 @@ +using UnityEngine; +using System.Collections.Generic; + +namespace GitMerge +{ + /// + /// Dictionaries that categorize the scene's objects into our objects, their objects, and temporary + /// copies of their objects that have been instantiated while merging. + /// + public static class ObjectDictionaries + { + //This dict holds all of "our" objects + //Needed for Reference handling + // + private static Dictionary ourObjects = new Dictionary(); + + //This dict maps our instances of their objects + //Whenever we instantiate a copy of "their" new object, they're both added here + private static Dictionary ourInstances = new Dictionary(); + + //This dict holds all of "their" GameObjects + //Needed for scene cleaning after merge + // + private static Dictionary theirObjects = new Dictionary(); + + //This dict holds all GameObjects that might or might not exist, + //depending on the current merge state. The referenced objects are the versions that will definitely exist throughout the merge. + //Also maps the MergeActions responsible for their existence to them. + private static Dictionary schroedingersObjects = new Dictionary(); + + + public static void SetAsOurObjects(List objects) + { + foreach(var obj in objects) + { + SetAsOurObject(obj); + } + } + + public static void SetAsTheirObjects(List objects) + { + foreach(var obj in objects) + { + SetAsTheirs(obj, false); + } + } + + + public static void SetAsOurObject(GameObject go) + { + AddOurObject(go); + foreach(var c in go.GetComponents()) + { + AddOurObject(c); + } + } + + public static void SetAsOurObject(Component c) + { + AddOurObject(c); + } + + private static void AddOurObject(Object o) + { + if(o == null) + return; + + ourObjects.Add(ObjectIDFinder.GetIdentifierFor(o), o); + } + + public static void RemoveOurObject(GameObject go) + { + foreach(var c in go.GetComponents()) + { + RemoveOurSingleObject(c); + } + RemoveOurSingleObject(go); + } + + public static void RemoveOurObject(Component c) + { + RemoveOurSingleObject(c); + } + + private static void RemoveOurSingleObject(Object o) + { + if(o == null) + return; + + ourObjects.Remove(ObjectIDFinder.GetIdentifierFor(o)); + } + + public static Object GetOurObject(int id) + { + Object result = null; + ourObjects.TryGetValue(id, out result); + return result; + } + + /// + /// Returns: + /// * the given object if it is "ours" + /// * "our" counterpart of obj if it is "theirs" + /// * null if the object is deleted for some reason + /// The returned object can be an instance of "their" object temporarily added for the merge + /// + /// the original object + /// the counterpart of the object in "our" version + public static Object GetOurCounterpartFor(Object obj) + { + var result = obj; + if(IsTheirs(obj)) + { + result = GetOurObject(ObjectIDFinder.GetIdentifierFor(obj)); + if(!result) + { + result = GetOurInstanceOfCopy(obj); + } + } + return result; + } + + public static void Clear() + { + ourObjects.Clear(); + theirObjects.Clear(); + ourInstances.Clear(); + schroedingersObjects.Clear(); + } + + /// + /// Mark o as an instance of theirs + /// + public static void SetAsCopy(GameObject o, GameObject theirs) + { + ourInstances.Add(theirs, o); + var instanceComponents = o.GetComponents(); + var theirComponents = theirs.GetComponents(); + for(int i = 0; i < instanceComponents.Length; ++i) + { + SetAsCopy(instanceComponents[i], theirComponents[i]); + } + } + + public static void SetAsCopy(Component c, Component theirs) + { + if(c == null) + return; + + ourInstances.Add(theirs, c); + } + + public static void RemoveCopyOf(GameObject theirs) + { + ourInstances.Remove(theirs); + foreach(var c in theirs.GetComponents()) + { + if(c != null) + { + ourInstances.Remove(c); + } + } + } + + public static void RemoveCopyOf(Component theirs) + { + ourInstances.Remove(theirs); + } + + /// + /// Returns: + /// * the given object if it is "ours" + /// * "our" instance of obj if it is "theirs" + /// * null if there is no such instance + /// + /// the original object + /// the instance of the original object + public static Object GetOurInstanceOfCopy(Object obj) + { + var result = obj; + if(IsTheirs(obj)) + { + ourInstances.TryGetValue(obj, out result); + } + return result; + } + + private static bool IsTheirs(Object obj) + { + var go = obj as GameObject; + if(go) + { + return theirObjects.ContainsKey(go); + } + var c = obj as Component; + if(c) + { + return theirObjects.ContainsKey(c.gameObject); + } + return false; + } + + public static void SetAsTheirs(GameObject go, bool active) + { + if(!theirObjects.ContainsKey(go)) + { + theirObjects.Add(go, go.activeSelf); + } + go.SetActiveForMerging(false); + } + + /// + /// Copy an object that has been disabled and hidden for merging into the scene, + /// enable and unhide the copy. + /// + /// The original GameObject. + /// The copy GameObject. + public static GameObject InstantiateFromMerging(GameObject go) + { + var copy = GameObject.Instantiate(go) as GameObject; + + //Destroy children + foreach(Transform t in copy.GetComponent()) + { + Object.DestroyImmediate(t.gameObject); + } + + bool wasActive; + if(!theirObjects.TryGetValue(go, out wasActive)) + { + wasActive = go.activeSelf; + } + + //Apply some special properties of the GameObject + copy.SetActive(wasActive); + copy.hideFlags = HideFlags.None; + copy.name = go.name; + copy.GetComponent().parent = GetOurCounterpartFor(go.GetComponent().parent) as Transform; + + return copy; + } + + public static void DestroyTheirObjects() + { + foreach(var obj in theirObjects.Keys) + { + Object.DestroyImmediate(obj); + } + theirObjects.Clear(); + } + + public static void AddToSchroedingersObjects(GameObject go, MergeActionExistence mergeAction) + { + schroedingersObjects.Add(go, mergeAction); + } + + public static void EnsureExistence(GameObject go) + { + schroedingersObjects[go].EnsureExistence(); + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/ObjectDictionaries.cs.meta b/Angels and Demons/Assets/Editor/ObjectDictionaries.cs.meta new file mode 100644 index 0000000..3a634d5 --- /dev/null +++ b/Angels and Demons/Assets/Editor/ObjectDictionaries.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e2d4344037c6c7c4ca962a1e4037b6fe +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/ObjectExtensions.cs b/Angels and Demons/Assets/Editor/ObjectExtensions.cs new file mode 100644 index 0000000..24ba0c8 --- /dev/null +++ b/Angels and Demons/Assets/Editor/ObjectExtensions.cs @@ -0,0 +1,24 @@ +using UnityEngine; + +namespace GitMerge +{ + public static class ObjectExtensions + { + /// + /// Get a fine, readable type string. Doesn't really need to be a Component extension method. + /// Example: UnityEngine.BoxCollider => BoxCollider + /// + /// The object whose type we want to display + /// The well readable type string + public static string GetPlainType(this object o) + { + var s = o.GetType().ToString(); + var i = s.LastIndexOf('.'); + if(i >= 0) + { + s = s.Substring(i + 1); + } + return s; + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/ObjectExtensions.cs.meta b/Angels and Demons/Assets/Editor/ObjectExtensions.cs.meta new file mode 100644 index 0000000..5d3f534 --- /dev/null +++ b/Angels and Demons/Assets/Editor/ObjectExtensions.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 0ee0303a3ea013b4eaa610e764182b3a +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/ObjectIDFinder.cs b/Angels and Demons/Assets/Editor/ObjectIDFinder.cs new file mode 100644 index 0000000..8f09442 --- /dev/null +++ b/Angels and Demons/Assets/Editor/ObjectIDFinder.cs @@ -0,0 +1,31 @@ +using UnityEngine; +using UnityEditor; +using System.Reflection; + +namespace GitMerge +{ + public static class ObjectIDFinder + { + //soooo hacky + //credits to thelackey3326 + //http://forum.unity3d.com/threads/how-to-get-the-local-identifier-in-file-for-scene-objects.265686/ + + public static int GetIdentifierFor(Object o) + { + if(o == null) + { + return -1; + } + + var inspectorModeInfo = typeof(SerializedObject).GetProperty("inspectorMode", BindingFlags.NonPublic | BindingFlags.Instance); + + SerializedObject serializedObject = new SerializedObject(o); + inspectorModeInfo.SetValue(serializedObject, InspectorMode.Debug, null); + + SerializedProperty localIdProp = + serializedObject.FindProperty("m_LocalIdentfierInFile"); //note the misspelling! + + return localIdProp.intValue; + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/ObjectIDFinder.cs.meta b/Angels and Demons/Assets/Editor/ObjectIDFinder.cs.meta new file mode 100644 index 0000000..aab28ab --- /dev/null +++ b/Angels and Demons/Assets/Editor/ObjectIDFinder.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 03c68a8557b31bf45a299c6f3be7bf10 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/Resources.cs b/Angels and Demons/Assets/Editor/Resources.cs new file mode 100644 index 0000000..2b1e260 --- /dev/null +++ b/Angels and Demons/Assets/Editor/Resources.cs @@ -0,0 +1,35 @@ +using UnityEngine; + +namespace GitMerge +{ + public class Resources : ScriptableObject + { + public static Resources styles { private set; get; } + private static Texture2D _logo; + public static Texture2D logo + { + get + { + if(!_logo) + { + _logo = UnityEngine.Resources.Load("GitMergeLogo"); + } + return _logo; + } + } + + public void OnEnable() + { + styles = UnityEngine.Resources.Load("GitMergeStyles"); + } + + public GUIStyle mergeActions; + public GUIStyle mergeAction; + + public static void DrawLogo() + { + GUI.DrawTexture(new Rect(5, 5, logo.width, logo.height), logo); + GUILayout.Space(logo.height + 15); + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/Resources.cs.meta b/Angels and Demons/Assets/Editor/Resources.cs.meta new file mode 100644 index 0000000..71d4115 --- /dev/null +++ b/Angels and Demons/Assets/Editor/Resources.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 67422f4e577bb17409644dcfe6bf75c8 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/Resources.meta b/Angels and Demons/Assets/Editor/Resources.meta new file mode 100644 index 0000000..47d1d08 --- /dev/null +++ b/Angels and Demons/Assets/Editor/Resources.meta @@ -0,0 +1,5 @@ +fileFormatVersion: 2 +guid: 718f5b21123c36448b66c0e2ec52031c +folderAsset: yes +DefaultImporter: + userData: diff --git a/Angels and Demons/Assets/Editor/Resources/GitMergeBox.psd b/Angels and Demons/Assets/Editor/Resources/GitMergeBox.psd new file mode 100644 index 0000000000000000000000000000000000000000..a001acd3532ff7c73a9333265553ca6247a40950 GIT binary patch literal 23498 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 Ve1Z8dwnSe$vv1#-Ytgoj^?xHUQ4Ih9 literal 0 HcmV?d00001 diff --git a/Angels and Demons/Assets/Editor/Resources/GitMergeBox.psd.meta b/Angels and Demons/Assets/Editor/Resources/GitMergeBox.psd.meta new file mode 100644 index 0000000..a3b4cf3 --- /dev/null +++ b/Angels and Demons/Assets/Editor/Resources/GitMergeBox.psd.meta @@ -0,0 +1,46 @@ +fileFormatVersion: 2 +guid: be9ca0515e031fb4c86b12a6b9ba2410 +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} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: diff --git a/Angels and Demons/Assets/Editor/Resources/GitMergeLogo.psd b/Angels and Demons/Assets/Editor/Resources/GitMergeLogo.psd new file mode 100644 index 0000000000000000000000000000000000000000..ab8bfb7207ce69cc2f01b9480b002428fd7f583a GIT binary patch literal 88199 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 Zg=6O9Y#3LA;YHABk61B=U-z2N{{W8Rl0^Uj literal 0 HcmV?d00001 diff --git a/Angels and Demons/Assets/Editor/Resources/GitMergeLogo.psd.meta b/Angels and Demons/Assets/Editor/Resources/GitMergeLogo.psd.meta new file mode 100644 index 0000000..a47f32e --- /dev/null +++ b/Angels and Demons/Assets/Editor/Resources/GitMergeLogo.psd.meta @@ -0,0 +1,46 @@ +fileFormatVersion: 2 +guid: e18ddd74b76685d46a46ca56a3f6380f +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} + spriteBorder: {x: 0, y: 0, z: 0, w: 0} + spritePixelsToUnits: 100 + alphaIsTransparency: 1 + textureType: 2 + buildTargetSettings: [] + spriteSheet: + sprites: [] + spritePackingTag: + userData: diff --git a/Angels and Demons/Assets/Editor/Resources/GitMergeStyles.asset b/Angels and Demons/Assets/Editor/Resources/GitMergeStyles.asset new file mode 100644 index 0000000000000000000000000000000000000000..a9c1397d0874f492721c3ff49fd6ebfaa4a14218 GIT binary patch literal 10660 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 R&GeA5!afw6V=H= 0) + { + s = s.Substring(i + 1); + } + return s; + } + + public static bool IsRealArray(this SerializedProperty p) + { + return p.propertyType == SerializedPropertyType.Generic && p.isArray; + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/SerializedPropertyExtensions.cs.meta b/Angels and Demons/Assets/Editor/SerializedPropertyExtensions.cs.meta new file mode 100644 index 0000000..cce9a73 --- /dev/null +++ b/Angels and Demons/Assets/Editor/SerializedPropertyExtensions.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: e055732d013ee174fb764659113f8ea9 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/VCS.cs b/Angels and Demons/Assets/Editor/VCS.cs new file mode 100644 index 0000000..4de8548 --- /dev/null +++ b/Angels and Demons/Assets/Editor/VCS.cs @@ -0,0 +1,71 @@ +using UnityEditor; +using System.Diagnostics; +using System.ComponentModel; + +namespace GitMerge +{ + /// + /// This abstract class represents a vcs interface. + /// It manages saving and retrieving the exe path from/to the EditorPrefs + /// and offers a small set of actions using the vcs. + /// + public abstract class VCS + { + protected abstract string GetDefaultPath(); + protected abstract string EditorPrefsKey(); + + //The two important methods + public abstract void GetTheirs(string path); + public abstract void MarkAsMerged(string path); + + //This one's for experimental three-way merging + public abstract void GetBase(string path); + + public string exe() + { + if(EditorPrefs.HasKey(EditorPrefsKey())) + { + return EditorPrefs.GetString(EditorPrefsKey()); + } + + return GetDefaultPath(); + } + + public void SetPath(string path) + { + EditorPrefs.SetString(EditorPrefsKey(), path); + } + + /// + /// Executes the VCS as a subprocess. + /// + /// The parameters passed. Like "status" for "git status" + /// Whatever the call returns. + protected string Execute(string args) + { + var process = new Process(); + var startInfo = new ProcessStartInfo(); + startInfo.WindowStyle = ProcessWindowStyle.Hidden; + startInfo.FileName = exe(); + startInfo.Arguments = args; + startInfo.UseShellExecute = false; + startInfo.RedirectStandardOutput = true; + startInfo.CreateNoWindow = true; + process.StartInfo = startInfo; + + try + { + process.Start(); + } + catch(Win32Exception) + { + throw new VCSException(); + } + + string output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + + return output; + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/VCS.cs.meta b/Angels and Demons/Assets/Editor/VCS.cs.meta new file mode 100644 index 0000000..402389d --- /dev/null +++ b/Angels and Demons/Assets/Editor/VCS.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: a08026e2ac14c0446a85134863995fd0 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/VCSException.cs b/Angels and Demons/Assets/Editor/VCSException.cs new file mode 100644 index 0000000..14c33c0 --- /dev/null +++ b/Angels and Demons/Assets/Editor/VCSException.cs @@ -0,0 +1,10 @@ + +namespace GitMerge +{ + public class VCSException : System.Exception + { + public VCSException(string message = "Could not find the VCS executable. Please enter the path to your VCS in the settings.") : base(message) + { + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/VCSException.cs.meta b/Angels and Demons/Assets/Editor/VCSException.cs.meta new file mode 100644 index 0000000..f146267 --- /dev/null +++ b/Angels and Demons/Assets/Editor/VCSException.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 339de811f43309449914e314f1366850 +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/Editor/VCSGit.cs b/Angels and Demons/Assets/Editor/VCSGit.cs new file mode 100644 index 0000000..5912f35 --- /dev/null +++ b/Angels and Demons/Assets/Editor/VCSGit.cs @@ -0,0 +1,57 @@ +using UnityEngine; +using System.Text.RegularExpressions; + +namespace GitMerge +{ + public class VCSGit : VCS + { + protected override string GetDefaultPath() + { + if(Application.platform == RuntimePlatform.WindowsEditor) + { + return @"C:\Program Files (x86)\Git\bin\git.exe"; + } + return @"/usr/bin/git"; + } + + protected override string EditorPrefsKey() + { + return "GitMerge_git"; + } + + public override void GetTheirs(string path) + { + Execute("checkout --theirs \"" + path + "\""); + } + + public override void MarkAsMerged(string path) + { + Execute("add \"" + path + "\""); + } + + public override void GetBase(string path) + { + var head = GetCommitID(Execute("show head")); + var mergeHead = GetCommitID(Execute("show merge_head")); + var mergeBase = Execute("merge-base "+head+" "+mergeHead); + + Execute("checkout " + mergeBase + " \"" + path + "\""); + } + + /// + /// Filters the commit id from the string printed by "git show object" + /// + /// The result of a "git show object" call + /// The commit id of the object shown. + private string GetCommitID(string showResult) + { + var pattern = @"(?<=(commit ))[\dabcdef]+\b"; + var match = Regex.Match(showResult, pattern); + if(!match.Success) + { + throw new VCSException("Could not find commit ID."); + } + return match.Value; + } + } +} \ No newline at end of file diff --git a/Angels and Demons/Assets/Editor/VCSGit.cs.meta b/Angels and Demons/Assets/Editor/VCSGit.cs.meta new file mode 100644 index 0000000..419299d --- /dev/null +++ b/Angels and Demons/Assets/Editor/VCSGit.cs.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: b4b1187c33991394a9c66710b250f24b +MonoImporter: + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: diff --git a/Angels and Demons/Assets/LICENSE.md b/Angels and Demons/Assets/LICENSE.md new file mode 100644 index 0000000..23cb790 --- /dev/null +++ b/Angels and Demons/Assets/LICENSE.md @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {description} + Copyright (C) {year} {fullname} + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + {signature of Ty Coon}, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/Angels and Demons/Assets/LICENSE.md.meta b/Angels and Demons/Assets/LICENSE.md.meta new file mode 100644 index 0000000..1463f2b --- /dev/null +++ b/Angels and Demons/Assets/LICENSE.md.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: 785798e4822ed284b8daad687e2df88d +DefaultImporter: + userData: diff --git a/Angels and Demons/Assets/README.md b/Angels and Demons/Assets/README.md new file mode 100644 index 0000000..940fe64 --- /dev/null +++ b/Angels and Demons/Assets/README.md @@ -0,0 +1,17 @@ +GitMerge-for-Unity +================== + +A Unity plugin which allows you to merge scene and prefab files when using git. +More information: http://flashg.github.io/GitMerge-for-Unity/ + +### This tool does not work with Unity 5... + +...and it will not do so until a small change in scene serialization will be reverted in Unity. +Please vote for this here: http://feedback.unity3d.com/suggestions/re-add-local-identifiers-in-scene-file-for-prefab-instances + +## Current state of the project + +You can merge both scenes and prefabs. +Working with the scene hierarchy is currently being improved. + +There are still a few bugs to work on. If the tool messes up something, remember to git reset --hard :wink: diff --git a/Angels and Demons/Assets/README.md.meta b/Angels and Demons/Assets/README.md.meta new file mode 100644 index 0000000..392dd3d --- /dev/null +++ b/Angels and Demons/Assets/README.md.meta @@ -0,0 +1,4 @@ +fileFormatVersion: 2 +guid: b364bc2946220bf428dccb47dbcee795 +DefaultImporter: + userData: diff --git a/Angels and Demons/Assets/Scenes.meta b/Angels and Demons/Assets/Scenes.meta new file mode 100644 index 0000000..709eb49 --- /dev/null +++ b/Angels and Demons/Assets/Scenes.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 4f704ae4b4f98ae41a0bce26658850c1 +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Angels and Demons/Assets/Scenes/Main.unity b/Angels and Demons/Assets/Scenes/Main.unity new file mode 100644 index 0000000000000000000000000000000000000000..115a43f179630d9c1bc16ca656d7d6a76e03be27 GIT binary patch literal 13024 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 literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/ClusterInputManager.asset b/Angels and Demons/ProjectSettings/ClusterInputManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..15d7e5e48dd3bba1b202b86a40035540fe6b5384 GIT binary patch literal 4104 zcmeH^y-EW?6ov2XCL1;WYbU;dMMwgoc7a$Zq97uc5@Zx539|VCD;I2i21^?|JG(r9 zZ3-Koz{1kX^_-n0uq3tB9T@J;H+Rk?--vj3B8yWZvV@8vQY}}uOO;ajvMRrE92*1n z#f>-~6nEaAeufA4{g1Q9@6ESW&^jhtIJQJ09Ep=fL~i8Sm*~|lY@L6^(O7l6Gr4Y*+cIO?qyCe;`vN|0o>HsA7t|B z!~M=cj*-ZGuBS)bqW^%2&Jk1nJ)}aQ}fErK(YCsLB0X3io)PNdL1OIE_4?$jfK>z>% literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/DynamicsManager.asset b/Angels and Demons/ProjectSettings/DynamicsManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..824c93e323ca025ff207c18b1f1cf1aa08ebf104 GIT binary patch literal 4332 zcmeI0J8u&~5Xa|Y2oT<{@Cp##G(-d;KmwtV7a%3V$QIC{#a`P7m%He09mSz>OUnm9 zLP%6JAie-l(a=S7R6vCg5>QZp`TzIs94F8tp&M&-bHAONotYg=K8tkk5ZUlaL^k4z zi^z%NgJ%W?2aZ=x$gftbFUqDuO9}_=Z~KGas+f|m&gh(L*UlL zBQ1N=6mjr*oRj7=N1Dq387fvsK7g;s{Te7jK>D%PB@Yn;ISKu9#2tJW?#QW~e0Rc@ zx49#GVV{IDez)b^yG!07{y6R#gLxx0%LkCp72K~(H0F-Y=LQ)4S3xPPi+SFLP2NKY zN%>va)nNCSXVE( z)tsFN^v&{ZZ64P<3n%(*hf>nI?j@XaK}q?8gzqFAYFf_xUxIUA&I^V9+QxDi+wn3xk$d&h}=>%seDz{p-kPp7^)f9G-Os9GAH@G3D3nolgjAO zRo;0xjp~Z3dnQF7Qd0~OT|$6Q6&hcY{Wm)DQII{zZw;TYSptz#r?}GH;g8O zGSP4tsPc48MY`CSCb%AiUYSQywox?;1Rk3;Fw^xy{Z`qXu{B4SY%9sd+Mkl4$u3z3 zvJPY&$U2a9AnQQZfvf}nUk6@&?|bo=s)l;rK04d;_P{y5K-=Gj9X}9h_CxvN_b-c?lT9Qe402lW(XU;jl-h1wx@2*Cq_)6qTT|`C^ z5kzWL?_tHOR5xq#H%(K=0ULAiNYkmikDGsH4!-}MK6$(PX^m_f<@7sxgFKpVY&hRqduw7*xq}AL z&?BLUj)sB;QBd#$=n?+|31Q|v-z69ue!-13+PPXGg0*w}o#= zLU$MDlk}4Li|OocS*e6xkC#)P-FLSa_@t^wuB{Kd@YViGl5*dFZHTVHQ ziHv`i8vG!jM8>~K4L$}ak?|i=gO3ABfWx;5c}VyJ|EL4r?;8$sm~V2%GK=i@?dUe| z>0e3pj|m^9KY{sDPv_5bIS!s=zy67B-qXLI>Yo%oO#c+-ON08S!ISLQKeNp}YRkY2 zNz6@3ruwuf)mF;Sf|rOYfp*5$*f|}o$JBUOX*;%&3Rl?1+C*g&IcvgNtJyTxNx#Nc zt582xPP$7bU}1|o;z|@iVZqpC-mLjnHyz}zab2}SH_wmM6K!m|q1Lq8aaH+`c^Imy z0Af|M$riWd{JY$CEz4!oXj+bAV>7|N_JWGt&PZQt+1Q2(|IwW1zxGwF*$**46NYMS!2O|EN3wSv z&s~YsTF60ivT}Yn;4D_NB42X{atCq;atCq;atCq;atCq;atHqF06zX!bgbpwdTNQC onp)5y$Gt81Fwfqzv1SQADv1wM*LHL5MmSfG+?*c>NCUIK0dE*0(EtDd literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/GraphicsSettings.asset b/Angels and Demons/ProjectSettings/GraphicsSettings.asset new file mode 100644 index 0000000000000000000000000000000000000000..58d747f3530d85064106cf14a14d4d5035eb704a GIT binary patch literal 4406 zcmZ{nONlq;o3usd}+b>u5di2p;-5U(<(Os{7dv&RSEk`lB@1RY_J*T<48x7XHxX(A` zynCN-)-if@Z^^mQ{RnO9-iktVb#F7cNB8!eckAAvWAy6YnRBE2DcaQCghF$5Hyhle zdsoi8b(#KVKUaxjUfq*(qx%Kg)ZKzYb9J{G+@rfK=iR#7b&THi-kWoy`wiOEy$^-v z>h3VON0;{>&ApHA*Q{6fft-8d2i#xZqs{w$7p_P*#`pH3#zOued1)p7CXC^I`Vcvc zy3Dup@aH|Tn-J!S{;vCaP_vHa#r@xtb2Fbm(5CJXu2-86=5^Ki@OeYyeE9In=Ce=J z-uXPNeS7EgNX|WRv`=*I!UK=%;`5c3olEw-!t)Z0ar(OQ>OjstF@re!Z5-|u&5d(V zv)(w5=iJl#Sw993$`0zdNtS$*+)``z6b(qvWsw@UIbr=eQMZzBf!9lULta z-Q(o2EZ=^${Di@p{|@Hi1;uC51bK)41A0H#f%gMFNe;{SHp@>Mobw^eCmnvk@^XR0 zr*je<^5T@kFIdhzqEVOrZ&_Y3IOp$Le%j#|EuV7u=ax@9{9DUs9R8!_XUI#OPrshe zKbD`(xygq$c(HTeo+gJe519`*N6q=(re%DW<ASxIXtxdd56Dk z`3nyJ#PSy%{*~pk4*%KmmmL15<%lie+ zx-$%CzK<@T2(bly1L6|+0dU3%P`m;^XnE7&hb#{re%SIT=lwc<$?_IBjq9au+47eO zOXQx;&vW1&P)rWXc-!*CU~xQ_t%hGqrd68y;d~-S+kSxBbVJrvoYUs)xSCUOtm>jU zTaM~VR4QqF$aozFGc{C>`GJ}kN2MauhOT5Ir_@}aYN;QE2?x|-rNZf!tSOF-76>QR zysRx{0|3TVD{Y*L;-(DPUyeBZETTlQ_V?!-=?NJ`dTf{+3uLTN9|;!ZQZf9!2~bV*<>SYtM?Mm^SRij_o~ zG}ZBmvC5etudP?tPw+djJ3>S_$5H4;=+ zJ(^ySX{|94&P9oEiNJJ~s6_$NCKmIeDx_%o;ba>>*ca%@#X`fZH93(QDomJC zb{p7*+bxP`RI{aGnYMBB@}b3&*^WY^P!(c*=7DRhlBgZmRI;ZXLSVLP9%8d<5qq)X z$MG!L$5hr{`N%Q-B|9YRHy?whkIZ~DuC!M^h79A&gqDrhFDbNFTekAgIRL+8VcNG) g?<>^3T`N8tZ9N;)S7;kA_qTH-n}^%uWtMmR2i*kOWB>pF literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/InputManager.asset b/Angels and Demons/ProjectSettings/InputManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..4e1d4dd60345b4f635b3745026d258e1491a3118 GIT binary patch literal 5520 zcmeI0%Wl&^6ox0HEq5q|ax0f`r$|)2EI>$rTdJxcT7fEV5)yKgY3q{Mk?r)hieSTz zvfu%bP&dG)2(d#vKv}ZoQ3(G(j^j+?w0q= zLJal~T<;s`>z^4Ee=C(rDg|vX2P&1%z4@;n-?e}199q1ZepQ~h0`&*#HV&g)6QYce ziZaSVr0`oyi}HF(l-nQ@71WgY4V_r}t9a$VEj0iJEA=$S#ZShh#UE%rh#`lSC5_)9 zwDVPeTnl>iZxiG2{e*eR>8R=L8h<GDVb7|BgQOUWzDp?PAHFFl}>Ix}xx6 zcu9JJHoRncA}779YZZNKK~9s!sNo{o#n|=L1hXTky4iu{8?51IqHO@n`9u# zK$3wZ14#yw3?vyyGLU58Kg|FSe>}jBJFd0j*uI&ME|f3*c{$_r_1Lv$=X|t$Z#JuK z4lPa=hST->2HW~~YL4(A{TMIdRyLxNnR6Cx9{Nkgfajsl*hSRMcwEPrTIiaV8m^Gk3weSUW{Yv^KQ*-+>dcgecsm0xcwN% z)8}l>;|IE_(saS9J{MuGsJqfHxptt$&B3R8vwh!+&0I^{#}|yC(2ky`&DVOzw6l$y Vm-FDuhl#HZs>H2zK#jC!{{tbnn$`dS literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/NavMeshAreas.asset b/Angels and Demons/ProjectSettings/NavMeshAreas.asset new file mode 100644 index 0000000000000000000000000000000000000000..426ff604973cd48b63e373e62290000c5ec23b07 GIT binary patch literal 4464 zcmeH~O-~b16oyYr3x0op0V?>pFp&UC)Qz;zC>SLOB*vAwwAV5??KGVkqb|(2m%U4j zi3?qr$PbXMaZTJ9m&Tp(4_M##&P>zkKhVs9+k4J4=iZ+4-n;0C=Vk%t(*}v_l@|?^d_aZ{>)7$3x#QuHYnF8~B6Z>x?H=|eXVn^=Jczc`Y@GuJZ?c z@_s^Coc{|x`2Zm-=6~Rm4-&#+-j@ma@emu{4|WLy|K=`9KtC_*IIn9FKgs+8d3bM& z;5^46@_;;fj>F`eomE}-7PaSD^{OkXsg*0y!yWUZoi?-9yp*w9`wPmq&rK(F!rx9h<)RS9&tx+sD z9oH>cuBYl{Ej6pYpsSWsXSGsObzj-j+O`+1XQ(-LLcV0#+Hfy+RJrUom8a)zRn|3B z6!&hLDCuRt+U}&n2>sr$6_jfCJqK}IqjmTJfoTn@pIWLwsz9nhsz9nhsz9nhsz9p1 zKd8VkYUT^?J!L;q%eKaw$RuA-=NxailjXqp6CG0l0~SpF@AJ^cz?Y7n`s}wLa(N0< b)VomtOyvE$3)fB5v4AeMo$>LG;#ifx4WKaT literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/NetworkManager.asset b/Angels and Demons/ProjectSettings/NetworkManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..43d23d180ad3eec4d94e04174de3cab82b65455f GIT binary patch literal 4112 zcmeH_&q~8U5XNWIMs599&k7#JOKGB@7m-3y3jQf5c#xu7x~#u`yBM?^EoNJMtQTFqPeLc^wS-}i+8 zyW%Z_X+ht7{ms&?7jQhn7%v!^wzRW6~ZNcRNA@B`x9V1<9ou+v#PpdHW-Xa}?d+5zo=c0fCz Q9ncPF2mZeU7e$`&2jNv+y#N3J literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/Physics2DSettings.asset b/Angels and Demons/ProjectSettings/Physics2DSettings.asset new file mode 100644 index 0000000000000000000000000000000000000000..51cb48eb9ad1c418e2979b3f898fd209df61db46 GIT binary patch literal 4448 zcmeH~O>h)N6vulKKS4xPLGQGq`~T!#-K3In_M_V4Z=m@0_B7Z(>T3+x6e78gI=wtd0<{r$d= zPpf)wslA0+LhMDm2JLb*3jO>FKcnc=_KJkX3GYYT8pe!!WyY<< z{%MS9PaTEpr=RE0C%>8yLe6!bK%aaSAw=^x(I;O`2+{mB`s7?IMe{GwCtpJd(fk+m z$*(1ZX#OYqn6U#cxckSvN}rU?o{zt!M9*iiJd z(ctS9=l;t3->LX*1|L;?v%ww3Z#VdhiuW1(ZN;}3{4>SxF!)c3Z#DQu#qTuu2D~VI z-n$H5Q2g$UTS;BU!I3(#E#d5^{HWsF4c=D#9)rK4_`L>yNAdd%epc}v;5ZVpxZf(i z)8M};-f!@Witi#XrG8GKplgr>=3yXbe*}CBAm54I2|tOr={d7U*%u7HSMmD|zEAN7 z48C9S2QzL7#vKG_+&vjz8W-m!*3w;%j?42b&vo2~4X)$v%Xk{MuHrtD@uhM18(hbI z)ZjWU-|hT*blhUb)40b}+)~Du#w{CM#~m`bj$1Lfjys%jOMH*>{CULX|DDIkW3IKw zLak#q@)7b9d2e!_Z^Rr-5eLYV)|>pFdKUKN2Nj=~?u1SwEDYCV6ggfi6f@5p2<+od z)DiQdBPJ%IV81dtWJfY^Y_~8x2&-W^Ww+f(Ly!^2mDOQ%ChTZB=^T-+-+-$sCfbqX zdvrhHhYtHopth_}2Qr-Y-6jkV+4EzLC+(o(2Z3yGPD;yOtL-kw^}zN*H;Gf5l~TEQ z!jG1qA-g@(vV%z08n!FpFavdGMvhNaXJ+k2q?KfKzvfCg3)6Ht$*AtTcqXqw4{ANj zxJpsQ_ae9)pPC9~Bvdj-{mJp!Bqar=tUDC#P#!{Jod~J0o2Us1e1uLINw3kV`2j11 zNmUPdo%Pd4Mp|V!Ap>}V>ad(sVY{s2yROruD=sI;UF3MgwOa-n@|&FzKWOCK);eBe z*a;hfjO2(jAGJ}fEGCt7?s#p#9WGOkI9~HGBFQjXwuC2Vo7i0Sz-hH4%55yLimH0e zn(rQ`GK}(@B(LLwsn^BJ>y*=fQ;+(Nhh<%QF8n&V=e&g5ZW7Av$gepw*q>^@KHkPl zBg2DEq#lp&fP%FM8?oxC!UWV*Y}cK%8^^+N4~e!XlP#J!MRt=NKII29v@E-Gb|1qM*kpcwtha5R>Eyye9+%5QLyVrLz8nPpoX(>5UY@g$Ve`0tAOg`6lfq6Wq@l|p zUy)QxH!01M&PiR&VA^!d?y#t^5AlBEGzpyf+Ju znQlC;_@$Xm>n(~p+!*7vSJxFU{1p{nera{_+(N0CAD=yQ25XAB21r~y{lPBuIZoLN OaXi=u4KIKHr~Ct~fE!c* literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/PresetManager.asset b/Angels and Demons/ProjectSettings/PresetManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..45bb13d84e106c4eca2705f86d052e0c4dc4db6c GIT binary patch literal 4212 zcmeH~J!n%=6vxlYr{8VWx>#@#p@?bI!4HtoAq0a-c}fQzBz?B8Cg!zh^Atq_ZxdXc zEI4%u5kv$(1}zR<#6=JXC&587>*!MY{QvKLPkf@Q(>-uZUg<)t}V237p=Oql=Tm99w(#r==-j&aG`^P^Xz|=aNCA1SF z0hWX+3CJz!rzLo9Nss|#opwt;f$u@i`V^uBoJ(0njOgTP@(uE|d;vR%HS9yDk&hu> zjq0<60eMCW7@mT&PYzM@tm;!j${u-gm?c?hrX;9iybHdK5N1hE)i*F^{dUCy#S1t~ z6FF;l{NhCX^Yjp(!O8c*X%=ot{bL>WD;6mJyAGofw^!Vb z>(`xS=Zd=`OO3L-=-lx9TC3wrG}a}G=;7?`Yp(eGSZd5Te(R=7QY|CZjTV^Ow$Fx@ zmZzs|$8Y{wQ*k`+vNPY71--5A-%0K)>~vaBmC#X-a3f_=P-Ff%VpBiJvCRu}LxYLh zy;qfXE%o)O`t{02cl`aWneqCI!NsHYhqs=lj~?ECTpfI`zIol9J#y#Lnx=6(6O95! z0i%FXz$jo8FbWt2i~>dhqriVvfNvV_FFWc7r^wv;vEJy9&xbeF<0o0j8a`>_ns5K8 IA&zT)0^hyLQ~&?~ literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/ProjectSettings.asset b/Angels and Demons/ProjectSettings/ProjectSettings.asset new file mode 100644 index 0000000000000000000000000000000000000000..566ad3025aac640c0d7cb25f978afc4c580a334e GIT binary patch literal 56267 zcmeI5d4L>6`S*L1aNqYS1i7z}i$FkNHoG|%HrY*fb09~U-RWe9?9L1`vpJAsI0Xeo z5Jdq2Q3O%B1rcz;i&GFqP!I({0Yy0!K@i^Wx1Oh;?dt7@{NDG^-`g;|GoOC?(^b_~ z)z#HKn;>}j{2+Mxp&$r034(`j#P49@_z4G&n=o$tl8M3lAAImZQxn)}T=BsN8#aee zKYhoO+g<(mkw;yA=CQlAt{DVR?H>diaW$3$f}nv-K5!ozLC}Q%t{ELPZf*)1V*sg= zS5xp9V!7SMc#Xn~OB3EPemlHp{AfZf5#Jf_$@$_E@xAe$Yz;yz5ub$j9K6*W*3;dW2Xa{~hjq5Ufv_ z8;*Yj9~r*^UJ;*#a(DqBS)Ln`HyA&vX#(EyMb7eT%8`$D>Ddn3(8Ds7avmMbMeOl- z@iUIG@}_|P*YKI1P4PR;zZv6mBmJ9`yO;0Z!gy0~�jB9zHX#TjIrq$A#s#E;eR5 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 literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/ProjectVersion.txt b/Angels and Demons/ProjectSettings/ProjectVersion.txt new file mode 100644 index 0000000..22977b3 --- /dev/null +++ b/Angels and Demons/ProjectSettings/ProjectVersion.txt @@ -0,0 +1 @@ +m_EditorVersion: 2018.1.0f2 diff --git a/Angels and Demons/ProjectSettings/QualitySettings.asset b/Angels and Demons/ProjectSettings/QualitySettings.asset new file mode 100644 index 0000000000000000000000000000000000000000..ac5c19066742a34e01824b1fd8cff7a4092808e0 GIT binary patch literal 5000 zcmeI0%WoV-5Ql61CO~)uLLed8g!jvD0z!&eKa?1>i8mfQ<`%VgdhHIgGpm_dKOznb zCv!mJf<)jzav+CrL*jtA_z03Cap4#cLU0U%KLAs|?w;M9kuAi1+ip+w*VR?kH9g~= zChGr|=+G7sJw)^%Ux-GBM_wNs862*R(m%VqySW^*A$08SzWwP?`2CTC3$xez?kt@? z_TFnu-{>OhMK>32H+aadZZwE;{ByIL8eiwA(Zdj?OwG~HtP6+WY(f|4|HS$yxdl7j zpbd=a%8jsu3XUi4m1?DWtq>rQbKcKVyVH)Vf-d9DEa zeJT6*fa}^v7uh20qkW*sd$j&JZX=$1xuWE_h50VA41OOVmxNzt8T@`gE(x!(41N%h zOTyo08TsKWVV$SP!3Aek#MiwEU?I z|IYHKGyF%(pK0Sa*!~}uKMP(k@!oV*AIq*h<<4Ldo=fStZ)9e2Qa55avQSyAf zs4;kd>N$Fed9DCHka~_@2G@25QqR#VO`fB_*wJwt=U~%*`aPguOAXjhzXyh)ukCl! ziLO?h{Qj`>HSdAXxrO(@Xp^V&fOm+AKb?nhaBV;R9(c9M%{o88IqG3{nk(YDSQnm( zHys(r%3q17dU?7YhSHC12ZO9ix=p^QN~)<1=@r$=YCIj({g|Rv;RfrGz0S)h@anM& zd<#lpa7C8ez_f_U!j;K@85PCCFH5U_L5i^C317}tDv?aA+cBd~jb~srEgLs%gEr}^ z?Wv0B-SdGfY1xy$yCe~7M6uk6>!D00D|{6NaTwH8dBOBVB>Y%SdP+o`ABuuXToPfd z${vE{)+M(8b7UyP)a!qRNZBtBqAQTSn~qmF0fa}Q=G6iI90Dy zWay}?lEUT;D2QR8&rHCVI3^((|V7=V{ z_8ey4QJdtih{9L5ZcTi+wRPt9PI2PTyLTs`+tO(tN%YyZYq}pkcrQ=5@<#Yg{KfVq z%3I}b&icbVXL-{L zj+*#^Oq8FZJ@N=0({J>(oa3U$n4G~7czYgkD538g9I}kOLI<^FbAO-4meGX_m&WI| zo*$fLed3^H(ThXDSBz=*XY3uGrhVpU`uFlj1vaHmFS7n{FoMIHN#6|vTGf7% L`NeB<4&;9U>Yo}3 literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/TagManager.asset b/Angels and Demons/ProjectSettings/TagManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..03680d830a0befd742713f5a531a711f46379a03 GIT binary patch literal 4324 zcmeHKJx?1!5FOiRLqf<01VTaqT{?s*5@?VM2V#jtkPszZv@+|H6LUxI&V{=Z9e>1s zAXPg5iL_Mw0Vt8+^4^}$@*%fW**Wda&fD49w{NQvDc*=Yy%3RS@Ngm<>($M*>e_m1 zLvE8KDHMQx>n_{B|`Cr zQzJa(Ndd@b8h#Sb2gC;{pK+cKiNo&l{He8k)u^Yc7siJOt<`FMnjVcPq{jfNV%bOR8I$?9Do1p2T|K|_tJPgdgnz! zPds(3rR%kSb#dvwLKx`~$u`u;Bmz literal 0 HcmV?d00001 diff --git a/Angels and Demons/ProjectSettings/TimeManager.asset b/Angels and Demons/ProjectSettings/TimeManager.asset new file mode 100644 index 0000000000000000000000000000000000000000..9de8d7631502d203c809b1a8cf6a0c45b63aa88f GIT binary patch literal 4116 zcmZQzV02_)5D;NtU=Re-K#GCE$iUD-&rr`G&4}UO|Ns9P8G&M~Ac_C~Z$B!ZwoE@N zB8Kt!6}j#g_t=1SWR$#-wcOlSBf{E9Y76!IlaF$uvn zdY`56gGOPFA@XEsbdPhNGRHi_frfnW17b{+1=J1kY%rJVJnXFxGVN zEPZ9x&(|Qg6I?`=9x{Kyfq7TVHG+PD=g{fqK24v!O4aWH7oqBJkGUr5xyf_Xvx6gA z*BlQKxNk3b2(65$$7=DNG0!UN-Njt4H>a!@8!79_DeL7uZgHt!=Q-}XmwS0%RevAy zbzGzB?*|v5>PP#A`jh*6kaN)@{#uNQ`T+Hixkhh!h`_UOh$HeLsdE^dTci`+?+Bm{ zuF*OV5m;A0_h~vu!MQ~`pT(Hyn4p_H(mxKaOP+iV2f#yc)we;$X}+BR7oqxgGUjq$ zqUUgkIeprye+v0JxVkT=!9}S0XJW3=Hy$J4&spx%{5c2C?Qeh1OC0map9|o+%!fZe zcnbZC;9m@fVcDqGV(hEoryTVL%i4goQL52y=>`sD)03aV_AQf@?KvB7y4a_ zKhrUn`aQe{(7(o9*01VcN51e!)t`xVRQ($+mI?u|fwH~; literal 0 HcmV?d00001