コード例 #1
0
        public void Initialize()
        {
            if (_noteAssetLoader.SelectedNote != 0)
            {
                CustomNote activeNote = _noteAssetLoader.CustomNoteObjects[_noteAssetLoader.SelectedNote];

                if (activeNote.NoteBomb != null)
                {
                    MaterialSwapper.ReplaceMaterialsForGameObject(activeNote.NoteBomb);
                }

                if (_gameplayCoreSceneSetupData.gameplayModifiers.ghostNotes)
                {
                    _submission?.DisableScoreSubmission("Custom Notes", "Ghost Notes");
                }
                if (_gameplayCoreSceneSetupData.gameplayModifiers.disappearingArrows)
                {
                    _submission?.DisableScoreSubmission("Custom Notes", "Disappearing Arrows");
                }
                if (_gameplayCoreSceneSetupData.gameplayModifiers.smallCubes)
                {
                    _submission?.DisableScoreSubmission("Custom Notes", "Small Notes");
                }
                if (Utils.IsNoodleMap(_level))
                {
                    _submission?.DisableScoreSubmission("Custom Notes", "Noodle Extensions");
                }
            }
        }
コード例 #2
0
        public void OnGameSceneLoaded()
        {
            CustomNote activeNote = NoteAssetLoader.CustomNoteObjects[NoteAssetLoader.SelectedNote];

            if (activeNote.FileName != "DefaultNotes")
            {
                MaterialSwapper.GetMaterials();
                MaterialSwapper.ReplaceMaterialsForGameObject(activeNote.NoteLeft);
                MaterialSwapper.ReplaceMaterialsForGameObject(activeNote.NoteRight);

                if (activeNote.NoteDotLeft != null)
                {
                    MaterialSwapper.ReplaceMaterialsForGameObject(activeNote.NoteDotLeft);
                }

                if (activeNote.NoteDotRight != null)
                {
                    MaterialSwapper.ReplaceMaterialsForGameObject(activeNote.NoteDotRight);
                }

                if (activeNote.NoteBomb != null)
                {
                    MaterialSwapper.ReplaceMaterialsForGameObject(activeNote.NoteBomb);
                }

                CheckCustomNotesScoreDisable();
            }
            else if (ScoreUtility.ScoreIsBlocked)
            {
                ScoreUtility.EnableScoreSubmission("ModifiersEnabled");
            }
        }
コード例 #3
0
 public void Construct(NoteAssetLoader _noteAssetLoader, DiContainer Container, GameplayCoreSceneSetupData sceneSetupData)
 {
     CanSetup = !(sceneSetupData.gameplayModifiers.ghostNotes || sceneSetupData.gameplayModifiers.disappearingArrows) || !Container.HasBinding <MultiplayerLevelSceneSetupData>();
     if (_noteAssetLoader.SelectedNote != 0)
     {
         var note = _noteAssetLoader.CustomNoteObjects[_noteAssetLoader.SelectedNote];
         MaterialSwapper.GetMaterials();
         MaterialSwapper.ReplaceMaterialsForGameObject(note.NoteLeft);
         MaterialSwapper.ReplaceMaterialsForGameObject(note.NoteRight);
         MaterialSwapper.ReplaceMaterialsForGameObject(note.NoteDotLeft);
         MaterialSwapper.ReplaceMaterialsForGameObject(note.NoteDotRight);
         Utils.AddMaterialPropertyBlockController(note.NoteLeft);
         Utils.AddMaterialPropertyBlockController(note.NoteRight);
         Utils.AddMaterialPropertyBlockController(note.NoteDotLeft);
         Utils.AddMaterialPropertyBlockController(note.NoteDotRight);
         Container.BindMemoryPool <SiraPrefabContainer, SiraPrefabContainer.Pool>().WithId("cn.left.arrow").WithInitialSize(25).FromComponentInNewPrefab(NotePrefabContainer(note.NoteLeft));
         Container.BindMemoryPool <SiraPrefabContainer, SiraPrefabContainer.Pool>().WithId("cn.right.arrow").WithInitialSize(25).FromComponentInNewPrefab(NotePrefabContainer(note.NoteRight));
         if (note.NoteDotLeft != null)
         {
             Container.BindMemoryPool <SiraPrefabContainer, SiraPrefabContainer.Pool>().WithId("cn.left.dot").WithInitialSize(10).FromComponentInNewPrefab(NotePrefabContainer(note.NoteDotLeft));
         }
         if (note.NoteDotRight != null)
         {
             Container.BindMemoryPool <SiraPrefabContainer, SiraPrefabContainer.Pool>().WithId("cn.right.dot").WithInitialSize(10).FromComponentInNewPrefab(NotePrefabContainer(note.NoteDotRight));
         }
     }
 }
コード例 #4
0
 /// <summary>
 /// Start this instance.  If environment is Android, instantiate the location tracker, and send it
 /// an instance of the LocationUpdateListener, attached to the primary sphere object.
 /// </summary>
 void Start()
 {
             #if UNITY_ANDROID
     AndroidJavaObject javaObj  = new AndroidJavaObject("edu.temple.gamemanager.LocationTracker");
     MaterialSwapper   listener = new MaterialSwapper(GameObject.Find("Sphere"));
     javaObj.Call("SetLocationUpdateListener", listener);
             #else
     Debug.Log("LocationUpdateListener placeholder for Play Mode (not running on Android device)");
             #endif
 }
コード例 #5
0
 public void Construct(NoteAssetLoader _noteAssetLoader, DiContainer Container, GameplayCoreSceneSetupData sceneSetupData)
 {
     CanSetup = !(sceneSetupData.gameplayModifiers.ghostNotes || sceneSetupData.gameplayModifiers.disappearingArrows) || !Container.HasBinding <MultiplayerLevelSceneSetupData>();
     if (_noteAssetLoader.SelectedNote != 0 && CanSetup)
     {
         var note = _noteAssetLoader.CustomNoteObjects[_noteAssetLoader.SelectedNote];
         MaterialSwapper.GetMaterials();
         MaterialSwapper.ReplaceMaterialsForGameObject(note.NoteBomb);
         Container.BindMemoryPool <SiraPrefabContainer, SiraPrefabContainer.Pool>().WithId("cn.bomb").WithInitialSize(10).FromComponentInNewPrefab(NotePrefabContainer(note.NoteBomb));
     }
 }
コード例 #6
0
 /// <summary>
 /// This function is called when on Able to Move is invoked from XRPOIGazeMovement.
 /// Gets material swapper component from gazed target,
 /// and uses it to set highlight material on target.
 /// </summary>
 /// <param name="target"></param>
 private void CanMove(Transform target)
 {
     if (target != null)
     {
         matSwap = target.GetComponent <MaterialSwapper>();
         if (matSwap != null)
         {
             matSwap.SetHighLightMaterial();
         }
     }
 }
コード例 #7
0
        public override void InstallBindings()
        {
            bool autoDisable = _pluginConfig.AutoDisable && (_gameplayCoreSceneSetupData.gameplayModifiers.ghostNotes || _gameplayCoreSceneSetupData.gameplayModifiers.disappearingArrows || _gameplayCoreSceneSetupData.gameplayModifiers.smallCubes || Utils.IsNoodleMap(_gameplayCoreSceneSetupData.difficultyBeatmap));

            _shouldSetup = !autoDisable && (!(_gameplayCoreSceneSetupData.gameplayModifiers.ghostNotes || _gameplayCoreSceneSetupData.gameplayModifiers.disappearingArrows) || !Container.HasBinding <MultiplayerLevelSceneSetupData>());
            if (_pluginConfig.Enabled && _noteAssetLoader.SelectedNote != 0 && _shouldSetup)
            {
                Container.BindInterfacesAndSelfTo <GameCameraManager>().AsSingle();
                Container.Bind <IInitializable>().To <CustomNoteManager>().AsSingle();
                CustomNote note = _noteAssetLoader.CustomNoteObjects[_noteAssetLoader.SelectedNote];

                #region Note Setup

                Container.RegisterRedecorator(new BasicNoteRegistration(DecorateNote, DECORATION_PRIORITY));
                MaterialSwapper.GetMaterials();
                MaterialSwapper.ReplaceMaterialsForGameObject(note.NoteLeft);
                MaterialSwapper.ReplaceMaterialsForGameObject(note.NoteRight);
                MaterialSwapper.ReplaceMaterialsForGameObject(note.NoteDotLeft);
                MaterialSwapper.ReplaceMaterialsForGameObject(note.NoteDotRight);
                Utils.AddMaterialPropertyBlockController(note.NoteLeft);
                Utils.AddMaterialPropertyBlockController(note.NoteRight);
                Utils.AddMaterialPropertyBlockController(note.NoteDotLeft);
                Utils.AddMaterialPropertyBlockController(note.NoteDotRight);

                Container.BindMemoryPool <SiraPrefabContainer, SiraPrefabContainer.Pool>().WithId(Protocol.LeftArrowPool).WithInitialSize(25).FromComponentInNewPrefab(NotePrefabContainer(note.NoteLeft));
                Container.BindMemoryPool <SiraPrefabContainer, SiraPrefabContainer.Pool>().WithId(Protocol.RightArrowPool).WithInitialSize(25).FromComponentInNewPrefab(NotePrefabContainer(note.NoteRight));
                if (note.NoteDotLeft != null)
                {
                    Container.BindMemoryPool <SiraPrefabContainer, SiraPrefabContainer.Pool>().WithId(Protocol.LeftDotPool).WithInitialSize(10).FromComponentInNewPrefab(NotePrefabContainer(note.NoteDotLeft));
                }
                if (note.NoteDotRight != null)
                {
                    Container.BindMemoryPool <SiraPrefabContainer, SiraPrefabContainer.Pool>().WithId(Protocol.RightDotPool).WithInitialSize(10).FromComponentInNewPrefab(NotePrefabContainer(note.NoteDotRight));
                }

                #endregion

                #region Bomb Setup

                if (note.NoteBomb != null)
                {
                    MaterialSwapper.GetMaterials();
                    MaterialSwapper.ReplaceMaterialsForGameObject(note.NoteBomb);
                    Container.BindMemoryPool <SiraPrefabContainer, SiraPrefabContainer.Pool>().WithId(Protocol.BombPool).WithInitialSize(10).FromComponentInNewPrefab(NotePrefabContainer(note.NoteBomb));
                    Container.RegisterRedecorator(new BombNoteRegistration(DecorateBombs, DECORATION_PRIORITY));
                }

                #endregion
            }
        }
コード例 #8
0
    /// <summary>
    /// This function is called,
    /// when on Not Able to Move is invoked from XRPOIGazeMovement.
    /// if material swapper is not null,
    /// sets the normal material of that material swapper,
    /// and nulls the material swapper.
    /// if target is not null,
    /// sets material swapper to match target material swapper,
    /// and sets it material to normal material.
    /// </summary>
    /// <param name="target"></param>
    private void CantMove(Transform target)
    {
        if (matSwap != null)
        {
            matSwap.SetNormalMaterial();
            matSwap = null;
        }

        if (target != null)
        {
            matSwap = target.GetComponent <MaterialSwapper>();
            if (matSwap != null)
            {
                matSwap.SetNormalMaterial();
            }
        }
    }
コード例 #9
0
    public void AddComponentsToSceneObject(AreaSceneObjectData jsonData, GameObject so)
    {
        if (jsonData.soData.Count > 0)
        {
            SceneObjectDataGeneric data            = jsonData.soData [0];
            SceneObjectData        sceneObjectData = so.GetComponent <SceneObjectData> ();
            Bumper bumper = so.GetComponent <Bumper> ();

            if (data.bumperForce > 0)
            {
                if (bumper == null)
                {
                    bumper = so.gameObject.AddComponent <Bumper> ();
                }
                bumper.force = data.bumperForce;
            }
            if (data.size != Vector3.zero)
            {
                if (sceneObjectData == null)
                {
                    sceneObjectData = so.gameObject.AddComponent <SceneObjectData> ();
                }
                sceneObjectData.size = data.size;
            }
            if (data.random_pos_x != 0)
            {
                if (sceneObjectData == null)
                {
                    sceneObjectData = so.gameObject.AddComponent <SceneObjectData> ();
                }

                sceneObjectData.random_pos_x = data.random_pos_x;
            }
            if (data.minPayers != 0)
            {
                if (sceneObjectData == null)
                {
                    sceneObjectData = so.gameObject.AddComponent <SceneObjectData> ();
                }

                sceneObjectData.minPayers = data.minPayers;
            }
        }

        MaterialSwapper mat = so.GetComponent <MaterialSwapper>();

        if (mat != null && jsonData.mat.Count > 0)
        {
            MaterialChanger data = jsonData.mat[0];
            //if (mat == null)
            //    mat = so.gameObject.AddComponent<MaterialSwapper>();
            mat.Init(data.name);
        }

        FullRotation fullRotation = so.GetComponent <FullRotation> ();

        if (jsonData.fullRotationData.Count > 0)
        {
            FullRotationData data = jsonData.fullRotationData [0];

            if (fullRotation == null)
            {
                fullRotation = so.gameObject.AddComponent <FullRotation> ();
            }
            fullRotation.rotateX        = data.rotateX;
            fullRotation.rotateY        = data.rotateY;
            fullRotation.rotateZ        = data.rotateZ;
            fullRotation.speed          = data.speed;
            fullRotation.randomRotation = data.random;
        }
        else if (fullRotation != null)
        {
            fullRotation.OnComponentDisposed();
        }

        TimelineAnimation timelineAnimation = so.GetComponent <TimelineAnimation> ();

        if (jsonData.timelineAnimation.Count > 0)
        {
            TimelineAnimationData data = jsonData.timelineAnimation [0];
            if (timelineAnimation == null)
            {
                timelineAnimation = so.gameObject.AddComponent <TimelineAnimation> ();
            }
            timelineAnimation.timeLineData = data.timeLineData;
        }
        else if (timelineAnimation != null)
        {
            timelineAnimation.OnComponentDisposed();
        }

        if (jsonData.bossSettings.Count > 0)
        {
            BossSettingsData data         = jsonData.bossSettings [0];
            BossSettings     newcomponent = so.GetComponent <BossSettings> ();

            if (newcomponent == null)
            {
                newcomponent = so.gameObject.AddComponent <BossSettings> ();
            }

            newcomponent.bossModule           = data.bossModule;
            newcomponent.time_to_init_enemies = data.time_to_init_enemies;
            newcomponent.asset                 = data.asset;
            newcomponent.time_to_kill          = data.time_to_kill;
            newcomponent.distance_from_avatars = data.distance_from_avatars;
            newcomponent.bundle                = data.bundle;
        }
        if (jsonData.moveForward.Count > 0)
        {
            MoveForwardData data         = jsonData.moveForward [0];
            MoveForward     newcomponent = so.GetComponent <MoveForward> ();
            if (newcomponent == null)
            {
                newcomponent = so.gameObject.AddComponent <MoveForward> ();
            }
            newcomponent.speed           = data.speed;
            newcomponent.randomSpeedDiff = data.randomSpeedDiff;
            newcomponent.moveBackIn      = data.moveBackIn;
        }
    }
コード例 #10
0
    public void AddComponentsToJson(AreaSceneObjectData newSOdata, GameObject go)
    {
        FullRotation      fullRotation      = go.GetComponent <FullRotation> ();
        MaterialSwapper   mat               = go.GetComponent <MaterialSwapper>();
        TimelineAnimation timelineAnimation = go.GetComponent <TimelineAnimation> ();
        BossSettings      bossSettings      = go.GetComponent <BossSettings> ();
        MoveForward       moveForward       = go.GetComponent <MoveForward> ();
        Bumper            bumper            = go.GetComponent <Bumper> ();
        SceneObjectData   soData            = go.GetComponent <SceneObjectData> ();

        if (newSOdata.name == "wallSmall")
        {
            newSOdata.mat = new List <MaterialChanger>();
            MaterialChanger mc = new MaterialChanger();
            mc.name = go.GetComponent <MeshRenderer>().sharedMaterial.ToString().Split(" "[0])[0];
            newSOdata.mat.Add(mc);
        }

        if (soData != null)
        {
            newSOdata.soData = new List <SceneObjectDataGeneric> ();
            SceneObjectDataGeneric data = new SceneObjectDataGeneric();
            data.size         = soData.size;
            data.random_pos_x = soData.random_pos_x;
            data.minPayers    = soData.minPayers;
            data.minPayers    = soData.minPayers;
            newSOdata.soData.Add(data);
        }
        if (bumper != null)
        {
            newSOdata.soData = new List <SceneObjectDataGeneric> ();
            SceneObjectDataGeneric data = new SceneObjectDataGeneric();
            data.bumperForce = bumper.force;
            newSOdata.soData.Add(data);
        }
        if (fullRotation != null)
        {
            newSOdata.fullRotationData = new List <FullRotationData> ();
            FullRotationData data = new FullRotationData();
            data.rotateX = fullRotation.rotateX;
            data.rotateY = fullRotation.rotateY;
            data.rotateZ = fullRotation.rotateZ;
            data.speed   = fullRotation.speed;
            data.random  = fullRotation.randomRotation;
            newSOdata.fullRotationData.Add(data);
        }
        if (timelineAnimation != null)
        {
            newSOdata.timelineAnimation = new List <TimelineAnimationData> ();
            TimelineAnimationData data = new TimelineAnimationData();
            data.timeLineData = timelineAnimation.timeLineData;
            newSOdata.timelineAnimation.Add(data);
        }
        if (bossSettings != null)
        {
            newSOdata.bossSettings = new List <BossSettingsData> ();
            BossSettingsData data = new BossSettingsData();
            data.bossModule            = bossSettings.bossModule;
            data.asset                 = bossSettings.asset;
            data.time_to_init_enemies  = bossSettings.time_to_init_enemies;
            data.time_to_kill          = bossSettings.time_to_kill;
            data.distance_from_avatars = bossSettings.distance_from_avatars;
            data.bundle                = bossSettings.bundle;
            newSOdata.bossSettings.Add(data);
        }
        if (moveForward != null)
        {
            newSOdata.moveForward = new List <MoveForwardData> ();
            MoveForwardData data = new MoveForwardData();
            data.speed           = moveForward.speed;
            data.randomSpeedDiff = moveForward.randomSpeedDiff;
            data.moveBackIn      = moveForward.moveBackIn;
            newSOdata.moveForward.Add(data);
        }
    }
コード例 #11
0
        private void RecursivelyAddSubObject(TankBlock block, ModContents mod, Transform targetTransform, JObject jData, Material defaultMaterial, bool isNewSubObject)
        {
            Debug.Log("[Nuterra] Called RecursivelyAddSubObject");

            // Material - Used in the next step
            Material mat = null;

            bool hasMaterial     = TryGetStringMultipleKeys(jData, out string materialName, "MaterialName", "MeshMaterialName");
            bool hasAlbedo       = TryGetStringMultipleKeys(jData, out string albedoName, "MeshTextureName", "TextureName");
            bool hasGloss        = TryGetStringMultipleKeys(jData, out string glossName, "MetallicTextureName", "GlossTextureName", "MeshGlossTextureName");
            bool hasEmissive     = TryGetStringMultipleKeys(jData, out string emissiveName, "EmissionTextureName", "MeshEmissionTextureName");
            bool hasAnyOverrides = hasAlbedo || hasGloss || hasEmissive;

            // Calculate a unique string that defines this material
            string compoundMaterialName = "";

            if (hasMaterial)
            {
                compoundMaterialName = $"{compoundMaterialName}M:{materialName};";
            }
            if (hasAlbedo)
            {
                compoundMaterialName = $"{compoundMaterialName}A:{albedoName};";
            }
            if (hasGloss)
            {
                compoundMaterialName = $"{compoundMaterialName}G:{glossName};";
            }
            if (hasEmissive)
            {
                compoundMaterialName = $"{compoundMaterialName}E:{emissiveName};";
            }

            if (hasAnyOverrides)
            {
                if (sMaterialCache.TryGetValue(compoundMaterialName, out Material existingMat))
                {
                    mat = existingMat;
                }
                else
                {
                    // Default to missing texture, then see if we have a base texture reference
                    mat = defaultMaterial;
                    if (hasMaterial)
                    {
                        string matName = materialName.Replace("Venture_", "VEN_").Replace("GeoCorp_", "GC_");
                        mat = TTReferences.FindMaterial(matName);
                    }

                    Texture2D albedo   = hasAlbedo ? TTReferences.Find <Texture2D>(albedoName, mod) : null;
                    Texture2D gloss    = hasGloss ? TTReferences.Find <Texture2D>(glossName, mod) : null;
                    Texture2D emissive = hasEmissive ? TTReferences.Find <Texture2D>(emissiveName, mod) : null;
                    mat = Util.CreateMaterial(mat, true, albedo, gloss, emissive);

                    // Cache our newly created material in case it comes up again
                    sMaterialCache.Add(compoundMaterialName, mat);
                }
            }
            else
            {
                // Actually, if we make no references, we can keep official modded block behaviour
                mat = defaultMaterial;
            }

            // Physics Material - Used in the next step
            PhysicMaterial physMat = new PhysicMaterial();

            if (jData.TryGetValue("Friction", out JToken jFriction))
            {
                physMat.dynamicFriction = jFriction.ToObject <float>();
            }
            if (jData.TryGetValue("StaticFriction", out JToken jStaticFriction))
            {
                physMat.staticFriction = jStaticFriction.ToObject <float>();
            }
            if (jData.TryGetValue("Bounciness", out JToken jBounce))
            {
                physMat.bounciness = jBounce.ToObject <float>();
            }
            // MeshName & ColliderMeshName Override
            Mesh mesh         = null;
            Mesh colliderMesh = null;
            bool supressBoxColliderFallback = TryParse(jData, "SupressBoxColliderFallback", TryParse(jData, "NoBoxCollider", false));

            if (TryGetStringMultipleKeys(jData, out string meshName, "MeshName", "ModelName"))
            {
                foreach (UnityEngine.Object obj in mod.FindAllAssets(meshName))
                {
                    if (obj != null)
                    {
                        if (obj is Mesh)
                        {
                            mesh = (Mesh)obj;
                        }
                        else if (obj is GameObject)
                        {
                            mesh = ((GameObject)obj).GetComponentInChildren <MeshFilter>().sharedMesh;
                        }
                    }
                }
                Debug.Assert(mesh != null, $"[Nuterra] Failed to find mesh with name {meshName}");
            }
            if (TryGetStringMultipleKeys(jData, out string meshColliderName, "ColliderMeshName", "MeshColliderName"))
            {
                foreach (UnityEngine.Object obj in mod.FindAllAssets(meshColliderName))
                {
                    if (obj is Mesh)
                    {
                        colliderMesh = (Mesh)obj;
                    }
                    else if (obj is GameObject)
                    {
                        colliderMesh = ((GameObject)obj).GetComponentInChildren <MeshFilter>().sharedMesh;
                    }
                }
                Debug.Assert(colliderMesh != null, $"[Nuterra] Failed to find collider mesh with name {meshColliderName}");
            }

            // This is the only bit where the root object majorly differs from subobjects
            if (targetTransform == block.transform)
            {
                // Generally speaking, the root of a TT block does not have meshes, so needs to add a child
                // Add mesh / collider mesh sub GameObject if we have either
                if (mesh != null || colliderMesh != null)
                {
                    GameObject meshObj = CreateMeshGameObject(targetTransform, mesh, mat, colliderMesh, physMat, supressBoxColliderFallback);
                }
            }
            else             // However, if we are poking around in a subobject, we may want to swap out existing renderers
            {
                // If we provided a new mesh, do a full swap
                if (mesh != null)
                {
                    targetTransform.gameObject.EnsureComponent <MeshFilter>().sharedMesh       = mesh;
                    targetTransform.gameObject.EnsureComponent <MeshRenderer>().sharedMaterial = mat;
                }
                else                 // If we don't want to swap out the mesh we may still want to swap out the properties of existing renderers
                {
                    bool forceEmissive = TryParse(jData, "ForceEmission", false);
                    foreach (Renderer renderer in targetTransform.GetComponents <Renderer>())
                    {
                        renderer.sharedMaterial = mat;
                        if (renderer is ParticleSystemRenderer psrenderer)
                        {
                            psrenderer.trailMaterial = mat;
                        }

                        if (forceEmissive)
                        {
                            MaterialSwapper.SetMaterialPropertiesOnRenderer(renderer, ManTechMaterialSwap.MaterialColour.Normal, 1f, 0);
                        }
                    }
                }

                // If we provided a collider mesh, do a full swap
                if (colliderMesh != null)
                {
                    MeshCollider mc = targetTransform.gameObject.EnsureComponent <MeshCollider>();
                    mc.convex         = true;
                    mc.sharedMesh     = colliderMesh;
                    mc.sharedMaterial = physMat;
                }
                // If we want a box collider, try to make one from our mesh
                bool makeBoxCollider = GetBoolMultipleKeys(jData, false, "MakeBoxCollider", "GenerateBoxCollider");
                if (makeBoxCollider)
                {
                    BoxCollider bc = targetTransform.gameObject.EnsureComponent <BoxCollider>();
                    bc.sharedMaterial = physMat;
                    if (mesh != null)
                    {
                        mesh.RecalculateBounds();
                        bc.size   = mesh.bounds.size - Vector3.one * 0.2f;
                        bc.center = mesh.bounds.center;
                    }
                    else
                    {
                        bc.size   = Vector3.one;
                        bc.center = Vector3.zero;
                    }
                }
                // Weird option from TTQMM that has a fixed size sphere
                bool makeSphereCollider = TryParse(jData, "MakeSphereCollider", false);
                if (makeSphereCollider)
                {
                    SphereCollider sc = targetTransform.gameObject.EnsureComponent <SphereCollider>();
                    sc.radius         = 0.5f;
                    sc.center         = Vector3.zero;
                    sc.sharedMaterial = physMat;
                }
            }

            // ------------------------------------------------------
            #region Deserializers
            if (TryGetTokenMultipleKeys(jData, out JToken jDeserialObj, "Deserializer", "JSONBLOCK") && jDeserialObj.Type == JTokenType.Object)
            {
                JObject jDeserializer = (JObject)jDeserialObj;

                // TTQMM Ref: GameObjectJSON.CreateGameObject(jBlock.Deserializer, blockbuilder.Prefab);
                NuterraDeserializer.DeserializeIntoGameObject(jDeserializer, block.gameObject);
            }
            #endregion
            // ------------------------------------------------------

            // ------------------------------------------------------
            #region Sub objects
            if (jData.TryGetValue("SubObjects", out JToken jSubObjectList) && jSubObjectList.Type == JTokenType.Array)
            {
                foreach (JToken token in (JArray)jSubObjectList)
                {
                    if (token.Type == JTokenType.Object)
                    {
                        JObject jSubObject = (JObject)token;
                        if (TryGetStringMultipleKeys(jSubObject, out string subObjName, "SubOverrideName", "OverrideName", "ObjectName"))
                        {
                            GameObject subObject   = (targetTransform.RecursiveFindWithProperties(subObjName) as Component)?.gameObject;
                            bool       creatingNew = subObject == null;
                            if (subObject != null)
                            {
                                if (TryGetTokenMultipleKeys(jSubObject, out JToken jLayer, "Layer", "PhysicsLayer") && jLayer.Type == JTokenType.Integer)
                                {
                                    subObject.layer = jLayer.ToObject <int>();
                                }
                            }
                            else                             // Reference was not matched, so we want to add a new subobject
                            {
                                if (subObjName.NullOrEmpty())
                                {
                                    subObjName = $"SubObject_{targetTransform.childCount + 1}";
                                }

                                subObject = new GameObject(subObjName);
                                subObject.transform.parent        = targetTransform;
                                subObject.transform.localPosition = Vector3.zero;
                                subObject.transform.localRotation = Quaternion.identity;

                                if (TryGetTokenMultipleKeys(jSubObject, out JToken jLayer, "Layer", "PhysicsLayer") && jLayer.Type == JTokenType.Integer)
                                {
                                    subObject.layer = jLayer.ToObject <int>();
                                }
                                else
                                {
                                    subObject.layer = 8;                                     // Globals.inst.layerTank;
                                }
                            }

                            // Target acquired, lets tweak a few things
                            bool destroyColliders = GetBoolMultipleKeys(jSubObject, false, "DestroyExistingColliders", "DestroyColliders");
                            if (destroyColliders)
                            {
                                foreach (Collider col in subObject.GetComponents <Collider>())
                                {
                                    UnityEngine.Object.DestroyImmediate(col);
                                }

                                UnityEngine.Object.DestroyImmediate(subObject.GetComponentInParents <ColliderSwapper>());
                            }

                            bool destroyRenderers = GetBoolMultipleKeys(jSubObject, false, "DestroyExistingRenderer", "DestroyExistingRenderers", "DestroyRenderers");
                            if (destroyRenderers)
                            {
                                foreach (Renderer renderer in subObject.GetComponents <Renderer>())
                                {
                                    UnityEngine.Object.DestroyImmediate(renderer);
                                }
                                foreach (MeshFilter mf in subObject.GetComponents <MeshFilter>())
                                {
                                    UnityEngine.Object.DestroyImmediate(mf);
                                }
                            }

                            // If there is already a material set on this sub object ref, use it
                            Material matForSubObject = mat;
                            if (!creatingNew && !destroyRenderers)
                            {
                                Renderer ren = subObject.GetComponent <Renderer>();
                                if (ren)
                                {
                                    matForSubObject = ren.sharedMaterial;
                                }
                            }

                            // Optional resize settings
                            if (TryGetTokenMultipleKeys(jSubObject, out JToken jPos, "SubPosition", "Position") && jPos.Type == JTokenType.Object)
                            {
                                subObject.transform.localPosition = GetVector3(jPos);
                            }
                            if (TryGetTokenMultipleKeys(jSubObject, out JToken jEuler, "SubRotation", "Rotation") && jEuler.Type == JTokenType.Object)
                            {
                                subObject.transform.localEulerAngles = GetVector3(jEuler);
                            }
                            if (TryGetTokenMultipleKeys(jSubObject, out JToken jScale, "SubScale", "Scale") && jScale.Type == JTokenType.Object)
                            {
                                subObject.transform.localScale = GetVector3(jScale);
                            }

                            RecursivelyAddSubObject(block, mod, subObject.transform, jSubObject, matForSubObject, creatingNew);
                        }
                        else
                        {
                            Debug.LogError($"[Nuterra] Failed to find SubOverrideName tag in sub object JSON");
                        }
                    }