Project started, GitMerge added.
This commit is contained in:
parent
9c493ee69e
commit
faf829348b
79 changed files with 3211 additions and 0 deletions
5
Angels and Demons/Assets/Editor.meta
Normal file
5
Angels and Demons/Assets/Editor.meta
Normal file
|
@ -0,0 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 13c149b5679992c4aad3260153c5f7ae
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
67
Angels and Demons/Assets/Editor/GameObjectExtensions.cs
Normal file
67
Angels and Demons/Assets/Editor/GameObjectExtensions.cs
Normal file
|
@ -0,0 +1,67 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
public static class GameObjectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Adds the copy of a Component to a GameObject.
|
||||
/// </summary>
|
||||
/// <param name="go">The GameObject that will get the new Component</param>
|
||||
/// <param name="original">The original component to copy</param>
|
||||
/// <returns>The reference to the newly added Component copy</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates/deactivates the GameObjct, and hides it when it is disabled.
|
||||
/// This is used for "their" objects to hide them while merging.
|
||||
/// </summary>
|
||||
/// <param name="go">The object do enable/disable</param>
|
||||
/// <param name="active">Enable or disable the object?</param>
|
||||
public static void SetActiveForMerging(this GameObject go, bool active)
|
||||
{
|
||||
go.SetActive(active);
|
||||
go.hideFlags = active ? HideFlags.None : HideFlags.HideAndDontSave;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ping the GameObject in the hierarchy, select it, and center it in the scene view.
|
||||
/// </summary>
|
||||
/// <param name="go">The GameObject of interest</param>
|
||||
public static void Highlight(this GameObject go)
|
||||
{
|
||||
Selection.activeGameObject = go;
|
||||
EditorGUIUtility.PingObject(go);
|
||||
|
||||
var view = SceneView.lastActiveSceneView;
|
||||
if(view)
|
||||
{
|
||||
view.FrameSelected();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 327c0f7ed6330514791026c2d1ba5a5c
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
362
Angels and Demons/Assets/Editor/GameObjectMergeActions.cs
Normal file
362
Angels and Demons/Assets/Editor/GameObjectMergeActions.cs
Normal file
|
@ -0,0 +1,362 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// 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".
|
||||
/// </summary>
|
||||
public class GameObjectMergeActions
|
||||
{
|
||||
/// <summary>
|
||||
/// Reference to "our" version of the GameObject.
|
||||
/// </summary>
|
||||
public GameObject ours { private set; get; }
|
||||
/// <summary>
|
||||
/// Reference to "their" versoin of the GameObject.
|
||||
/// </summary>
|
||||
public GameObject theirs { private set; get; }
|
||||
|
||||
private string name;
|
||||
public bool merged { private set; get; }
|
||||
public bool hasActions
|
||||
{
|
||||
get { return actions.Count > 0; }
|
||||
}
|
||||
/// <summary>
|
||||
/// All actions available for solving specific conflicts on the GameObject.
|
||||
/// </summary>
|
||||
private List<MergeAction> actions;
|
||||
|
||||
|
||||
public GameObjectMergeActions(GameObject ours, GameObject theirs)
|
||||
{
|
||||
actions = new List<MergeAction>();
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Generate a title for this object
|
||||
/// </summary>
|
||||
private void GenerateName()
|
||||
{
|
||||
name = "";
|
||||
if(ours)
|
||||
{
|
||||
name = "Your[" + GetPath(ours) + "]";
|
||||
}
|
||||
if(theirs)
|
||||
{
|
||||
if(ours)
|
||||
{
|
||||
name += " vs. ";
|
||||
}
|
||||
name += "Their[" + GetPath(theirs) + "]";
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
private void FindPropertyDifferences()
|
||||
{
|
||||
CheckForDifferentParents();
|
||||
FindPropertyDifferences(ours, theirs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Since parenting is quite special, here's some dedicated handling.
|
||||
/// </summary>
|
||||
private void CheckForDifferentParents()
|
||||
{
|
||||
var transform = ours.GetComponent<Transform>();
|
||||
var ourParent = transform.parent;
|
||||
var theirParent = theirs.GetComponent<Transform>().parent;
|
||||
if(ourParent != theirParent)
|
||||
{
|
||||
actions.Add(new MergeActionParenting(transform, ourParent, theirParent));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Check for Components that one of the sides doesn't have, and/or for defferent values
|
||||
/// on Components.
|
||||
/// </summary>
|
||||
private void FindComponentDifferences()
|
||||
{
|
||||
var ourComponents = ours.GetComponents<Component>();
|
||||
var theirComponents = theirs.GetComponents<Component>();
|
||||
|
||||
//Map "their" Components to their respective ids
|
||||
var theirDict = new Dictionary<int, Component>();
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find all the values different in "our" and "their" version of a component.
|
||||
/// </summary>
|
||||
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()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns true when the two properties have different values, false otherwise.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the path of a GameObject in the hierarchy.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use "our" version for all conflicts.
|
||||
/// This is used on all GameObjectMergeActions objects when the merge is aborted.
|
||||
/// </summary>
|
||||
public void UseOurs()
|
||||
{
|
||||
foreach(var action in actions)
|
||||
{
|
||||
action.UseOurs();
|
||||
}
|
||||
merged = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Use "their" version for all conflicts.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cfa76c0e71adc0f439476432ffe37fa3
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
255
Angels and Demons/Assets/Editor/GitMergeWindow.cs
Normal file
255
Angels and Demons/Assets/Editor/GitMergeWindow.cs
Normal file
|
@ -0,0 +1,255 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// The window that lets you perform merges on scenes and prefabs.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tab that offers scene merging.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tab that offers prefab merging.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tab that offers various settings for the tool.
|
||||
/// </summary>
|
||||
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)");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// If no merge is in progress, draws the buttons to switch between tabs.
|
||||
/// Otherwise, draws the "abort merge" button.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays all MergeActions and the "apply merge" button if a merge is in progress.
|
||||
/// </summary>
|
||||
private void DisplayMergeProcess()
|
||||
{
|
||||
if(mergeInProgress)
|
||||
{
|
||||
var done = DisplayMergeActions();
|
||||
GUILayout.BeginHorizontal();
|
||||
if(done && GUILayout.Button("Apply merge"))
|
||||
{
|
||||
manager.CompleteMerge();
|
||||
manager = null;
|
||||
}
|
||||
GUILayout.EndHorizontal();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays all GameObjectMergeActions.
|
||||
/// </summary>
|
||||
/// <returns>True, if all MergeActions are flagged as "merged".</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
8
Angels and Demons/Assets/Editor/GitMergeWindow.cs.meta
Normal file
8
Angels and Demons/Assets/Editor/GitMergeWindow.cs.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: aaa504546605b3a479405bee3c11cd04
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
154
Angels and Demons/Assets/Editor/MergeAction.cs
Normal file
154
Angels and Demons/Assets/Editor/MergeAction.cs
Normal file
|
@ -0,0 +1,154 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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();
|
||||
|
||||
/// <summary>
|
||||
/// Displays the MergeAction.
|
||||
/// </summary>
|
||||
/// <returns>True when the represented conflict has now been merged.</returns>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
8
Angels and Demons/Assets/Editor/MergeAction.cs.meta
Normal file
8
Angels and Demons/Assets/Editor/MergeAction.cs.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: d9583c639208c1e49b6cc77e128f7ccb
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
268
Angels and Demons/Assets/Editor/MergeActionChangeValues.cs
Normal file
268
Angels and Demons/Assets/Editor/MergeActionChangeValues.cs
Normal file
|
@ -0,0 +1,268 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
using System.Collections;
|
||||
using System.Linq;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// The MergeAction allowing to merge the value of a single property of a Component.
|
||||
/// </summary>
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
/// <param name="p">The SerializedProerty to display</param>
|
||||
/// <param name="width">The width of the whole thing in the ui</param>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Displays Toggles that let the user set the static flags of the object.
|
||||
/// </summary>
|
||||
/// <param name="p">The StaticEditorFlags SerializedProperty to display</param>
|
||||
/// <param name="width">The width of the whole thing in the ui</param>
|
||||
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<StaticEditorFlags>())
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9a327ed62a5572a459e54869df134f42
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -0,0 +1,71 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// The MergeAction that handles a Component which exists in "their" version but not "ours".
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6414ac2f8bebc8b4bb33597977332630
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
|
@ -0,0 +1,57 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// The MergeAction that handles a GameObject which exists in "our" version but not "theirs".
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 627176a0f25069f46919f6e3489eb8c3
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
23
Angels and Demons/Assets/Editor/MergeActionExistence.cs
Normal file
23
Angels and Demons/Assets/Editor/MergeActionExistence.cs
Normal file
|
@ -0,0 +1,23 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// The abstract base MergeAction for all MergeActions that manage whether or not an object exists
|
||||
/// </summary>
|
||||
public abstract class MergeActionExistence : MergeAction
|
||||
{
|
||||
public MergeActionExistence(GameObject ours, GameObject theirs)
|
||||
: base(ours, theirs)
|
||||
{
|
||||
ObjectDictionaries.AddToSchroedingersObjects(ours ?? theirs, this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
public abstract void EnsureExistence();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f94b44d1572d0d14ea949190ef678a3a
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
66
Angels and Demons/Assets/Editor/MergeActionNewComponent.cs
Normal file
66
Angels and Demons/Assets/Editor/MergeActionNewComponent.cs
Normal file
|
@ -0,0 +1,66 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// The MergeAction that handles Components that exist in "our" version but not in "theirs".
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6c13706c0946b934a95c41957ce759f5
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
59
Angels and Demons/Assets/Editor/MergeActionNewGameObject.cs
Normal file
59
Angels and Demons/Assets/Editor/MergeActionNewGameObject.cs
Normal file
|
@ -0,0 +1,59 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// The MergeAction that handles GameObjects that exist in "their" version but not in "ours".
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fb60feef4712bcc4a9f2d266c289a2d7
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
97
Angels and Demons/Assets/Editor/MergeActionParenting.cs
Normal file
97
Angels and Demons/Assets/Editor/MergeActionParenting.cs
Normal file
|
@ -0,0 +1,97 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// The MergeAction that handles a differing parents for a Transform.
|
||||
/// </summary>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 29139dc665a42db45b67df932a04c810
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
123
Angels and Demons/Assets/Editor/MergeManager.cs
Normal file
123
Angels and Demons/Assets/Editor/MergeManager.cs
Normal file
|
@ -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<GameObjectMergeActions> 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<GameObjectMergeActions>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates "their" version of the file at the given path,
|
||||
/// named filename--THEIRS.unity.
|
||||
/// </summary>
|
||||
/// <param name="path">The path of the file, relative to the project folder.</param>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all specific merge conflicts between two sets of GameObjects,
|
||||
/// representing "our" scene and "their" scene.
|
||||
/// </summary>
|
||||
/// <param name="ourObjects">The GameObjects of "our" version of the scene.</param>
|
||||
/// <param name="theirObjects">The GameObjects of "their" version of the scene.</param>
|
||||
protected void BuildAllMergeActions(List<GameObject> ourObjects, List<GameObject> theirObjects)
|
||||
{
|
||||
allMergeActions = new List<GameObjectMergeActions>();
|
||||
|
||||
//Map "their" GameObjects to their respective ids
|
||||
var theirObjectsDict = new Dictionary<int, GameObject>();
|
||||
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."));
|
||||
}
|
||||
}
|
||||
}
|
8
Angels and Demons/Assets/Editor/MergeManager.cs.meta
Normal file
8
Angels and Demons/Assets/Editor/MergeManager.cs.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b7759967878514e4aace05e726704e8f
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
149
Angels and Demons/Assets/Editor/MergeManagerPrefab.cs
Normal file
149
Angels and Demons/Assets/Editor/MergeManagerPrefab.cs
Normal file
|
@ -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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Recursively find all GameObjects that are part of the prefab
|
||||
/// </summary>
|
||||
/// <param name="prefab">The prefab to analyze</param>
|
||||
/// <param name="list">The list with all the objects already found. Pass null in the beginning.</param>
|
||||
/// <returns>The list with all the objects</returns>
|
||||
private static List<GameObject> GetAllObjects(GameObject prefab, List<GameObject> list = null)
|
||||
{
|
||||
if(list == null)
|
||||
{
|
||||
list = new List<GameObject>();
|
||||
}
|
||||
|
||||
list.Add(prefab);
|
||||
foreach(Transform t in prefab.transform)
|
||||
{
|
||||
GetAllObjects(t.gameObject, list);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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."));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aborts merge by using "our" version in all conflicts.
|
||||
/// Cleans up merge related data.
|
||||
/// </summary>
|
||||
public override void AbortMerge()
|
||||
{
|
||||
base.AbortMerge();
|
||||
|
||||
//delete prefab file
|
||||
AssetDatabase.DeleteAsset(theirFilename);
|
||||
OpenPreviousScene();
|
||||
ourPrefab = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Opens the previously opened scene, if there was any.
|
||||
/// </summary>
|
||||
private static void OpenPreviousScene()
|
||||
{
|
||||
if(!string.IsNullOrEmpty(previouslyOpenedScene))
|
||||
{
|
||||
EditorApplication.OpenScene(previouslyOpenedScene);
|
||||
previouslyOpenedScene = "";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: f8db1a549c01168468790b325c85f3c1
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
118
Angels and Demons/Assets/Editor/MergeManagerScene.cs
Normal file
118
Angels and Demons/Assets/Editor/MergeManagerScene.cs
Normal file
|
@ -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<GameObject> GetAllSceneObjects()
|
||||
{
|
||||
var objects = (GameObject[])Object.FindObjectsOfType(typeof(GameObject));
|
||||
return new List<GameObject>(objects);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds all GameObjects in the scene, minus the ones passed.
|
||||
/// </summary>
|
||||
private static List<GameObject> GetAllNewSceneObjects(List<GameObject> oldObjects)
|
||||
{
|
||||
var all = GetAllSceneObjects();
|
||||
var old = oldObjects;
|
||||
|
||||
foreach(var obj in old)
|
||||
{
|
||||
all.Remove(obj);
|
||||
}
|
||||
|
||||
return all;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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."));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aborts merge by using "our" version in all conflicts.
|
||||
/// Cleans up merge related data.
|
||||
/// </summary>
|
||||
public override void AbortMerge()
|
||||
{
|
||||
base.AbortMerge();
|
||||
|
||||
//Save scene
|
||||
EditorApplication.SaveScene();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 2143b0a739488bb4698c51777c4863fc
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
262
Angels and Demons/Assets/Editor/ObjectDictionaries.cs
Normal file
262
Angels and Demons/Assets/Editor/ObjectDictionaries.cs
Normal file
|
@ -0,0 +1,262 @@
|
|||
using UnityEngine;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// Dictionaries that categorize the scene's objects into our objects, their objects, and temporary
|
||||
/// copies of their objects that have been instantiated while merging.
|
||||
/// </summary>
|
||||
public static class ObjectDictionaries
|
||||
{
|
||||
//This dict holds all of "our" objects
|
||||
//Needed for Reference handling
|
||||
//<fileID, Object>
|
||||
private static Dictionary<int, Object> ourObjects = new Dictionary<int, Object>();
|
||||
|
||||
//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<Object, Object> ourInstances = new Dictionary<Object, Object>();
|
||||
|
||||
//This dict holds all of "their" GameObjects
|
||||
//Needed for scene cleaning after merge
|
||||
//<GameObject, originallyActive>
|
||||
private static Dictionary<GameObject, bool> theirObjects = new Dictionary<GameObject, bool>();
|
||||
|
||||
//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<GameObject, MergeActionExistence> schroedingersObjects = new Dictionary<GameObject, MergeActionExistence>();
|
||||
|
||||
|
||||
public static void SetAsOurObjects(List<GameObject> objects)
|
||||
{
|
||||
foreach(var obj in objects)
|
||||
{
|
||||
SetAsOurObject(obj);
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetAsTheirObjects(List<GameObject> objects)
|
||||
{
|
||||
foreach(var obj in objects)
|
||||
{
|
||||
SetAsTheirs(obj, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static void SetAsOurObject(GameObject go)
|
||||
{
|
||||
AddOurObject(go);
|
||||
foreach(var c in go.GetComponents<Component>())
|
||||
{
|
||||
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<Component>())
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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
|
||||
/// </summary>
|
||||
/// <param name="obj">the original object</param>
|
||||
/// <returns>the counterpart of the object in "our" version</returns>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark o as an instance of theirs
|
||||
/// </summary>
|
||||
public static void SetAsCopy(GameObject o, GameObject theirs)
|
||||
{
|
||||
ourInstances.Add(theirs, o);
|
||||
var instanceComponents = o.GetComponents<Component>();
|
||||
var theirComponents = theirs.GetComponents<Component>();
|
||||
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<Component>())
|
||||
{
|
||||
if(c != null)
|
||||
{
|
||||
ourInstances.Remove(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void RemoveCopyOf(Component theirs)
|
||||
{
|
||||
ourInstances.Remove(theirs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns:
|
||||
/// * the given object if it is "ours"
|
||||
/// * "our" instance of obj if it is "theirs"
|
||||
/// * null if there is no such instance
|
||||
/// </summary>
|
||||
/// <param name="obj">the original object</param>
|
||||
/// <returns>the instance of the original object</returns>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copy an object that has been disabled and hidden for merging into the scene,
|
||||
/// enable and unhide the copy.
|
||||
/// </summary>
|
||||
/// <param name="go">The original GameObject.</param>
|
||||
/// <returns>The copy GameObject.</returns>
|
||||
public static GameObject InstantiateFromMerging(GameObject go)
|
||||
{
|
||||
var copy = GameObject.Instantiate(go) as GameObject;
|
||||
|
||||
//Destroy children
|
||||
foreach(Transform t in copy.GetComponent<Transform>())
|
||||
{
|
||||
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<Transform>().parent = GetOurCounterpartFor(go.GetComponent<Transform>().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();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e2d4344037c6c7c4ca962a1e4037b6fe
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
24
Angels and Demons/Assets/Editor/ObjectExtensions.cs
Normal file
24
Angels and Demons/Assets/Editor/ObjectExtensions.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using UnityEngine;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
public static class ObjectExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Get a fine, readable type string. Doesn't really need to be a Component extension method.
|
||||
/// Example: UnityEngine.BoxCollider => BoxCollider
|
||||
/// </summary>
|
||||
/// <param name="o">The object whose type we want to display</param>
|
||||
/// <returns>The well readable type string</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
8
Angels and Demons/Assets/Editor/ObjectExtensions.cs.meta
Normal file
8
Angels and Demons/Assets/Editor/ObjectExtensions.cs.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0ee0303a3ea013b4eaa610e764182b3a
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
31
Angels and Demons/Assets/Editor/ObjectIDFinder.cs
Normal file
31
Angels and Demons/Assets/Editor/ObjectIDFinder.cs
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
8
Angels and Demons/Assets/Editor/ObjectIDFinder.cs.meta
Normal file
8
Angels and Demons/Assets/Editor/ObjectIDFinder.cs.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 03c68a8557b31bf45a299c6f3be7bf10
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
35
Angels and Demons/Assets/Editor/Resources.cs
Normal file
35
Angels and Demons/Assets/Editor/Resources.cs
Normal file
|
@ -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<Texture2D>("GitMergeLogo");
|
||||
}
|
||||
return _logo;
|
||||
}
|
||||
}
|
||||
|
||||
public void OnEnable()
|
||||
{
|
||||
styles = UnityEngine.Resources.Load<Resources>("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);
|
||||
}
|
||||
}
|
||||
}
|
8
Angels and Demons/Assets/Editor/Resources.cs.meta
Normal file
8
Angels and Demons/Assets/Editor/Resources.cs.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 67422f4e577bb17409644dcfe6bf75c8
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
5
Angels and Demons/Assets/Editor/Resources.meta
Normal file
5
Angels and Demons/Assets/Editor/Resources.meta
Normal file
|
@ -0,0 +1,5 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 718f5b21123c36448b66c0e2ec52031c
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
userData:
|
BIN
Angels and Demons/Assets/Editor/Resources/GitMergeBox.psd
Normal file
BIN
Angels and Demons/Assets/Editor/Resources/GitMergeBox.psd
Normal file
Binary file not shown.
|
@ -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:
|
BIN
Angels and Demons/Assets/Editor/Resources/GitMergeLogo.psd
Normal file
BIN
Angels and Demons/Assets/Editor/Resources/GitMergeLogo.psd
Normal file
Binary file not shown.
|
@ -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:
|
BIN
Angels and Demons/Assets/Editor/Resources/GitMergeStyles.asset
Normal file
BIN
Angels and Demons/Assets/Editor/Resources/GitMergeStyles.asset
Normal file
Binary file not shown.
|
@ -0,0 +1,4 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a88667aa2092d4b46aa3ad92e778e994
|
||||
NativeFormatImporter:
|
||||
userData:
|
1
Angels and Demons/Assets/Editor/Resources/Licenses.txt
Normal file
1
Angels and Demons/Assets/Editor/Resources/Licenses.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Git Logo by Jason Long [used in GitMergeLogo.psd] is licensed under the Creative Commons Attribution 3.0 Unported License.
|
|
@ -0,0 +1,4 @@
|
|||
fileFormatVersion: 2
|
||||
guid: fcbbf2375e3889c4398c97d289eab2ac
|
||||
TextScriptImporter:
|
||||
userData:
|
181
Angels and Demons/Assets/Editor/SerializedPropertyExtensions.cs
Normal file
181
Angels and Demons/Assets/Editor/SerializedPropertyExtensions.cs
Normal file
|
@ -0,0 +1,181 @@
|
|||
using UnityEngine;
|
||||
using UnityEditor;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
public static class SerializedPropertyExtensions
|
||||
{
|
||||
public static object GetValue(this SerializedProperty p, bool forComparison = false)
|
||||
{
|
||||
switch(p.propertyType)
|
||||
{
|
||||
case SerializedPropertyType.AnimationCurve:
|
||||
return p.animationCurveValue;
|
||||
case SerializedPropertyType.ArraySize:
|
||||
return p.intValue;
|
||||
case SerializedPropertyType.Boolean:
|
||||
return p.boolValue;
|
||||
case SerializedPropertyType.Bounds:
|
||||
return p.boundsValue;
|
||||
case SerializedPropertyType.Character:
|
||||
return p.stringValue; //TODO: might be bullshit
|
||||
case SerializedPropertyType.Color:
|
||||
return p.colorValue;
|
||||
case SerializedPropertyType.Enum:
|
||||
return p.enumValueIndex;
|
||||
case SerializedPropertyType.Float:
|
||||
return p.floatValue;
|
||||
case SerializedPropertyType.Generic: //(arrays)
|
||||
if(p.isArray)
|
||||
{
|
||||
var arr = new object[p.arraySize];
|
||||
for(int i = 0; i < arr.Length; ++i)
|
||||
{
|
||||
arr[i] = p.GetArrayElementAtIndex(i).GetValue();
|
||||
}
|
||||
return arr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
case SerializedPropertyType.Gradient:
|
||||
return 0; //TODO: erm
|
||||
case SerializedPropertyType.Integer:
|
||||
return p.intValue;
|
||||
case SerializedPropertyType.LayerMask:
|
||||
return p.intValue;
|
||||
case SerializedPropertyType.ObjectReference:
|
||||
if(forComparison)
|
||||
{
|
||||
return ObjectIDFinder.GetIdentifierFor(p.objectReferenceValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
return p.objectReferenceValue;
|
||||
}
|
||||
case SerializedPropertyType.Quaternion:
|
||||
return p.quaternionValue;
|
||||
case SerializedPropertyType.Rect:
|
||||
return p.rectValue;
|
||||
case SerializedPropertyType.String:
|
||||
return p.stringValue;
|
||||
case SerializedPropertyType.Vector2:
|
||||
return p.vector2Value;
|
||||
case SerializedPropertyType.Vector3:
|
||||
return p.vector3Value;
|
||||
case SerializedPropertyType.Vector4:
|
||||
return p.vector4Value;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static void SetValue(this SerializedProperty p, object value)
|
||||
{
|
||||
switch(p.propertyType)
|
||||
{
|
||||
case SerializedPropertyType.AnimationCurve:
|
||||
p.animationCurveValue = value as AnimationCurve;
|
||||
break;
|
||||
case SerializedPropertyType.ArraySize:
|
||||
//TODO: erm
|
||||
p.intValue = (int)value;
|
||||
break;
|
||||
case SerializedPropertyType.Boolean:
|
||||
p.boolValue = (bool)value;
|
||||
break;
|
||||
case SerializedPropertyType.Bounds:
|
||||
p.boundsValue = (Bounds)value;
|
||||
break;
|
||||
case SerializedPropertyType.Character:
|
||||
p.stringValue = (string)value; //TODO: might be bullshit
|
||||
break;
|
||||
case SerializedPropertyType.Color:
|
||||
p.colorValue = (Color)value;
|
||||
break;
|
||||
case SerializedPropertyType.Enum:
|
||||
p.enumValueIndex = (int)value;
|
||||
break;
|
||||
case SerializedPropertyType.Float:
|
||||
p.floatValue = (float)value;
|
||||
break;
|
||||
case SerializedPropertyType.Generic: //(arrays)
|
||||
if(p.isArray)
|
||||
{
|
||||
//var size = p.arraySize;
|
||||
//var resetPath = p.propertyPath;
|
||||
var values = (object[])value;
|
||||
/*
|
||||
for(int i = 0; i < p.arraySize; ++i)
|
||||
{
|
||||
Debug.Log(i + ": " + p.GetArrayElementAtIndex(i).GetValue());
|
||||
}
|
||||
*/
|
||||
p.ClearArray();
|
||||
for(int i = 0; i < values.Length; ++i)
|
||||
{
|
||||
p.InsertArrayElementAtIndex(i);
|
||||
//Debug.Log(i + ": " + pv.GetArrayElementAtIndex(i).GetValue());
|
||||
p.GetArrayElementAtIndex(i).SetValue(values[i]);
|
||||
}
|
||||
|
||||
//p.FindPropertyRelative(resetPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
//p.stringValue = (string)value;
|
||||
}
|
||||
break;
|
||||
case SerializedPropertyType.Gradient:
|
||||
//TODO: erm
|
||||
break;
|
||||
case SerializedPropertyType.Integer:
|
||||
p.intValue = (int)value;
|
||||
break;
|
||||
case SerializedPropertyType.LayerMask:
|
||||
p.intValue = (int)value;
|
||||
break;
|
||||
case SerializedPropertyType.ObjectReference:
|
||||
p.objectReferenceValue = value as Object; //TODO: what about non-UnityEngine objects?
|
||||
break;
|
||||
case SerializedPropertyType.Quaternion:
|
||||
p.quaternionValue = (Quaternion)value;
|
||||
break;
|
||||
case SerializedPropertyType.Rect:
|
||||
p.rectValue = (Rect)value;
|
||||
break;
|
||||
case SerializedPropertyType.String:
|
||||
p.stringValue = (string)value;
|
||||
break;
|
||||
case SerializedPropertyType.Vector2:
|
||||
p.vector2Value = (Vector2)value;
|
||||
break;
|
||||
case SerializedPropertyType.Vector3:
|
||||
p.vector3Value = (Vector3)value;
|
||||
break;
|
||||
case SerializedPropertyType.Vector4:
|
||||
p.vector4Value = (Vector4)value;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static string GetPlainName(this SerializedProperty p)
|
||||
{
|
||||
var s = p.name;
|
||||
var i = s.IndexOf('_');
|
||||
if(i >= 0)
|
||||
{
|
||||
s = s.Substring(i + 1);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
public static bool IsRealArray(this SerializedProperty p)
|
||||
{
|
||||
return p.propertyType == SerializedPropertyType.Generic && p.isArray;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: e055732d013ee174fb764659113f8ea9
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
71
Angels and Demons/Assets/Editor/VCS.cs
Normal file
71
Angels and Demons/Assets/Editor/VCS.cs
Normal file
|
@ -0,0 +1,71 @@
|
|||
using UnityEditor;
|
||||
using System.Diagnostics;
|
||||
using System.ComponentModel;
|
||||
|
||||
namespace GitMerge
|
||||
{
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Executes the VCS as a subprocess.
|
||||
/// </summary>
|
||||
/// <param name="args">The parameters passed. Like "status" for "git status"</param>
|
||||
/// <returns>Whatever the call returns.</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;
|
||||
}
|
||||
}
|
||||
}
|
8
Angels and Demons/Assets/Editor/VCS.cs.meta
Normal file
8
Angels and Demons/Assets/Editor/VCS.cs.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: a08026e2ac14c0446a85134863995fd0
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
10
Angels and Demons/Assets/Editor/VCSException.cs
Normal file
10
Angels and Demons/Assets/Editor/VCSException.cs
Normal file
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
8
Angels and Demons/Assets/Editor/VCSException.cs.meta
Normal file
8
Angels and Demons/Assets/Editor/VCSException.cs.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 339de811f43309449914e314f1366850
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
57
Angels and Demons/Assets/Editor/VCSGit.cs
Normal file
57
Angels and Demons/Assets/Editor/VCSGit.cs
Normal file
|
@ -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 + "\"");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Filters the commit id from the string printed by "git show object"
|
||||
/// </summary>
|
||||
/// <param name="showResult">The result of a "git show object" call</param>
|
||||
/// <returns>The commit id of the object shown.</returns>
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
8
Angels and Demons/Assets/Editor/VCSGit.cs.meta
Normal file
8
Angels and Demons/Assets/Editor/VCSGit.cs.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b4b1187c33991394a9c66710b250f24b
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
339
Angels and Demons/Assets/LICENSE.md
Normal file
339
Angels and Demons/Assets/LICENSE.md
Normal file
|
@ -0,0 +1,339 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
|
||||
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.
|
4
Angels and Demons/Assets/LICENSE.md.meta
Normal file
4
Angels and Demons/Assets/LICENSE.md.meta
Normal file
|
@ -0,0 +1,4 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 785798e4822ed284b8daad687e2df88d
|
||||
DefaultImporter:
|
||||
userData:
|
17
Angels and Demons/Assets/README.md
Normal file
17
Angels and Demons/Assets/README.md
Normal file
|
@ -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:
|
4
Angels and Demons/Assets/README.md.meta
Normal file
4
Angels and Demons/Assets/README.md.meta
Normal file
|
@ -0,0 +1,4 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b364bc2946220bf428dccb47dbcee795
|
||||
DefaultImporter:
|
||||
userData:
|
8
Angels and Demons/Assets/Scenes.meta
Normal file
8
Angels and Demons/Assets/Scenes.meta
Normal file
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 4f704ae4b4f98ae41a0bce26658850c1
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
BIN
Angels and Demons/Assets/Scenes/Main.unity
Normal file
BIN
Angels and Demons/Assets/Scenes/Main.unity
Normal file
Binary file not shown.
7
Angels and Demons/Assets/Scenes/Main.unity.meta
Normal file
7
Angels and Demons/Assets/Scenes/Main.unity.meta
Normal file
|
@ -0,0 +1,7 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 6f41f0233cb7644e4a83e5f5543ed237
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
4
Angels and Demons/Packages/manifest.json
Normal file
4
Angels and Demons/Packages/manifest.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"dependencies": {
|
||||
}
|
||||
}
|
BIN
Angels and Demons/ProjectSettings/AudioManager.asset
Normal file
BIN
Angels and Demons/ProjectSettings/AudioManager.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/ClusterInputManager.asset
Normal file
BIN
Angels and Demons/ProjectSettings/ClusterInputManager.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/DynamicsManager.asset
Normal file
BIN
Angels and Demons/ProjectSettings/DynamicsManager.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/EditorBuildSettings.asset
Normal file
BIN
Angels and Demons/ProjectSettings/EditorBuildSettings.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/EditorSettings.asset
Normal file
BIN
Angels and Demons/ProjectSettings/EditorSettings.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/GraphicsSettings.asset
Normal file
BIN
Angels and Demons/ProjectSettings/GraphicsSettings.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/InputManager.asset
Normal file
BIN
Angels and Demons/ProjectSettings/InputManager.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/NavMeshAreas.asset
Normal file
BIN
Angels and Demons/ProjectSettings/NavMeshAreas.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/NetworkManager.asset
Normal file
BIN
Angels and Demons/ProjectSettings/NetworkManager.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/Physics2DSettings.asset
Normal file
BIN
Angels and Demons/ProjectSettings/Physics2DSettings.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/PresetManager.asset
Normal file
BIN
Angels and Demons/ProjectSettings/PresetManager.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/ProjectSettings.asset
Normal file
BIN
Angels and Demons/ProjectSettings/ProjectSettings.asset
Normal file
Binary file not shown.
1
Angels and Demons/ProjectSettings/ProjectVersion.txt
Normal file
1
Angels and Demons/ProjectSettings/ProjectVersion.txt
Normal file
|
@ -0,0 +1 @@
|
|||
m_EditorVersion: 2018.1.0f2
|
BIN
Angels and Demons/ProjectSettings/QualitySettings.asset
Normal file
BIN
Angels and Demons/ProjectSettings/QualitySettings.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/TagManager.asset
Normal file
BIN
Angels and Demons/ProjectSettings/TagManager.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/TimeManager.asset
Normal file
BIN
Angels and Demons/ProjectSettings/TimeManager.asset
Normal file
Binary file not shown.
BIN
Angels and Demons/ProjectSettings/UnityConnectSettings.asset
Normal file
BIN
Angels and Demons/ProjectSettings/UnityConnectSettings.asset
Normal file
Binary file not shown.
Reference in a new issue