diff --git a/Assets/Scripts/GenerateDemoFiles.cs b/Assets/Scripts/GenerateDemoFiles.cs
index f30520b4d57f3306f0ff738de1164569a2b27c7e..6203e440bfde7e9fdfea30060404e040bd022a64 100644
--- a/Assets/Scripts/GenerateDemoFiles.cs
+++ b/Assets/Scripts/GenerateDemoFiles.cs
@@ -46,11 +46,11 @@ public static void GenerateDemoA()
             buttom = new PointFact(Vector3.zero, Vector3.up, StageStatic.stage.solution),
             top = new PointFact(Vector3.zero + Vector3.up * minimalSolutionHight, Vector3.up, StageStatic.stage.solution);
 
-        StageStatic.stage.solution.Add(buttom, out _);
-        StageStatic.stage.solution.Add(top, out _, true);
+        StageStatic.stage.solution.Add(buttom, out _, false, null, null);
+        StageStatic.stage.solution.Add(top, out _, true, null, null);
 
         LineFact target = new LineFact(buttom.Id, top.Id, StageStatic.stage.solution);
-        var target_Id = StageStatic.stage.solution.Add(target, out _, true);
+        var target_Id = StageStatic.stage.solution.Add(target, out _, true, null, null);
 
         // Set Solution
         StageStatic.stage.solution.ValidationSet =
@@ -92,11 +92,11 @@ public static void GenerateDemoB()
             buttom = new PointFact(Vector3.zero, Vector3.up, StageStatic.stage.solution),
             top = new PointFact(Vector3.zero + Vector3.up * minimalSolutionHight, Vector3.up, StageStatic.stage.solution);
 
-        StageStatic.stage.solution.Add(buttom, out _);
-        StageStatic.stage.solution.Add(top, out _, true);
+        StageStatic.stage.solution.Add(buttom, out _, false, null, null);
+        StageStatic.stage.solution.Add(top, out _, true, null, null);
 
         LineFact target = new LineFact(buttom.Id, top.Id, StageStatic.stage.solution);
-        var target_Id = StageStatic.stage.solution.Add(target, out _, true);
+        var target_Id = StageStatic.stage.solution.Add(target, out _, true, null, null);
 
         // Set Solution
         StageStatic.stage.solution.ValidationSet =
diff --git a/Assets/Scripts/IJSONsavable.cs b/Assets/Scripts/IJSONsavable.cs
new file mode 100644
index 0000000000000000000000000000000000000000..83571ab2711939958e0504bac8240e5859d6b3da
--- /dev/null
+++ b/Assets/Scripts/IJSONsavable.cs
@@ -0,0 +1,405 @@
+using System.Collections.Generic;
+using System.Reflection;
+using System.Linq;
+using System.IO;
+using System;
+using Newtonsoft.Json;
+using UnityEngine;
+using static CommunicationEvents;
+
+
+// I would go for static virtual methods, but C#9 does not allow me...
+// static methods cannot be overwritten -> virtual
+public interface IJSONsavable<T> where T : IJSONsavable<T>, new()
+{
+    // stand-in for non static methods
+    public static readonly IJSONsavable<T> Instance = new T();
+
+    public static readonly FieldInfo[] JsonSaveableFields =
+    #region one-time-initialisation
+         typeof(T)
+        .GetFields(
+            BindingFlags.Instance |
+            BindingFlags.Public |
+            BindingFlags.NonPublic |
+            BindingFlags.Static)
+        .Where((field)
+            => !field.GetCustomAttributes().Any((attribute)
+                => attribute.GetType() == typeof(JsonIgnoreAttribute))
+            && field.FieldType.GetInterfaces().Any((inter)
+                => inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IJSONsavable<>)))
+        .ToArray();
+    #endregion one-time-initialisation
+
+    public static readonly FieldInfo[] JsonAutoPreProcessFields =
+    #region one-time-initialisation
+         typeof(T)
+        .GetFields(
+            BindingFlags.Instance |
+            BindingFlags.Public |
+            BindingFlags.NonPublic |
+            BindingFlags.Static)
+        .Where((field)
+            => !field.GetCustomAttributes().Any((attribute)
+                => attribute.GetType() == typeof(JsonIgnoreAttribute))
+            && field.GetCustomAttributes().Any((attribute)
+                => attribute.GetType() == typeof(JSONsavable.JsonAutoPreProcessAttribute))
+            && field.FieldType.GetInterfaces().Any((inter)
+                => inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IJSONsavable<>)))
+        .ToArray();
+    #endregion one-time-initialisation
+
+    public static readonly FieldInfo[] JsonAutoPostProcessFields =
+    #region one-time-initialisation
+         typeof(T)
+        .GetFields(
+            BindingFlags.Instance |
+            BindingFlags.Public |
+            BindingFlags.NonPublic |
+            BindingFlags.Static)
+        .Where((field)
+            => !field.GetCustomAttributes().Any((attribute)
+                => attribute.GetType() == typeof(JsonIgnoreAttribute))
+            && field.GetCustomAttributes().Any((attribute)
+                => attribute.GetType() == typeof(JSONsavable.JsonAutoPostProcessAttribute))
+            && field.FieldType.GetInterfaces().Any((inter)
+                => inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IJSONsavable<>)))
+        .ToArray();
+    #endregion one-time-initialisation
+
+    public static readonly FieldInfo[] JsonSeperateFields =
+    #region one-time-initialisation
+         typeof(T)
+        .GetFields(
+            BindingFlags.Instance |
+            BindingFlags.Public |
+            BindingFlags.NonPublic |
+            BindingFlags.Static)
+        .Where((field)
+            => field.GetCustomAttributes().Any((attribute)
+                => attribute.GetType() == typeof(JSONsavable.JsonSeparateAttribute))
+            && field.FieldType.GetInterfaces().Any((inter)
+                => inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IJSONsavable<>)))
+        .ToArray();
+    #endregion one-time-initialisation
+
+
+    // TODO: this?
+    public string name { get; set; }
+    public string path { get; set; }
+    protected static List<Directories>
+        hierarchie = new List<Directories> { Directories.misc };
+
+    #region OverridableMethods
+
+    public virtual string _IJGetName(string name) => name;
+    public virtual List<Directories> _IJGetHierarchie(List<Directories> hierarchie_base)
+    {
+        hierarchie_base ??= new List<Directories>();
+        return hierarchie_base.Concat(hierarchie).ToList();
+    }
+    public virtual bool _IJGetRawObject(out T payload, string path) => JSONsavable.ReadFromJsonFile<T>(out payload, path);
+    public virtual T _IJPreProcess(T payload) => payload;
+    public virtual T _IJPostProcess(T payload) => payload;
+
+    #endregion OverridableMethods
+
+
+    #region MethodTemplates
+
+    public bool store(List<Directories> hierarchie, string name, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
+        => store(hierarchie, name, (T)this, use_install_folder, overwrite, deep_store);
+
+    public static bool store(List<Directories> hierarchie, string name, T payload, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
+    {
+        var new_hierarchie =
+            Instance._IJGetHierarchie(hierarchie);
+        var new_name =
+            Instance._IJGetName(name);
+
+        string path = CreatePathToFile(out bool exists, new_name, "JSON", new_hierarchie, use_install_folder);
+        if (exists && !overwrite)
+            return false;
+
+        // store fields decorated with JsonSeparateAttribute and implementing IJSONsavable<> separately
+        if (deep_store
+            && !store_children(hierarchie, name, payload, use_install_folder: false, overwrite, deep_store: true))
+            return false;
+
+        // store this
+        string path_o = payload.path;
+        payload.path = path;
+
+        var new_payload =
+            preprocess(payload);
+
+        payload.path = path_o;
+
+        JSONsavable.WriteToJsonFile(path, new_payload);
+        return true;
+    }
+
+    public bool store_children(List<Directories> hierarchie, string name, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
+        => store_children(hierarchie, name, (T)this, use_install_folder, overwrite, deep_store);
+
+    public static bool store_children(List<Directories> hierarchie, string name, T payload, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
+    {
+        var new_hierarchie =
+            Instance._IJGetHierarchie(hierarchie);
+        var new_name =
+            Instance._IJGetName(name);
+
+        for ((int max_i, bool success) = (0, true); max_i < JsonSeperateFields.Count(); max_i++)
+        {
+            var field = JsonSeperateFields[max_i];
+            dynamic save_me = field.GetValue(payload); // is S:IJSONsavable<S>
+
+            Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType);
+            Type[] store_args_type = new Type[] { typeof(List<Directories>), typeof(string), field.FieldType, typeof(bool), typeof(bool), typeof(bool) };
+            object[] store_args = new object[] { new_hierarchie, new_name, save_me, use_install_folder, overwrite, deep_store };
+
+            var method = interface_type.GetMethod("store", store_args_type);
+            success &= (bool)method.Invoke(null, store_args);
+
+            // in case of no success: delete it again
+            if (!success)
+            {
+                delete_children(hierarchie, name, use_install_folder, JsonSeperateFields.Count() - max_i);
+                return false;
+            }
+        }
+
+        return true;
+    }
+
+    public static bool load_children(List<Directories> hierarchie, string name, ref T raw_payload, bool use_install_folder = false, bool deep_load = true, bool post_process = true)
+    {
+        var new_hierarchie =
+            Instance._IJGetHierarchie(hierarchie);
+        var new_name =
+            Instance._IJGetName(name);
+
+        bool success = true;
+        for (int max_i = 0; max_i < JsonSeperateFields.Count(); max_i++)
+        {
+            var field = JsonSeperateFields[max_i];
+
+            Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType);
+            Type[] load_args_type = new Type[] { typeof(List<Directories>), typeof(string), field.FieldType.MakeByRefType(), typeof(bool), typeof(bool), typeof(bool) };
+            object[] load_args = new object[] { new_hierarchie, new_name, null, use_install_folder, deep_load, post_process };
+
+            var method = interface_type.GetMethod("load", BindingFlags.Public | BindingFlags.Static, null, load_args_type, null);
+            bool success_i = (bool)method.Invoke(null, load_args);
+
+            field.SetValue(raw_payload, success_i ? load_args[2] : Activator.CreateInstance(field.FieldType));
+            success &= success_i;
+        }
+
+        return success;
+    }
+
+    public static bool load(List<Directories> hierarchie, string name, out T payload, bool use_install_folder = false, bool deep_load = true, bool post_process = true)
+    {
+        payload = default(T);
+        bool success = true;
+
+        var new_hierarchie =
+            Instance._IJGetHierarchie(hierarchie);
+        var new_name =
+            Instance._IJGetName(name);
+
+        string path = CreatePathToFile(out bool loadable, new_name, "JSON", new_hierarchie, use_install_folder);
+        if (!loadable)
+            return false;
+
+        if (!Instance._IJGetRawObject(out T raw_payload, path))
+            return false;
+        raw_payload.name = new_name;
+
+        // load fields decorated with JsonSeparateAttribute and implementing IJSONsavable<> separately
+        if (deep_load
+            && !load_children(hierarchie, name, ref raw_payload, false /*use_install_folder*/))
+            success = false;
+
+        payload = post_process
+            ? postprocess(raw_payload)
+            : raw_payload;
+
+        return success;
+    }
+
+    public static T postprocess(T payload)
+    {
+        if (payload == null)
+            return Instance._IJPostProcess(payload);
+
+        for (int i = 0; i < JsonAutoPostProcessFields.Count(); i++)
+        {
+            var field = JsonAutoPostProcessFields[i];
+            dynamic process_me = field.GetValue(payload); // is S:IJSONsavable<S>
+
+            Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType);
+            Type[] process_args_type = new Type[] { field.FieldType };
+            object[] process_args = new object[] { process_me };
+
+            var method = interface_type.GetMethod("postprocess", process_args_type);
+            var processed = method.Invoke(null, process_args);
+
+            field.SetValue(payload, processed);
+        }
+
+        return Instance._IJPostProcess(payload);
+    }
+
+    public static T preprocess(T payload)
+    {
+        if (payload == null)
+            return Instance._IJPreProcess(payload);
+
+        for (int i = 0; i < JsonAutoPreProcessFields.Count(); i++)
+        {
+            var field = JsonAutoPreProcessFields[i];
+            dynamic process_me = field.GetValue(payload); // is S:IJSONsavable<S>
+
+            Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType);
+            Type[] process_args_type = new Type[] { field.FieldType };
+            object[] process_args = new object[] { process_me };
+
+            var method = interface_type.GetMethod("preprocess", process_args_type);
+            var processed = method.Invoke(null, process_args);
+
+            field.SetValue(payload, processed);
+        }
+
+        return Instance._IJPreProcess(payload);
+    }
+
+    public static void delete_children(List<Directories> hierarchie, string name, bool use_install_folder = false, int skip_last_children = 0)
+    {
+        var new_hierarchie =
+            Instance._IJGetHierarchie(hierarchie);
+        var new_name =
+            Instance._IJGetName(name);
+
+        for (int i = 0; i < JsonSeperateFields.Count() - skip_last_children; i++)
+        {
+            var field = JsonSeperateFields[i];
+
+            Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType);
+            Type[] delete_args_type = new Type[] { typeof(List<Directories>), typeof(string), typeof(bool) };
+            object[] delete_args = new object[] { new_hierarchie, new_name, use_install_folder };
+
+            var method = interface_type.GetMethod("delete", delete_args_type);
+            method.Invoke(null, delete_args);
+        }
+    }
+
+    public static bool delete(List<Directories> hierarchie, string name, bool use_install_folder = false)
+    {
+        var new_hierarchie =
+            Instance._IJGetHierarchie(hierarchie);
+        var new_name =
+            Instance._IJGetName(name);
+
+        string path = CreatePathToFile(out bool _, new_name, "JSON", new_hierarchie, use_install_folder);
+        if (!delete(path))
+            return false;
+
+        delete_children(hierarchie, name, use_install_folder);
+        return true;
+    }
+
+    // does not delete children!
+    private static bool delete(string path)
+    {
+        if (!File.Exists(path))
+            return false;
+
+        File.Delete(path);
+        return true;
+    }
+
+    // public bool delete() => delete(hierarchie, name);
+
+    #endregion MethodTemplates
+
+}
+
+public static class JSONsavable
+{
+    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
+    public sealed class JsonSeparateAttribute : Attribute { }
+
+    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
+    public sealed class JsonAutoPostProcessAttribute : Attribute { }
+
+    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
+    public sealed class JsonAutoPreProcessAttribute : Attribute { }
+
+    /// <summary>
+    /// Writes the given object instance to a Json file, recursively, including public members, excluding [JsonIgnore].
+    /// <para>Object type must have a parameterless constructor.</para>
+    /// <para>Only public properties and variables will be written to the file. These can be any type though, even other non-abstract classes.</para>
+    /// </summary>
+    /// <param name="filePath">The file path to write the object instance to.</param>
+    /// <param name="objectToWrite">The object instance to write to the file.</param>
+    public static bool WriteToJsonFile(string filePath, object objectToWrite)
+    {
+        // This tells your serializer that multiple references are okay.
+        var settings = new JsonSerializerSettings
+        {
+            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
+        };
+
+        TextWriter writer = null;
+        try
+        {
+            string payload = JsonConvert.SerializeObject(objectToWrite, settings);
+            writer = new StreamWriter(filePath);
+            writer.Write(payload);
+            return true;
+        }
+        catch (Exception e)
+        {
+            Debug.LogError(e);
+            return false;
+        }
+        finally
+        {
+            if (writer != null)
+                writer.Close();
+        }
+    }
+
+    /// <summary>
+    /// Reads an object instance from an Json file.
+    /// <para>Object type must have a parameterless constructor.</para>
+    /// </summary>
+    /// <typeparam name="T">The type of object to read from the file.</typeparam>
+    /// <param name="filePath">The file path to read the object instance from.</param>
+    /// <returns>Returns a new instance of the object read from the Json file.</returns>
+    public static bool ReadFromJsonFile<T>(out T payload, string filePath) where T : new()
+    {
+        payload = default(T);
+        TextReader reader = null;
+
+        try
+        {
+            reader = new StreamReader(filePath);
+            var fileContents = reader.ReadToEnd();
+            payload = JsonConvert.DeserializeObject<T>(fileContents);
+            return true;
+        }
+        catch (Exception e)
+        {
+            Debug.LogError(e);
+            return false;
+        }
+        finally
+        {
+            if (reader != null)
+                reader.Close();
+        }
+    }
+
+}
\ No newline at end of file
diff --git a/Assets/Scripts/IJSONsavable.cs.meta b/Assets/Scripts/IJSONsavable.cs.meta
new file mode 100644
index 0000000000000000000000000000000000000000..35b3b75b81fb85c6494b0aa557624e745b972270
--- /dev/null
+++ b/Assets/Scripts/IJSONsavable.cs.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 515d546c824caf241ac2fe28cb89ebf9
+MonoImporter:
+  externalObjects: {}
+  serializedVersion: 2
+  defaultReferences: []
+  executionOrder: 0
+  icon: {instanceID: 0}
+  userData: 
+  assetBundleName: 
+  assetBundleVariant: 
diff --git a/Assets/Scripts/InteractionEngine/FactHandling/FactManager.cs b/Assets/Scripts/InteractionEngine/FactHandling/FactManager.cs
index 8426ed9d9c2c4e816bfb8fe28ed7aa2dfaa0d76c..f096094ef1261ce2f4e9973b283c520cf0a140a2 100644
--- a/Assets/Scripts/InteractionEngine/FactHandling/FactManager.cs
+++ b/Assets/Scripts/InteractionEngine/FactHandling/FactManager.cs
@@ -1,4 +1,5 @@
-using UnityEngine;
+using System;
+using UnityEngine;
 
 /// <summary>
 /// Initiates named <see cref="Fact"/> and adds it to <see cref="StageStatic.stage.factState"/>
@@ -15,44 +16,57 @@ public static class FactManager
     /// <param name="fact">to be added</param>
     /// <param name="exists"><c>true</c> iff <paramref name="fact"/> already has a equivalent counterpart in <paramref name="fact._Facts"/></param>
     /// \copydetails FactManager
-    public static Fact AddFactIfNotFound(Fact fact, out bool exists, bool samestep)
+    public static Fact AddFactIfNotFound(Fact fact, out bool exists, bool samestep, Type gadget, string scroll_label)
     {
-        return StageStatic.stage.factState[StageStatic.stage.factState.Add(fact, out exists, samestep)];
+        return StageStatic.stage.factState[
+            StageStatic.stage.factState.Add(
+                fact, out exists, samestep
+                , gadget ?? (scroll_label == null ? GadgetManager.activeGadget.GetType() : null)
+                , scroll_label
+            )];
     }
 
     /// \copybrief FactManager <summary></summary>
     /// <param name="hit"><c>RaycastHit</c> where and how (orientation) to spawn <see cref="PointFact"/></param>
     /// \copydetails FactManager
-    public static PointFact AddPointFact(RaycastHit hit, bool samestep = false)
+    public static PointFact AddPointFact(RaycastHit hit, bool samestep = false, Type gadget = null, string scroll_label = null)
     {
-        return (PointFact) AddFactIfNotFound(new PointFact(hit.point, hit.normal, StageStatic.stage.factState), out _, samestep);
+        return (PointFact) AddFactIfNotFound(
+            new PointFact(hit.point, hit.normal, StageStatic.stage.factState)
+            , out _, samestep, gadget, scroll_label);
     }
 
     /// \copybrief FactManager <summary></summary>
     /// <param name="point">where to spawn <see cref="PointFact"/></param>
     /// <param name="normal">how (orientation) to spawn <see cref="PointFact"/></param>
     /// \copydetails FactManager
-    public static PointFact AddPointFact(Vector3 point, Vector3 normal, bool samestep = false)
+    public static PointFact AddPointFact(Vector3 point, Vector3 normal, bool samestep = false, Type gadget = null, string scroll_label = null)
     {
-        return (PointFact) AddFactIfNotFound(new PointFact(point, normal, StageStatic.stage.factState), out _, samestep);
+        return (PointFact) AddFactIfNotFound(
+            new PointFact(point, normal, StageStatic.stage.factState)
+            , out _, samestep, gadget, scroll_label);
     }
 
     /// \copybrief FactManager <summary></summary>
     /// <param name="pid"><see cref="Fact.Id"/> of <see cref="PointFact"/> which lies on <paramref name="lid"/></param>
     /// <param name="lid"><see cref="Fact.Id"/> of <see cref="LineFact"/> on which <paramref name="pid"/> lies</param>
     /// \copydetails FactManager
-    public static OnLineFact AddOnLineFact(string pid, string lid, bool samestep = false)
+    public static OnLineFact AddOnLineFact(string pid, string lid, bool samestep = false, Type gadget = null, string scroll_label = null)
     {
-        return (OnLineFact)AddFactIfNotFound(new OnLineFact(pid, lid, StageStatic.stage.factState), out _, samestep);
+        return (OnLineFact)AddFactIfNotFound(
+            new OnLineFact(pid, lid, StageStatic.stage.factState)
+            , out _, samestep, gadget, scroll_label);
     }
 
     /// \copybrief FactManager <summary></summary>
     /// <param name="pid1"><see cref="Fact.Id"/> of first <see cref="PointFact"/> defining a <see cref="LineFact"/></param>
     /// <param name="pid2"><see cref="Fact.Id"/> of second <see cref="PointFact"/> defining a <see cref="LineFact"/></param>
     /// \copydetails FactManager
-    public static LineFact AddLineFact(string pid1, string pid2, bool samestep = false)
+    public static LineFact AddLineFact(string pid1, string pid2, bool samestep = false, Type gadget = null, string scroll_label = null)
     {
-        return (LineFact)AddFactIfNotFound(new LineFact(pid1, pid2, StageStatic.stage.factState), out _, samestep);
+        return (LineFact)AddFactIfNotFound(
+            new LineFact(pid1, pid2, StageStatic.stage.factState)
+            , out _, samestep, gadget, scroll_label);
     }
 
     /// \copybrief FactManager
@@ -62,9 +76,12 @@ public static LineFact AddLineFact(string pid1, string pid2, bool samestep = fal
     /// <param name="pid1"><see cref="Fact.Id"/> of first <see cref="PointFact"/> defining a <see cref="RayFact"/></param>
     /// <param name="pid2"><see cref="Fact.Id"/> of second <see cref="PointFact"/> defining a <see cref="RayFact"/></param>
     /// \copydetails FactManager
-    public static RayFact AddRayFact(string pid1, string pid2, bool samestep = false)
+    public static RayFact AddRayFact(string pid1, string pid2, bool samestep = false, Type gadget = null, string scroll_label = null)
     {
-        RayFact rayFact = (RayFact)AddFactIfNotFound(new RayFact(pid1, pid2, StageStatic.stage.factState), out bool exists, samestep);
+        RayFact rayFact = (RayFact)AddFactIfNotFound(
+            new RayFact(pid1, pid2, StageStatic.stage.factState)
+            , out bool exists, samestep, gadget, scroll_label);
+
         if (exists)
             return rayFact;
 
@@ -79,7 +96,8 @@ void AddHitIfOnLine(RaycastHit hit)
         {
             if (Math3d.IsPointApproximatelyOnLine(rayP1.Point, rayFact.Dir, hit.transform.position))
             {
-                AddOnLineFact(hit.transform.gameObject.GetComponent<FactObject>().URI, rayFact.Id, true);
+                AddOnLineFact(
+                    hit.transform.gameObject.GetComponent<FactObject>().URI, rayFact.Id, true, gadget, scroll_label);
             }
         }
 
@@ -90,8 +108,8 @@ void AddHitIfOnLine(RaycastHit hit)
             AddHitIfOnLine(hit);
 
         // for good measure
-        AddOnLineFact(rayFact.Pid1, rayFact.Id, true);
-        AddOnLineFact(rayFact.Pid2, rayFact.Id, true);
+        AddOnLineFact(rayFact.Pid1, rayFact.Id, true, gadget, scroll_label);
+        AddOnLineFact(rayFact.Pid2, rayFact.Id, true, gadget, scroll_label);
 
         return rayFact;
     }
@@ -101,9 +119,11 @@ void AddHitIfOnLine(RaycastHit hit)
     /// <param name="pid2"><see cref="Fact.Id"/> of second <see cref="PointFact"/> defining a <see cref="AngleFact"/></param>
     /// <param name="pid3"><see cref="Fact.Id"/> of third <see cref="PointFact"/> defining a <see cref="AngleFact"/></param>
     /// \copydetails FactManager
-    public static AngleFact AddAngleFact(string pid1, string pid2, string pid3, bool samestep = false)
+    public static AngleFact AddAngleFact(string pid1, string pid2, string pid3, bool samestep = false, Type gadget = null, string scroll_label = null)
     {
-        return (AngleFact)AddFactIfNotFound(new AngleFact(pid1, pid2, pid3, StageStatic.stage.factState), out _, samestep);
+        return (AngleFact)AddFactIfNotFound(
+            new AngleFact(pid1, pid2, pid3, StageStatic.stage.factState)
+            , out _, samestep, gadget, scroll_label);
     }
 
 }
diff --git a/Assets/Scripts/InteractionEngine/FactHandling/FactOrganizer.cs b/Assets/Scripts/InteractionEngine/FactHandling/FactOrganizer.cs
index 9e72d4b53960ecfa360ea4c92a6e20960a346556..6fac2c6c4c4e50eacf77e4da49e92f37aa66d485 100644
--- a/Assets/Scripts/InteractionEngine/FactHandling/FactOrganizer.cs
+++ b/Assets/Scripts/InteractionEngine/FactHandling/FactOrganizer.cs
@@ -171,15 +171,37 @@ protected internal struct meta
         /// </summary>
         public bool active;
 
+        /// <summary>
+        /// keeps track with wich <see cref="Gadget"/> the <see cref="Fact"/> is created
+        /// <c>null</c> iff its not the case
+        /// </summary>
+        public Type gadget {
+            get => _gadget;
+            set => _gadget = 
+                value == null ? null : 
+                Gadget.GadgetTypes.Contains(value) ? value : 
+                typeof(Gadget.UndefinedGadget);
+        }
+        private Type _gadget;
+
+        /// <summary>
+        /// keeps track with wich <see cref="Scroll"/> the <see cref="Fact"/> is created
+        /// <c>null</c> iff its not the case
+        /// </summary>
+        public string scroll_label;
+
         /// <summary>
         /// Initiator
         /// </summary>
         /// <param name="workflow_id">sets <see cref="workflow_id"/></param>
         /// <param name="active">sets <see cref="active"/></param>
-        public meta(int workflow_id, bool active = true)
+        public meta(int workflow_id, bool active, Type gadget, string scroll_label)
         {
             this.workflow_id = workflow_id;
             this.active = active;
+            this.scroll_label = scroll_label;
+            _gadget = null; //Compiler Restriction
+            this.gadget = gadget;
         }
     }
 
@@ -254,7 +276,7 @@ public static T ReInitializeFactOrganizer<T>
                     old_to_new.Add(sn.Id, add.Id);
                 }
 
-                target.Add(add, out _, sn.samestep);
+                target.Add(add, out _, sn.samestep, source.MetaInf[sn.Id].gadget, source.MetaInf[sn.Id].scroll_label);
             }
             else if (old_to_new.ContainsKey(sn.Id))
             // Remove
@@ -406,7 +428,7 @@ private void PruneWorkflow()
     /// <param name="samestep">set <c>true</c> if <see cref="Fact"/> creation happens as a subsequent/ consequent step of multiple <see cref="Fact"/> creations and/or deletions, 
     /// and you whish that these are affected by a single <see cref="undo"/>/ <see cref="redo"/> step</param>
     /// <returns><see cref="Fact.Id"/> of <paramref name="value"/> or <see cref="FindEquivalent(Fact, out string, out bool)">found</see> <see cref="Fact"/> iff <paramref name="exists"/>==<c>true</c></returns>
-    public string Add(Fact value, out bool exists, bool samestep = false)
+    public string Add(Fact value, out bool exists, bool samestep, Type gadget, string scroll_label)
     {
         soft_resetted = false;
 #pragma warning disable IDE0018 // Inlinevariablendeklaration
@@ -427,7 +449,7 @@ public string Add(Fact value, out bool exists, bool samestep = false)
                 zombie.creation = false; // this meta entry will be deleted, but will not trigger deletion
                 Workflow[MetaInf[key].workflow_id] = zombie;
                 // set new init location
-                MetaInf[key] = new meta(marker, true);
+                MetaInf[key] = new meta(marker, true, gadget, scroll_label);
             }
             // zombies are undead!
             else if (MetaInf[key].active)
@@ -440,7 +462,7 @@ public string Add(Fact value, out bool exists, bool samestep = false)
         {
             key = value.Id;
             FactDict.Add(key, value);
-            MetaInf.Add(key, new meta(marker, true));
+            MetaInf.Add(key, new meta(marker, true, gadget, scroll_label));
         }
 
         WorkflowAdd(new stepnote(key, samestep, true, this));
@@ -457,9 +479,7 @@ public string Add(Fact value, out bool exists, bool samestep = false)
     /// and you whish that these are affected by a single <see cref="undo"/>/ <see cref="redo"/> step</param>
     /// <returns><c>true</c> iff <paramref name="value"/><see cref="Fact.Id">.Id</see> was found.</returns>
     public bool Remove(Fact value, bool samestep = false)
-    {
-        return this.Remove(value.Id, samestep);
-    }
+        => this.Remove(value.Id, samestep);
 
     /// \copybrief Remove(Fact, bool)
     /// <remarks>this will not <see cref="Fact.delete(bool)">delete</see> a <see cref="Fact"/>, but sets it <see cref="meta.active">inactive</see> for later <see cref="Fact.delete(bool)">deletion</see> when <see cref="PruneWorkflow">pruned</see>.</remarks>
@@ -679,7 +699,7 @@ public void fastforward()
     }
 
     FactOrganizer IJSONsavable<FactOrganizer>._IJPostProcess(FactOrganizer raw_payload)
-        => ReInitializeFactOrganizer<FactOrganizer>(raw_payload, false, out _);
+        => raw_payload == null ? raw_payload : ReInitializeFactOrganizer<FactOrganizer>(raw_payload, false, out _);
 
     /// <summary>
     /// Call this after assigning a stored instance in an empty world, that was not drawn.
@@ -763,7 +783,7 @@ public bool DynamiclySolved(
     {
         MissingElements = new List<List<string>>();
         // need to work not on ref/out
-        List<List<string>> Solution_L = new List<List<string>>();
+        List<List<string>> Solution_L = new();
 
         int MissingElementsCount = 0;
         var activeList = FactDict.Values.Where(f => MetaInf[f.Id].active);
@@ -812,4 +832,14 @@ public bool DynamiclySolved(
         return MissingElementsCount == 0;
     }
 
+    public IEnumerable<Type> GetUsedGadgets()
+        => MetaInf.Values.Where(inf => inf.active & inf.gadget != null).Select(inf => inf.gadget).Distinct();
+
+    public int GetNumberOfGadgets() => GetUsedGadgets().Count();
+    
+    public IEnumerable<string> GetUsedScrolls()
+        => MetaInf.Values.Where(inf => inf.active && inf.scroll_label != null).Select(inf => inf.scroll_label).Distinct();
+
+    public int GetNumberOfScrolls() => GetUsedScrolls().Count();
+
 }
\ No newline at end of file
diff --git a/Assets/Scripts/InteractionEngine/FactHandling/SolutionOrganizer.cs b/Assets/Scripts/InteractionEngine/FactHandling/SolutionOrganizer.cs
index c2882c8ded2912cb878b9fef5f88dc62088c5f27..a015611bc99efb848ebca0d50d3912823c91985d 100644
--- a/Assets/Scripts/InteractionEngine/FactHandling/SolutionOrganizer.cs
+++ b/Assets/Scripts/InteractionEngine/FactHandling/SolutionOrganizer.cs
@@ -75,7 +75,7 @@ public string ComparerString
             }
         }
         /// <summary>
-        /// Collection of <c>string</c> representations of *all* available <see cref="FactComparer"/> to choose from.
+        /// Collection of <c>Type</c>s of *all* available <see cref="FactComparer"/> to choose from.
         /// </summary>
         [JsonIgnore]
         public static readonly IEnumerable<Type> fact_comparer = Assembly.GetExecutingAssembly().GetTypes().Where(typeof(FactComparer).IsAssignableFrom);
@@ -144,6 +144,9 @@ public List<Fact> getMasterFactsByIndex (int i)
     string IJSONsavable<SolutionOrganizer>._IJGetName(string name) => name + endingVal;
     SolutionOrganizer IJSONsavable<SolutionOrganizer>._IJPostProcess(SolutionOrganizer raw_payload)
     {
+        if (raw_payload == null)
+            return raw_payload;
+
         SolutionOrganizer payload = 
             ReInitializeFactOrganizer<SolutionOrganizer>
             (raw_payload, false, out Dictionary<string, string> old_to_new);
diff --git a/Assets/Scripts/InteractionEngine/Gadgets/AngleTool.cs b/Assets/Scripts/InteractionEngine/Gadgets/AngleTool.cs
index d39ced488e0e4d32fb93a02a1440e6f8be033dbd..e037236a891b5dd09f939c57c56a5d9a0b7b79e2 100644
--- a/Assets/Scripts/InteractionEngine/Gadgets/AngleTool.cs
+++ b/Assets/Scripts/InteractionEngine/Gadgets/AngleTool.cs
@@ -24,69 +24,68 @@ public class AngleTool : Gadget
     private Vector3 angleMiddlePoint;
     private float curveRadius;
 
-    new void Awake()
+    public override void _Awake()
     {
-        base.Awake();
         UiName = "Angle Mode";
         if(MaxRange == 0)
             MaxRange = GlobalBehaviour.GadgetLaserDistance;
     }
 
     //Initialize Gadget when enabled AND activated
-    new void OnEnable()
+    public override void _OnEnable()
     {
-        base.OnEnable();
-        this.ResetGadget();
+        ResetGadget();
     }
 
-    void OnDisable()
+    public override void _OnDisable()
     {
-        this.ResetGadget();
+        ResetGadget();
     }
 
-    public override void OnHit(RaycastHit hit)
+    public override void _OnHit(RaycastHit hit)
     {
-        if (!GadgetCanBeUsed) return;
-        if (!this.isActiveAndEnabled) return;
+        if (!GadgetCanBeUsed)       return;
+        if (!isActiveAndEnabled)    return;
+
         if (hit.transform.gameObject.layer == LayerMask.NameToLayer("Point"))
         {
             PointFact tempFact = (PointFact)StageStatic.stage.factState[hit.transform.GetComponent<FactObject>().URI];
 
             //If two points were already selected and now the third point got selected
-            if (this.angleModeIsFirstPointSelected && this.angleModeIsSecondPointSelected)
+            if (angleModeIsFirstPointSelected && angleModeIsSecondPointSelected)
             {
                 //Create AngleFact
                 //Check if new Point is equal to one of the previous points -> if true -> cancel
-                if (!(this.angleModeFirstPointSelected.Id == tempFact.Id || this.angleModeSecondPointSelected.Id == tempFact.Id))
+                if (!(angleModeFirstPointSelected.Id == tempFact.Id || angleModeSecondPointSelected.Id == tempFact.Id))
                 {
-                    FactManager.AddAngleFact(((PointFact)this.angleModeFirstPointSelected).Id, ((PointFact)this.angleModeSecondPointSelected).Id, ((PointFact)tempFact).Id);
+                    FactManager.AddAngleFact((angleModeFirstPointSelected).Id, (angleModeSecondPointSelected).Id, tempFact.Id);
                 }
 
                 ResetGadget();
             }
             //If only one point was already selected
-            else if (this.angleModeIsFirstPointSelected && !this.angleModeIsSecondPointSelected)
+            else if (angleModeIsFirstPointSelected && !angleModeIsSecondPointSelected)
             {
                 //Check if the 2 selected points are the same: If not
-                if (this.angleModeFirstPointSelected.Id != tempFact.Id)
+                if (angleModeFirstPointSelected.Id != tempFact.Id)
                 {
-                    this.angleModeIsSecondPointSelected = true;
-                    this.angleModeSecondPointSelected = tempFact;
+                    angleModeIsSecondPointSelected = true;
+                    angleModeSecondPointSelected = tempFact;
 
                     ActivateCurveDrawing();
                 }
-                else
-                {
-                    this.angleModeFirstPointSelected = null;
-                    this.angleModeIsFirstPointSelected = false;
-                }
+                //else
+                //{
+                //    angleModeFirstPointSelected = null;
+                //    angleModeIsFirstPointSelected = false;
+                //}
             }
             //If no point was selected before
             else
             {
                 //Save the first point selected
-                this.angleModeIsFirstPointSelected = true;
-                this.angleModeFirstPointSelected = tempFact;
+                angleModeIsFirstPointSelected = true;
+                angleModeFirstPointSelected = tempFact;
             }
         }
         //No point was hit
@@ -100,33 +99,33 @@ public override void OnHit(RaycastHit hit)
 
     void Update()
     {
-        if (!this.isActiveAndEnabled) return;
-        if (this.curveDrawingActivated)
-            UpdateCurveDrawing(this.Cursor.transform.position);
+        if (!isActiveAndEnabled) return;
+        if (curveDrawingActivated)
+            UpdateCurveDrawing(Cursor.transform.position);
     }
 
     private void ResetGadget()
     {
-        this.angleModeIsFirstPointSelected = false;
-        this.angleModeFirstPointSelected = null;
-        this.angleModeIsSecondPointSelected = false;
-        this.angleModeSecondPointSelected = null;
+        angleModeIsFirstPointSelected = false;
+        angleModeFirstPointSelected = null;
+        angleModeIsSecondPointSelected = false;
+        angleModeSecondPointSelected = null;
         DeactivateCurveDrawing();
     }
 
     //Expect a LineFact here, where Line.Pid2 will be the Basis-Point of the angle
     public void ActivateCurveDrawing()
     {
-        this.lineRenderer.enabled = true;
+        lineRenderer.enabled = true;
         //In AngleMode with 3 Points we want to draw nearly a rectangle so we add a startPoint and an Endpoint to this preview
-        this.lineRenderer.positionCount = curveDrawingVertexCount + 2;
-        this.lineRenderer.material = this.anglePreviewMaterial;
+        lineRenderer.positionCount = curveDrawingVertexCount + 2;
+        lineRenderer.material = anglePreviewMaterial;
 
         lineRenderer.startWidth = 0.05f;
         lineRenderer.endWidth = 0.05f;
 
         //Set CurveDrawing activated
-        this.curveDrawingActivated = true;
+        curveDrawingActivated = true;
 
         //curveEndPoint is a point on the Line selected, with some distance from point2
         curveEndPoint = angleModeSecondPointSelected.Point + 0.3f * (angleModeFirstPointSelected.Point - angleModeSecondPointSelected.Point).magnitude * (angleModeFirstPointSelected.Point - angleModeSecondPointSelected.Point).normalized;
@@ -150,7 +149,7 @@ public void UpdateCurveDrawing(Vector3 currentPosition)
             angleModeSecondPointSelected.Point
         };
 
-        for (float ratio = 0; ratio <= 1; ratio += 1.0f / this.curveDrawingVertexCount)
+        for (float ratio = 0; ratio <= 1; ratio += 1.0f / curveDrawingVertexCount)
         {
             var tangentLineVertex1 = Vector3.Lerp(startPoint, curveMiddlePoint, ratio);
             var tangentLineVertex2 = Vector3.Lerp(curveMiddlePoint, curveEndPoint, ratio);
@@ -168,10 +167,10 @@ public void UpdateCurveDrawing(Vector3 currentPosition)
 
     public void DeactivateCurveDrawing()
     {
-        this.lineRenderer.positionCount = 0;
-        this.linePositions = new List<Vector3>();
-        this.curveDrawingActivated = false;
-        this.lineRenderer.enabled = false;
+        lineRenderer.positionCount = 0;
+        linePositions = new List<Vector3>();
+        curveDrawingActivated = false;
+        lineRenderer.enabled = false;
     }
 
 }
diff --git a/Assets/Scripts/InteractionEngine/Gadgets/ExtraGadget.cs b/Assets/Scripts/InteractionEngine/Gadgets/ExtraGadget.cs
index 944e8f4cd9c037d7a541b0306126d6b854ecafe1..70acd17f9783ff6e9561ef4c662243edcda8a035 100644
--- a/Assets/Scripts/InteractionEngine/Gadgets/ExtraGadget.cs
+++ b/Assets/Scripts/InteractionEngine/Gadgets/ExtraGadget.cs
@@ -2,21 +2,18 @@
 
 public class ExtraGadget : Gadget
 {
-    new void Awake()
+    public override void _Awake()
     {
-        base.Awake();
         UiName = "Extra Mode";
         MaxRange = GlobalBehaviour.GadgetPhysicalDistance;
     }
 
-    new void OnEnable()
-    {
-        base.OnEnable();
-    }
+    public override void _OnDisable() { }
 
-    public override void OnHit(RaycastHit hit)
-    {
+    public override void _OnEnable() { }
 
+    public override void _OnHit(RaycastHit hit)
+    {
+        throw new System.NotImplementedException();
     }
-
 }
diff --git a/Assets/Scripts/InteractionEngine/Gadgets/Gadget.cs b/Assets/Scripts/InteractionEngine/Gadgets/Gadget.cs
index 194e6829cdc1a3d0e1b14e97fec7c4f6804df463..f7ca3e68ccb00ceaa0c22320a8627c469057d211 100644
--- a/Assets/Scripts/InteractionEngine/Gadgets/Gadget.cs
+++ b/Assets/Scripts/InteractionEngine/Gadgets/Gadget.cs
@@ -1,10 +1,14 @@
-using UnityEngine;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using UnityEngine;
 
 /// <summary>
 /// Base class for all Gadgets to derive from.
 /// A Gadget is a tool for the player (and level editor) to interact with the GameWorld.
 /// </summary>
-public class Gadget : MonoBehaviour
+public abstract class Gadget: MonoBehaviour
 {
     /// <summary> Position in tool belt. </summary>
     /// <remarks>Set in Inspector or <see cref="Awake"/></remarks>
@@ -19,30 +23,77 @@ public class Gadget : MonoBehaviour
     /// <summary>Which sprite to use</summary>
     /// <remarks>Set in Inspector</remarks>
     public Sprite Sprite;
-    /// <summary>Layers to ignore for thid gadget by default.</summary>
+    /// <summary>Layers to ignore for this gadget by default.</summary>
     /// <remarks>Set in Inspector</remarks>
     public LayerMask ignoreLayerMask;
     /// <summary>Which cursor to use</summary>
     /// <remarks>When not set in Inspector, will be searching for any <see cref="WorldCursor"/>.</remarks>
     public WorldCursor Cursor;
 
-    protected void Awake()
+    /// <summary>
+    /// Collection of <c>Type</c>s of *all* available <see cref="Gadget"/>s to choose from.
+    /// </summary>
+    public static readonly IEnumerable<Type> GadgetTypes = Assembly.GetExecutingAssembly().GetTypes().Where(typeof(Gadget).IsAssignableFrom);
+
+
+    void Awake()
     {
         if (Cursor == null)
             Cursor = GameObject.FindObjectOfType<WorldCursor>();
 
-        CommunicationEvents.TriggerEvent.AddListener(OnHit);
+        CommunicationEvents.TriggerEvent.AddListener(_OnHit);
+
+        _Awake();
     }
 
-    protected void OnEnable()
+    void OnEnable()
     {
         this.Cursor.setLayerMask(~this.ignoreLayerMask.value);
         Cursor.MaxRange = MaxRange;
+
+        _OnEnable();
     }
 
+    void OnDisable()
+    {
+        _OnDisable();
+    }
+
+    public void OnHit (RaycastHit hit)
+    {
+        _OnHit(hit);
+    }
+
+    public abstract void _Awake();
+    public abstract void _OnEnable();
+    public abstract void _OnDisable();
+
     /// <summary>
     /// Called when <see cref="CommunicationEvents.TriggerEvent"/> is invoked, a.k.a. when Player clicks in GameWorld.
     /// </summary>
     /// <param name="hit">the position where it was clicked</param>
-    public virtual void OnHit(RaycastHit hit) { }
+    public abstract void _OnHit(RaycastHit hit);
+
+    public class UndefinedGadget : Gadget
+    {
+        public override void _Awake()
+        {
+            throw new NotImplementedException();
+        }
+
+        public override void _OnDisable()
+        {
+            throw new NotImplementedException();
+        }
+
+        public override void _OnEnable()
+        {
+            throw new NotImplementedException();
+        }
+
+        public override void _OnHit(RaycastHit hit)
+        {
+            throw new NotImplementedException();
+        }
+    }
 }
diff --git a/Assets/Scripts/InteractionEngine/Gadgets/LineTool.cs b/Assets/Scripts/InteractionEngine/Gadgets/LineTool.cs
index a11985f3ed0d852997753c885244aba6d72f18dd..3945dd8ee3e779a397b2ffe99d3c330139352dee 100644
--- a/Assets/Scripts/InteractionEngine/Gadgets/LineTool.cs
+++ b/Assets/Scripts/InteractionEngine/Gadgets/LineTool.cs
@@ -15,27 +15,25 @@ public class LineTool : Gadget
     private List<Vector3> linePositions = new List<Vector3>();
     public Material linePreviewMaterial;
 
-    new void Awake()
+    public override void _Awake()
     {
-        base.Awake();
         UiName = "Line Mode";
         if (MaxRange == 0)
             MaxRange = GlobalBehaviour.GadgetLaserDistance;
     }
 
     //Initialize Gadget when enabled AND activated
-    new void OnEnable()
+    public override void _OnEnable()
     {
-        base.OnEnable();
         this.ResetGadget();
     }
 
-    void OnDisable()
+    public override void _OnDisable()
     {
         this.ResetGadget();
     }
 
-    public override void OnHit(RaycastHit hit)
+    public override void _OnHit(RaycastHit hit)
     {
         if (!GadgetCanBeUsed) return;
         if (!this.isActiveAndEnabled) return;
diff --git a/Assets/Scripts/InteractionEngine/Gadgets/LotTool.cs b/Assets/Scripts/InteractionEngine/Gadgets/LotTool.cs
index eeef723c9a94363071001077e72ff0b1aa25b5c4..67b1aa3ca2c1fdca71ccdf8498ce6975df5017b9 100644
--- a/Assets/Scripts/InteractionEngine/Gadgets/LotTool.cs
+++ b/Assets/Scripts/InteractionEngine/Gadgets/LotTool.cs
@@ -20,31 +20,26 @@ public class LotTool : Gadget
     private List<Vector3> linePositions = new List<Vector3>();
     public Material linePreviewMaterial;
 
-    new void Awake()
+    public override void _Awake()
     {
-        base.Awake();
         this.UiName = "Lot Mode";
         if (MaxRange == 0)
             MaxRange = GlobalBehaviour.GadgetLaserDistance;
     }
 
     //Initialize Gadget when enabled AND activated
-    new void OnEnable()
+    public override void _OnEnable()
     {
-        base.OnEnable();
         this.ResetGadget();
     }
 
-    void OnDisable()
+    public override void _OnDisable()
     {
         this.ResetGadget();
     }
 
-    public override void OnHit(RaycastHit hit)
+    public override void _OnHit(RaycastHit hit)
     {
-        
-
-
         void CreateRayAndAngles(string pidIntersectionPoint, string pidLotPoint, bool samestep)
         {
             FactManager.AddRayFact(pidIntersectionPoint, pidLotPoint, samestep);
diff --git a/Assets/Scripts/InteractionEngine/Gadgets/Pendulum.cs b/Assets/Scripts/InteractionEngine/Gadgets/Pendulum.cs
index f27caf4d834292f2308239ddaadce06a268dcbce..411a16673d5fce28bfce5fadb55dd09254bc2d02 100644
--- a/Assets/Scripts/InteractionEngine/Gadgets/Pendulum.cs
+++ b/Assets/Scripts/InteractionEngine/Gadgets/Pendulum.cs
@@ -13,27 +13,25 @@ public class Pendulum : Gadget
     private List<Vector3> linePositions = new List<Vector3>();
     public Material linePreviewMaterial;
 
-    new void Awake()
+    public override void _Awake()
     {
-        base.Awake();
         this.UiName = "Pendulum";
         if (MaxRange == 0)
             MaxRange = GlobalBehaviour.GadgetLaserDistance;
     }
 
-    new void OnEnable()
+    public override void _OnEnable()
     {
-        base.OnEnable();
         this.ResetGadget();
         ActivateLineDrawing();
     }
 
-    void OnDisable()
+    public override void _OnDisable()
     {
         this.ResetGadget();
     }
 
-    public override void OnHit(RaycastHit hit)
+    public override void _OnHit(RaycastHit hit)
     {
 
         if (!GadgetCanBeUsed) return; 
@@ -44,8 +42,7 @@ public override void OnHit(RaycastHit hit)
             PointFact tempFact = StageStatic.stage.factState[hit.transform.GetComponent<FactObject>().URI] as PointFact;
 
             //Raycast downwoard
-            RaycastHit ground;
-            if(Physics.Raycast(tempFact.Point, Vector3.down, out ground, Mathf.Infinity, this.LayerPendulumHits.value))
+            if(Physics.Raycast(tempFact.Point, Vector3.down, out RaycastHit ground, Mathf.Infinity, this.LayerPendulumHits.value))
             {
                 FactManager.AddPointFact(ground);
             }
@@ -89,8 +86,7 @@ private void UpdateLineDrawing()
         this.linePositions[0] = this.Cursor.transform.position;
 
         //Raycast downwoard
-        RaycastHit ground;
-        if (Physics.Raycast(this.linePositions[0], Vector3.down, out ground, Mathf.Infinity, this.LayerPendulumHits.value))
+        if (Physics.Raycast(this.linePositions[0], Vector3.down, out RaycastHit ground, Mathf.Infinity, this.LayerPendulumHits.value))
             this.linePositions[1] = ground.point;
         else
             this.linePositions[1] = this.linePositions[0];
diff --git a/Assets/Scripts/InteractionEngine/Gadgets/Pointer.cs b/Assets/Scripts/InteractionEngine/Gadgets/Pointer.cs
index 73dfcdcdd5c0c98b4c79bb356f0d56db5177ee9b..45d9f02fa26bc072cd89a23c9b00ff2a7be78f1d 100644
--- a/Assets/Scripts/InteractionEngine/Gadgets/Pointer.cs
+++ b/Assets/Scripts/InteractionEngine/Gadgets/Pointer.cs
@@ -5,21 +5,19 @@
 
 public class Pointer : Gadget
 {
-    new void Awake()
+    public override void _Awake()
     {
-        base.Awake();
         this.UiName = "Point Mode";
         if (MaxRange == 0)
             MaxRange = GlobalBehaviour.GadgetLaserDistance;
     }
 
-    public override void OnHit(RaycastHit hit)
+    public override void _OnHit(RaycastHit hit)
     {
-        //Debug.Log("pointer1");
         if (!GadgetCanBeUsed) return;
-       
+
         if (!this.isActiveAndEnabled) return;
-        
+
         var pid = FactManager.AddPointFact(hit).Id;
 
         if (hit.transform.gameObject.layer == LayerMask.NameToLayer("Ray"))
@@ -27,5 +25,8 @@ public override void OnHit(RaycastHit hit)
             FactManager.AddOnLineFact(pid, hit.transform.GetComponent<FactObject>().URI, true);
         }
     }
-  
+
+    public override void _OnEnable() { }
+
+    public override void _OnDisable() { }
 }
diff --git a/Assets/Scripts/InteractionEngine/Gadgets/PoleTool.cs b/Assets/Scripts/InteractionEngine/Gadgets/PoleTool.cs
index 003d18e1819d90ad69d8beafbe383a7f1180179c..a95d945a08c2165b0d29b637bc3fbcc7243eb668 100644
--- a/Assets/Scripts/InteractionEngine/Gadgets/PoleTool.cs
+++ b/Assets/Scripts/InteractionEngine/Gadgets/PoleTool.cs
@@ -15,9 +15,8 @@ public class PoleTool : Gadget
     public float poleHeight = 1f;
     public float maxHeight;
 
-    new void Awake()
+    public override void _Awake()
     {
-        base.Awake();
         UiName = "PoleTool";
         if (MaxRange == 0)
             MaxRange = GlobalBehaviour.GadgetPhysicalDistance;
@@ -25,19 +24,18 @@ public class PoleTool : Gadget
             maxHeight = 0.1f;
     }
 
-    new void OnEnable()
+    public override void _OnEnable()
     {
-        base.OnEnable();
         this.ResetGadget();
         ActivateLineDrawing();
     }
 
-    void OnDisable()
+    public override void _OnDisable()
     {
         this.ResetGadget();
     }
 
-    public override void OnHit(RaycastHit hit)
+    public override void _OnHit(RaycastHit hit)
     {
 
         if (!GadgetCanBeUsed) return; 
diff --git a/Assets/Scripts/InteractionEngine/Gadgets/Remover.cs b/Assets/Scripts/InteractionEngine/Gadgets/Remover.cs
index 47016f20e86581879d6b19431db06f62e9cee2d0..69438a411b22e1e405e8b648c7b3b11f43d463d7 100644
--- a/Assets/Scripts/InteractionEngine/Gadgets/Remover.cs
+++ b/Assets/Scripts/InteractionEngine/Gadgets/Remover.cs
@@ -5,15 +5,18 @@
 
 public class Remover : Gadget
 {
-    new void Awake()
+    public override void _Awake()
     {
-        base.Awake();
         this.UiName = "Remove Mode";
         if (MaxRange == 0)
             MaxRange = GlobalBehaviour.GadgetLaserDistance;
     }
 
-    public override void OnHit(RaycastHit hit)
+    public override void _OnDisable() { }
+
+    public override void _OnEnable() { }
+
+    public override void _OnHit(RaycastHit hit)
     {
 
         if (!GadgetCanBeUsed) return;
diff --git a/Assets/Scripts/InteractionEngine/Gadgets/Tape.cs b/Assets/Scripts/InteractionEngine/Gadgets/Tape.cs
index 933e75dba3b5009c049ff3d9e7e2da468009e726..273f96090c9fe7efb9dd3cf55c0bc8628d248fc1 100644
--- a/Assets/Scripts/InteractionEngine/Gadgets/Tape.cs
+++ b/Assets/Scripts/InteractionEngine/Gadgets/Tape.cs
@@ -16,29 +16,25 @@ public class Tape : Gadget
     private List<Vector3> linePositions = new List<Vector3>();
     public Material linePreviewMaterial;
 
-    new void Awake()
+    public override void _Awake()
     {
-        base.Awake();
         this.UiName = "Distance Mode";
         if (MaxRange == 0)
             MaxRange = GlobalBehaviour.GadgetPhysicalDistance;
-        //test
-        //MaxRange = 100;
     }
 
-    //Initialize Gadget when enabled AND activated
-    new void OnEnable()
+    //Initialize Gadget when enabled
+    public override void _OnEnable()
     {
-        base.OnEnable();
         this.ResetGadget();
     }
 
-    void OnDisable()
+    public override void _OnDisable()
     {
         this.ResetGadget();
     }
 
-    public override void OnHit(RaycastHit hit)
+    public override void _OnHit(RaycastHit hit)
     {
         if (!GadgetCanBeUsed) return;
         if (!this.isActiveAndEnabled) return;
@@ -142,4 +138,5 @@ private void DeactivateLineDrawing()
         this.linePositions = new List<Vector3>();
         this.lineRenderer.enabled = false;
     }
+
 }
diff --git a/Assets/Scripts/InventoryStuff/DisplayScrolls.cs b/Assets/Scripts/InventoryStuff/DisplayScrolls.cs
index fbd9abf21f1faa7448ff70b8ce88481b46114a79..ae6a58960e4f9fcd1a446c3ac362f91aef3b1825 100644
--- a/Assets/Scripts/InventoryStuff/DisplayScrolls.cs
+++ b/Assets/Scripts/InventoryStuff/DisplayScrolls.cs
@@ -10,13 +10,11 @@ public class DisplayScrolls : MonoBehaviour
     public string preferredStartScrollName;
     public int tryScrollListTimes = 2;
 
-    public List<Scroll> scrolls;
+    static public List<Scroll> AvailableScrolls;
     public GameObject[] ScrollButtons;
     public GameObject ScrollPrefab;
     public GameObject DetailScreen;
 
-
-
     public int x_Start;
     public int y_Start;
     public int X_Pacece_Between_Items;
@@ -24,23 +22,11 @@ public class DisplayScrolls : MonoBehaviour
     public int number_of_Column;
 
 
-    // Update is called once per frame
-    void Update()
-    {
-
-    }
-
-
     public Vector3 GetPosition(int i)
     {
         return new Vector3(x_Start + (X_Pacece_Between_Items * (i % number_of_Column)), y_Start + (-y_Pacece_Between_Items * (i / number_of_Column)), 0f);
     }
 
-    /*  [System.Serializable]
-      class ScrollArrayWrapper {
-          public Scroll[] Scrolls;
-      };*/
-
     // Start is called before the first frame update
     void Start()
     {
@@ -106,22 +92,20 @@ IEnumerator getScrollsfromServer()
 
     void BuildScrolls(string jsonString)
     {
-        var scrolls = Scroll.FromJSON(jsonString);
-        this.scrolls = scrolls;
-        ScrollButtons = new GameObject[this.scrolls.Count];
+        AvailableScrolls = Scroll.FromJSON(jsonString);
+        ScrollButtons = new GameObject[AvailableScrolls.Count];
         //Build Selection-GUI of Scrolls
-        for (int i = 0; i < this.scrolls.Count; i++)
+        for (int i = 0; i < AvailableScrolls.Count; i++)
         {
-
             var obj = Instantiate(ScrollPrefab, Vector3.zero, Quaternion.identity, transform);
             obj.GetComponent<RectTransform>().localPosition = GetPosition(i);
-            obj.GetComponent<ScrollClickedScript>().scroll = this.scrolls[i];
+            obj.GetComponent<ScrollClickedScript>().scroll = AvailableScrolls[i];
             obj.GetComponent<ScrollClickedScript>().DetailScreen = this.DetailScreen;
-            obj.transform.GetChild(0).gameObject.GetComponent<TextMeshProUGUI>().text = this.scrolls[i].label;
+            obj.transform.GetChild(0).gameObject.GetComponent<TextMeshProUGUI>().text = AvailableScrolls[i].label;
             ScrollButtons[i] = obj;
         }
 
-        Scroll preferredStartScroll = this.scrolls.Find(x => x.label.Equals(preferredStartScrollName));
+        Scroll preferredStartScroll = AvailableScrolls.Find(x => x.label.Equals(preferredStartScrollName));
         if (preferredStartScroll != null)
             this.DetailScreen.GetComponent<ScrollDetails>().setScroll(preferredStartScroll);
     }
diff --git a/Assets/Scripts/InventoryStuff/ScrollDetails.cs b/Assets/Scripts/InventoryStuff/ScrollDetails.cs
index 355b6b1c20de5a3411b7df3e61a808d956b67075..2536c3004f3ad5adf2d1d75dfee5e906e5405eb9 100644
--- a/Assets/Scripts/InventoryStuff/ScrollDetails.cs
+++ b/Assets/Scripts/InventoryStuff/ScrollDetails.cs
@@ -10,7 +10,7 @@ public class ScrollDetails : MonoBehaviour
 {
     public WorldCursor cursor;
     public GameObject parameterDisplayPrefab;
-    public Scroll scroll;
+    public Scroll ActiveScroll;
 
     public int x_Start;
     public int y_Start;
@@ -44,7 +44,7 @@ public void setScroll(Scroll s)
         Transform originalScroll = gameObject.transform.GetChild(1).transform;
         Transform originalScrollView = originalScroll.GetChild(1);
         Transform originalViewport = originalScrollView.GetChild(0);
-        this.scroll = s;
+        this.ActiveScroll = s;
         originalScroll.GetChild(0).GetComponent<TextMeshProUGUI>().text = s.description;
 
         //Clear all current ScrollFacts
@@ -117,11 +117,7 @@ IEnumerator sendView(string endpoint)
         using UnityWebRequest www = UnityWebRequest.Put(ServerAdress + endpoint, body);
         www.method = UnityWebRequest.kHttpVerbPOST;
         www.SetRequestHeader("Content-Type", "application/json");
-        var async = www.SendWebRequest();
-        while (!async.isDone) {
-            //Non blocking wait for one frame, for letting the game do other things
-            yield return null;
-        }
+        yield return www.SendWebRequest();
 
         if (www.result == UnityWebRequest.Result.ConnectionError 
          || www.result == UnityWebRequest.Result.ProtocolError)
@@ -144,17 +140,19 @@ private string prepareScrollAssignments()
 
         for (int i = 0; i < ParameterDisplays.Count; i++)
         {
-            Scroll.ScrollAssignment listEntry = new Scroll.ScrollAssignment();
             tempFact = ParameterDisplays[i].GetComponentInChildren<DropHandling>().currentFact;
             if (tempFact != null)
             {
-                listEntry.fact = new Scroll.UriReference(this.scroll.requiredFacts[i].@ref.uri);
-                listEntry.assignment = new JSONManager.OMS(tempFact.Id);
+                Scroll.ScrollAssignment listEntry = new Scroll.ScrollAssignment
+                {
+                    fact = new Scroll.UriReference(ActiveScroll.requiredFacts[i].@ref.uri),
+                    assignment = new JSONManager.OMS(tempFact.Id)
+                };
                 assignmentList.Add(listEntry);
             }
         }
 
-        Scroll.FilledScroll filledScroll = new Scroll.FilledScroll(this.scroll.@ref, assignmentList);
+        Scroll.FilledScroll filledScroll = new Scroll.FilledScroll(ActiveScroll.@ref, assignmentList);
         return Scroll.ToJSON(filledScroll);
     }
 
@@ -169,7 +167,7 @@ private void readPushout(List<Scroll.ScrollFact> pushoutFacts)
             Fact newFact = ParsingDictionary.parseFactDictionary[pushoutFacts[i].getType()].Invoke(pushoutFacts[i]);
             if (newFact != null)
             {
-                PushoutFactEvent.Invoke(FactManager.AddFactIfNotFound(newFact, out bool exists, samestep));
+                PushoutFactEvent.Invoke(FactManager.AddFactIfNotFound(newFact, out bool exists, samestep, null, ActiveScroll.label));
             }
             else {
                 Debug.Log("Parsing on pushout-fact returned null -> One of the dependent facts does not exist");
diff --git a/Assets/Scripts/JSONManager.cs b/Assets/Scripts/JSONManager.cs
index afb7adfd623d55f05fbb5bd4a2c4fcde32932c7d..a0df7e51260ac9f07b61918e3b9140f322e680f8 100644
--- a/Assets/Scripts/JSONManager.cs
+++ b/Assets/Scripts/JSONManager.cs
@@ -1,244 +1,21 @@
 using System.Collections.Generic;
-using System.Reflection;
-using System.IO;
-using System;
-using System.Linq;
 using Newtonsoft.Json;
 using JsonSubTypes;
-using System.Collections;
-using UnityEngine;
-using Newtonsoft.Json.Linq;
-using static CommunicationEvents;
 
-// I would go for static virtual methods, but C#9 does not allow me...
-// static methods cannot be overwritten -> virtual
-public interface IJSONsavable<T> where T : IJSONsavable<T>, new()
-{
-    // stand-in for non static methods
-    public static readonly IJSONsavable<T> Instance = new T();
-    public static readonly FieldInfo[] JsonSeperateFields =
-         typeof(T)
-        .GetFields(
-            BindingFlags.Instance |
-            BindingFlags.Public |
-            BindingFlags.NonPublic |
-            BindingFlags.Static )
-        .Where((field)
-            => field.GetCustomAttributes().Any((attribute) 
-                => attribute.GetType() == typeof(JSONManager.JsonSeparateAttribute))
-            && field.FieldType.GetInterfaces().Any((inter) 
-                => inter.IsGenericType && inter.GetGenericTypeDefinition() == typeof(IJSONsavable<>)))
-        .ToArray();
-
-
-    // TODO: this?
-    public string name { get; set; }
-    public string path { get; set; }
-    protected static List<Directories>
-        hierarchie = new List<Directories> { Directories.misc };
-
-    #region OverridableMethods
-
-    public virtual string _IJGetName(string name) => name;
-    public virtual List<Directories> _IJGetHierarchie(List<Directories> hierarchie_base)
-    {
-        hierarchie_base ??= new List<Directories>();
-        return hierarchie_base.Concat(hierarchie).ToList();
-    }
-    public virtual bool _IJGetRawObject(out T payload, string path) => JSONManager.ReadFromJsonFile<T>(out payload, path);
-    public virtual T _IJPreProcess(T payload) => payload;
-    public virtual T _IJPostProcess(T payload) => payload;
-
-    #endregion OverridableMethods
-
-
-    #region MethodTemplates
-
-    public bool store(List<Directories> hierarchie, string name, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
-        => store(hierarchie, name, (T) this, use_install_folder, overwrite, deep_store);
-
-    public static bool store(List<Directories> hierarchie, string name, T payload, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
-    {
-        var new_hierarchie = 
-            Instance._IJGetHierarchie(hierarchie);
-        var new_name = 
-            Instance._IJGetName(name);
-
-        string path = CreatePathToFile(out bool exists, new_name, "JSON", new_hierarchie, use_install_folder);
-        if (exists && !overwrite)
-            return false;
-
-        // store fields decorated with JsonSeparateAttribute and implementing IJSONsavable<> separately
-        if (deep_store
-            && !store_children(hierarchie, name, payload, use_install_folder: false, overwrite, deep_store: true))
-                return false;
-
-        // store this
-        string path_o = payload.path;
-        payload.path = path;
-
-        var new_payload =
-            Instance._IJPreProcess(payload);
-
-        payload.path = path_o;
-
-        JSONManager.WriteToJsonFile(path, new_payload);
-        return true;
-    }
-    
-    public bool store_children(List<Directories> hierarchie, string name, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
-        => store_children(hierarchie, name, (T) this, use_install_folder, overwrite, deep_store);
-
-    public static bool store_children(List<Directories> hierarchie, string name, T payload, bool use_install_folder = false, bool overwrite = true, bool deep_store = true)
-    {
-        var new_hierarchie =
-            Instance._IJGetHierarchie(hierarchie);
-        var new_name =
-            Instance._IJGetName(name);
-
-        for ((int max_i, bool success) = (0, true); max_i < JsonSeperateFields.Count(); max_i++)
-        {
-            var field = JsonSeperateFields[max_i];
-            dynamic save_me = field.GetValue(payload); // is S:IJSONsavable<S>
-
-            Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType);
-            Type[] store_args_type = new Type[] { typeof(List<Directories>), typeof(string), field.FieldType, typeof(bool), typeof(bool), typeof(bool) };
-            object[] store_args = new object[] { new_hierarchie, new_name, save_me, use_install_folder, overwrite, deep_store };
-
-            var method = interface_type.GetMethod("store", store_args_type);
-            success &= (bool)method.Invoke(null, store_args);
-
-            // in case of no success: delete it again
-            if (!success)
-            {
-                delete_children(hierarchie, name, use_install_folder, JsonSeperateFields.Count() - max_i);
-                return false;
-            }
-        }
-
-        return true;
-    }
-
-    public static bool load_children(List<Directories> hierarchie, string name, ref T raw_payload, bool use_install_folder = false, bool deep_load = true, bool post_process = true)
-    {
-        var new_hierarchie =
-            Instance._IJGetHierarchie(hierarchie);
-        var new_name =
-            Instance._IJGetName(name);
-
-        bool success = true;
-        for (int max_i = 0; max_i < JsonSeperateFields.Count(); max_i++)
-        {
-            var field = JsonSeperateFields[max_i];
-
-            Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType);
-            Type[] load_args_type = new Type[] { typeof(List<Directories>), typeof(string), field.FieldType.MakeByRefType(), typeof(bool), typeof(bool), typeof(bool) };
-            object[] load_args = new object[] { new_hierarchie, new_name, null, use_install_folder, deep_load, post_process };
-
-            var method = interface_type.GetMethod("load", BindingFlags.Public | BindingFlags.Static, null, load_args_type, null);
-            bool success_i = (bool)method.Invoke(null, load_args);
-            
-            field.SetValue(raw_payload, success_i ? load_args[2] : Activator.CreateInstance(field.FieldType));
-            success &= success_i;
-        }
-
-        return success;
-    }
-
-    public static bool load(List<Directories> hierarchie, string name, out T payload, bool use_install_folder = false, bool deep_load = true, bool post_process = true)
-    {
-        payload = default(T);
-        bool success = true;
-
-        var new_hierarchie =
-            Instance._IJGetHierarchie(hierarchie);
-        var new_name =
-            Instance._IJGetName(name);
-
-        string path = CreatePathToFile(out bool loadable, new_name, "JSON", new_hierarchie, use_install_folder);
-        if (!loadable)
-            return false;
-
-        if (!Instance._IJGetRawObject(out T raw_payload, path))
-            return false;
-        raw_payload.name = new_name;
-
-        // load fields decorated with JsonSeparateAttribute and implementing IJSONsavable<> separately
-        if (deep_load
-            && !load_children(hierarchie, name, ref raw_payload, false /*use_install_folder*/))
-            success = false;
-
-        payload = post_process
-            ? Instance._IJPostProcess(raw_payload)
-            : raw_payload;
-
-        return success;
-    }
-
-    public static void delete_children(List<Directories> hierarchie, string name, bool use_install_folder = false, int skip_last_children = 0)
-    {
-        var new_hierarchie =
-            Instance._IJGetHierarchie(hierarchie);
-        var new_name =
-            Instance._IJGetName(name);
-
-        for(int i = 0; i < JsonSeperateFields.Count() - skip_last_children; i++) 
-        {
-            var field = JsonSeperateFields[i];
-
-            Type interface_type = typeof(IJSONsavable<>).MakeGenericType(field.FieldType);
-            Type[] delete_args_type = new Type[] { typeof(List<Directories>), typeof(string), typeof(bool) };
-            object[] delete_args = new object[] { new_hierarchie, new_name, use_install_folder };
-
-            var method = interface_type.GetMethod("delete", delete_args_type);
-            method.Invoke(null, delete_args);
-        }
-    }
-
-    public static bool delete(List<Directories> hierarchie, string name, bool use_install_folder = false)
-    {
-        var new_hierarchie =
-            Instance._IJGetHierarchie(hierarchie);
-        var new_name =
-            Instance._IJGetName(name);
-
-        string path = CreatePathToFile(out bool _, new_name, "JSON", new_hierarchie, use_install_folder);
-        if (!delete(path))
-            return false;
-
-        delete_children(hierarchie, name, use_install_folder);
-        return true;
-    }
-
-    // does not delete children!
-    private static bool delete(string path)
-    {
-        if (!File.Exists(path))
-            return false;
-
-        File.Delete(path);
-        return true;
-    }
-
-    // public bool delete() => delete(hierarchie, name);
-
-    #endregion MethodTemplates
-
-}
 
 public class MMTURICollection
 {
-    public string Point = "http://mathhub.info/MitM/core/geometry?3DGeometry?point";
-    public string Tuple = "http://gl.mathhub.info/MMT/LFX/Sigma?Symbols?Tuple";
-    public string LineType = "http://mathhub.info/MitM/core/geometry?Geometry/Common?line_type";
-    public string LineOf = "http://mathhub.info/MitM/core/geometry?Geometry/Common?lineOf";
-    public string OnLine = "http://mathhub.info/MitM/core/geometry?Geometry/Common?onLine";
-    public string Ded = "http://mathhub.info/MitM/Foundation?Logic?ded";
-    public string Eq = "http://mathhub.info/MitM/Foundation?Logic?eq";
-    public string Metric = "http://mathhub.info/MitM/core/geometry?Geometry/Common?metric";
-    public string Angle = "http://mathhub.info/MitM/core/geometry?Geometry/Common?angle_between";
-    public string Sketch = "http://mathhub.info/MitM/Foundation?InformalProofs?proofsketch";
-    public string RealLit = "http://mathhub.info/MitM/Foundation?RealLiterals?real_lit";
+    public string Tuple     = "http://gl.mathhub.info/MMT/LFX/Sigma?Symbols?Tuple";
+    public string Point     = "http://mathhub.info/MitM/core/geometry?3DGeometry?point";
+    public string LineType  = "http://mathhub.info/MitM/core/geometry?Geometry/Common?line_type";
+    public string LineOf    = "http://mathhub.info/MitM/core/geometry?Geometry/Common?lineOf";
+    public string OnLine    = "http://mathhub.info/MitM/core/geometry?Geometry/Common?onLine";
+    public string Metric    = "http://mathhub.info/MitM/core/geometry?Geometry/Common?metric";
+    public string Angle     = "http://mathhub.info/MitM/core/geometry?Geometry/Common?angle_between";
+    public string Ded       = "http://mathhub.info/MitM/Foundation?Logic?ded";
+    public string Eq        = "http://mathhub.info/MitM/Foundation?Logic?eq";
+    public string Sketch    = "http://mathhub.info/MitM/Foundation?InformalProofs?proofsketch";
+    public string RealLit   = "http://mathhub.info/MitM/Foundation?RealLiterals?real_lit";
 }
 
 public static class JSONManager 
@@ -371,79 +148,4 @@ public MMTValueDeclaration(string label, MMTTerm lhs, MMTTerm valueTp, MMTTerm v
             this.value = value;
         }
     }
-
-    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false)]
-    public sealed class JsonSeparateAttribute : Attribute
-    {
-        public JsonSeparateAttribute() { }
-    }
-
-    // TODO? /// <para>If there are public properties/variables that you do not want written to the file, decorate them with the [JsonIgnore] attribute.</para>
-
-    /// <summary>
-    /// Writes the given object instance to a Json file, recursively to set depth, including all members.
-    /// <para>Object type must have a parameterless constructor.</para>
-    /// <para>Only All properties and variables will be written to the file. These can be any type though, even other non-abstract classes.</para>
-    /// </summary>
-    /// <param name="filePath">The file path to write the object instance to.</param>
-    /// <param name="objectToWrite">The object instance to write to the file.</param>
-    /// <param name="max_depth">The depth recursion will occur. Default = 0.</param>
-    public static bool WriteToJsonFile(string filePath, object objectToWrite, int max_depth = 0)
-    {
-        // This tells your serializer that multiple references are okay.
-        var settings = new JsonSerializerSettings
-        {
-            ReferenceLoopHandling = ReferenceLoopHandling.Ignore
-        };
-
-        TextWriter writer = null;
-        try
-        {
-            string payload = JsonConvert.SerializeObject(objectToWrite, settings);
-            writer = new StreamWriter(filePath);
-            writer.Write(payload);
-            return true;
-        }
-        catch (Exception e)
-        {
-            Debug.LogError(e);
-            return false;
-        }
-        finally
-        {
-            if (writer != null)
-                writer.Close();
-        }
-    }
-
-    /// <summary>
-    /// Reads an object instance from an Json file.
-    /// <para>Object type must have a parameterless constructor.</para>
-    /// </summary>
-    /// <typeparam name="T">The type of object to read from the file.</typeparam>
-    /// <param name="filePath">The file path to read the object instance from.</param>
-    /// <returns>Returns a new instance of the object read from the Json file.</returns>
-    public static bool ReadFromJsonFile<T>(out T payload, string filePath) where T : new()
-    {
-        payload = default(T);
-        TextReader reader = null;
-
-        try
-        {
-            reader = new StreamReader(filePath);
-            var fileContents = reader.ReadToEnd();
-            payload = JsonConvert.DeserializeObject<T>(fileContents);
-            return true;
-        }
-        catch (Exception e)
-        {
-            Debug.LogError(e);
-            return false;
-        }
-        finally
-        {
-            if (reader != null)
-                reader.Close();
-        }
-    }
 }
diff --git a/Assets/Scripts/Loading/Stage.cs b/Assets/Scripts/Loading/Stage.cs
index 4da3ed348565e1148a3080cfd34c0d420a35d483..04f8469c4b6ad3b0702b5e2546c742f1d923fe55 100644
--- a/Assets/Scripts/Loading/Stage.cs
+++ b/Assets/Scripts/Loading/Stage.cs
@@ -5,17 +5,18 @@
 using UnityEngine;
 using Newtonsoft.Json;
 using static CommunicationEvents;
-using System.Reflection;
 
-//using static IJSONsavable<Stage>;
 
 public class Stage: IJSONsavable<Stage>
 {
+    #region metadata
 
     /// <summary> Which category this <see cref="Stage"/> should be displayed in. </summary>
     public string category = null;
+
     /// <summary> Where to display this <see cref="Stage"/> within a <see cref="category"/> relative to others. </summary>
     public int number = -1;
+
     /// <summary>
     /// The name this <see cref="Stage"/> will be displayed with.
     /// Also defines names of save files of <see cref="player_record_list">stage progress</see>, <see cref="solution"/>
@@ -32,15 +33,37 @@ public class Stage: IJSONsavable<Stage>
     /// <summary> Wether this <see cref="Stage"/> is located in installation folder or user data (a.k.a. !local).</summary>
     public bool use_install_folder = false;
 
-    [JsonIgnore, JSONManager.JsonSeparate]
+    #endregion metadata
+
+    /// <summary>
+    /// Defining when this <see cref="Stage.player_record"/> is considered as solved.
+    /// <seealso cref="FactOrganizer.DynamiclySolved(SolutionOrganizer, out List{List{string}}, out List{List{string}})"/>
+    /// </summary>
+    [JSONsavable.JsonAutoPreProcess, JSONsavable.JsonAutoPostProcess]
+    public SolutionOrganizer solution = null;
+
+    [JsonIgnore, JSONsavable.JsonSeparate]
     public SaveGame savegame = null;
 
+    //public List<PlayerRecord> solution_approches = new();
+
+    #region makros/shortcuts
+
     /// <summary>
     /// <see langword="true"/> iff there is at least one element in <see cref="player_record_list"/> where <see cref="PlayerRecord.solved"/> == <see langword="true"/>.
     /// </summary>
     [JsonIgnore]
     //TODO? update color if changed
-    public bool completed_once { get { return player_record_list != null && player_record_list.Values.Where(s => s.solved == true).Any(); } }
+    public bool completed_once { get { return player_record_list != null && player_record_list.Values.Any(s => s.solved == true); } }
+
+    /// <summary> Current Stage progress.</summary>
+    [JsonIgnore]
+    public PlayerRecord player_record { 
+        get => savegame?.player_record;
+        set => (savegame ??= new())
+            .player_record = value;
+    }
+
     /// <summary>
     /// A list containing all saved player progress. <br/>
     /// - Key: name of file
@@ -53,12 +76,6 @@ public Dictionary<string, PlayerRecord> player_record_list {
             .player_record_list = value;
     }
 
-    /// <summary>
-    /// Defining when this <see cref="Stage.player_record"/> is considered as solved.
-    /// <seealso cref="FactOrganizer.DynamiclySolved(SolutionOrganizer, out List{List{string}}, out List{List{string}})"/>
-    /// </summary>
-    public SolutionOrganizer solution = null;
-
     /// <summary>
     /// A wrapper returning (or setting) <see cref="player_record.factState"/>. <br/>
     /// When <see cref="player_record"/> == <see langword="null"/>:
@@ -71,13 +88,9 @@ public FactOrganizer factState {
         set => (player_record ??= new(record_name))
             .factState = value;
     }
-    /// <summary> Current Stage progress.</summary>
-    [JsonIgnore]
-    public PlayerRecord player_record { 
-        get => savegame?.player_record;
-        set => (savegame ??= new())
-            .player_record = value;
-    }
+
+    #endregion makros/shortcuts
+
     /// <summary> Returns a name for <see cref="player_record.name"/> which needs to be uniquified once put into <see cref="player_record_list"/> (e.g. by <see cref="push_record(double, bool) adding '_i'"/>).</summary>
     private string record_name { get { return name + "_save"; } }
 
@@ -122,15 +135,6 @@ public Stage(string category, int number, string name, string description, strin
     }
 
 
-    Stage IJSONsavable<Stage>._IJPostProcess(Stage payload)
-    {
-        payload.solution = IJSONsavable<SolutionOrganizer>.Instance._IJPostProcess(payload.solution ?? new());
-        // // savegame is saved separately
-        // payload.savegame = IJSONsavable<SaveGame>.Instance._IJPostProcess(payload.savegame ?? new());
-
-        return payload;
-    }
-
     /// <summary>
     /// Sets members which are primitives.
     /// </summary>
@@ -344,7 +348,7 @@ public static bool ShallowLoad(out Stage set, string path)
         if (!IJSONsavable<Stage>.Instance._IJGetRawObject(out set, path))
             return false;
 
-        set = IJSONsavable<Stage>.Instance._IJPostProcess(set);
+        set = IJSONsavable<Stage>.postprocess(set);
         set.path = path;
         IJSONsavable<Stage>.load_children(null /*hierarchie*/, set.name, ref set, post_process: false);
 
@@ -446,8 +450,10 @@ public class SaveGame : IJSONsavable<SaveGame>
     public string name { get; set; }
     public string path { get; set; }
 
+    [JSONsavable.JsonAutoPreProcess, JSONsavable.JsonAutoPostProcess]
     public PlayerRecord player_record = new();
-    public Dictionary<string, PlayerRecord> player_record_list = new();
+
+    public Dictionary<string, PlayerRecord> player_record_list = new(); //entries are "PostProcess"ed when accessed/Cloned
 
     static SaveGame() 
     {
@@ -456,16 +462,6 @@ static SaveGame()
     public SaveGame() { }
 
     string IJSONsavable<SaveGame>._IJGetName(string name) => name + "_save";
-    SaveGame IJSONsavable<SaveGame>._IJPostProcess(SaveGame payload)
-    {
-        payload.player_record = IJSONsavable<PlayerRecord>.Instance._IJPostProcess(payload.player_record ?? new());
-
-        // // player_record_list entries are "PostProcess"ed when accessed/Cloned
-        //payload.player_record_list = new (payload.player_record_list.Select((v) => new KeyValuePair<string, PlayerRecord>
-        //    (v.Key, IJSONsavable<PlayerRecord>.Instance._IJPostProcess(v.Value))));
-
-        return payload;
-    }
 }
 
 
@@ -482,11 +478,11 @@ public class PlayerRecord: IJSONsavable<PlayerRecord>
     public double seconds = 0;
 
     /// <summary> Stage progress. </summary>
+    [JSONsavable.JsonAutoPreProcess, JSONsavable.JsonAutoPostProcess]
     public FactOrganizer factState = new();
     /// <summary> save game file name </summary>
     public string name { get; set; } = null;
-
-    public string path { get; set; }
+    public string path { get; set; } = null;
 
     static PlayerRecord(/*static!*/)
     {
@@ -507,12 +503,6 @@ public PlayerRecord(string name) {
         factState = new FactOrganizer();
     }
 
-    PlayerRecord IJSONsavable<PlayerRecord>._IJPostProcess(PlayerRecord payload)
-    {
-        payload.factState = IJSONsavable<FactOrganizer>.Instance._IJPostProcess(payload.factState);
-        return payload;
-    }
-
     /// <summary>
     /// Copies a specified <see cref="PlayerRecord"/>
     /// </summary>
@@ -529,4 +519,13 @@ public PlayerRecord Clone()
 
         return ret;
     }
+}
+
+
+public class SolutionApproach: IJSONsavable<SolutionApproach>
+{
+    public string name { get; set; } = null;
+    public string path { get; set; } = null;
+
+    //public PlayerRecord
 }
\ No newline at end of file
diff --git a/Assets/Scripts/StageStatic.cs b/Assets/Scripts/StageStatic.cs
index 1af549119cdc99b3c5dfe155c9aeddcbbc3b6937..17087d00893376758995d1a93c5a580ff0df35f9 100644
--- a/Assets/Scripts/StageStatic.cs
+++ b/Assets/Scripts/StageStatic.cs
@@ -218,7 +218,7 @@ public static StageErrorStruct Validate(string category, int id, string name, st
         return new StageErrorStruct(
             category.Length == 0,
             ContainsNumber(category, id, true),
-            name.Length == 0 || ContainsKey(name, true) || ContainsKey(name, false),
+            name.Length == 0 || ContainsKey(name, local),
             false,
             !Worlds.Contains(scene),
             local == false,
diff --git a/Assets/Scripts/UI/MainMenue/PageLoader/CreateLoader.cs b/Assets/Scripts/UI/MainMenue/PageLoader/CreateLoader.cs
index 3341ff9f1174316b4da28e9dd09f34404910a7ce..2eb0a497aa7bdfc6d8418ffa5b77836b4805f92b 100644
--- a/Assets/Scripts/UI/MainMenue/PageLoader/CreateLoader.cs
+++ b/Assets/Scripts/UI/MainMenue/PageLoader/CreateLoader.cs
@@ -84,7 +84,7 @@ protected void Error(StageStatic.StageErrorStruct error)
         {
             throw new System.NotImplementedException("Id must be unique within a category"); // technichal a lie
         }
-        if (error.name) // for savegame identification (could be chained with local/category/id)
+        if (error.name) // for savegame identification (could be chained with local/category/id => beware StageStatic.GetStage(name, local))
         {
             throw new System.NotImplementedException("Name must be unique");
         }
diff --git a/GlobalSuppressions2.cs b/GlobalSuppressions2.cs
new file mode 100644
index 0000000000000000000000000000000000000000..496b77e3fcf2119ba29c360ed0c3bf213256f33d
--- /dev/null
+++ b/GlobalSuppressions2.cs
@@ -0,0 +1,9 @@
+// This file is used by Code Analysis to maintain SuppressMessage
+// attributes that are applied to this project.
+// Project-level suppressions either have no target or are given
+// a specific target and scoped to a namespace, type, member, etc.
+
+using System.Diagnostics.CodeAnalysis;
+
+[assembly: SuppressMessage("Style", "IDE0034:Simplify 'default' expression", Justification = "Readabilty", Scope = "member", Target = "~M:IJSONsavable`1.load(System.Collections.Generic.List{CommunicationEvents.Directories},System.String,`0@,System.Boolean,System.Boolean,System.Boolean)~System.Boolean")]
+[assembly: SuppressMessage("Style", "IDE0034:Simplify 'default' expression", Justification = "<Pending>", Scope = "member", Target = "~M:JSONsavable.ReadFromJsonFile``1(``0@,System.String)~System.Boolean")]