/// <summary>
        /// saves discovery state to object automapGeometryDungeonState
        /// this class is mapping the hierarchy inside the GameObject gameObjectGeometry in such way
        /// that the MeshRenderer enabled state for objects in the 4th hierarchy level (which are the actual models)
        /// is matching value of field "discovered" in AutomapGeometryDungeonState.AutomapGeometryBlockState.AutomapGeometryBlockElementState.AutomapGeometryModelState
        /// dictionary of dungeon states of visited dungeons is then updated (which is used for save game mechanism)
        /// </summary>
        private void saveStateAutomapDungeon(bool forceSaveOfDungeonDiscoveryState)
        {
            if ((numberOfDungeonMemorized == 0)&&(!GameManager.Instance.IsPlayerInside)) // if discovery state of no dungeon has to be remembered, clear dictionary and skip the rest of this function
            {
                dictAutomapDungeonsDiscoveryState.Clear();
                return;
            }

            if ((!GameManager.Instance.IsPlayerInside)&&(!forceSaveOfDungeonDiscoveryState)) // if player is outside just skip this function
            {
                return;
            }

            int updatedNumberOfDungeonMemorized = numberOfDungeonMemorized;
            if ((numberOfDungeonMemorized == 0) && (GameManager.Instance.IsPlayerInside))
            {
                updatedNumberOfDungeonMemorized = 1;
            }

            Transform gameObjectGeometryDungeon = gameobjectGeometry.transform.GetChild(0);
            automapGeometryDungeonState = new AutomapGeometryDungeonState();
            automapGeometryDungeonState.locationName = gameObjectGeometryDungeon.name;
            automapGeometryDungeonState.entranceDiscovered = ((gameobjectBeaconEntrancePosition) && (gameobjectBeaconEntrancePosition.activeSelf)); // get entrance marker discovery state
            automapGeometryDungeonState.timeInSecondsLastVisited = DaggerfallUnity.Instance.WorldTime.DaggerfallDateTime.ToSeconds();
            automapGeometryDungeonState.blocks = new List<AutomapGeometryBlockState>();

            foreach (Transform currentBlock in gameObjectGeometryDungeon.transform)
            {
                AutomapGeometryBlockState automapGeometryBlockState = new AutomapGeometryBlockState();
                automapGeometryBlockState.blockName = currentBlock.name;

                List<AutomapGeometryBlockElementState> blockElements = new List<AutomapGeometryBlockElementState>();

                foreach (Transform currentTransformModel in currentBlock.transform)
                {
                    AutomapGeometryBlockElementState blockElement = new AutomapGeometryBlockElementState();

                    List<AutomapGeometryModelState> models = new List<AutomapGeometryModelState>();
                    foreach (Transform currentTransformMesh in currentTransformModel.transform)
                    {
                        AutomapGeometryModelState model = new AutomapGeometryModelState();
                        MeshRenderer meshRenderer = currentTransformMesh.GetComponent<MeshRenderer>();
                        if ((meshRenderer) && (meshRenderer.enabled))
                        {
                            model.discovered = true;
                            model.visitedInThisRun = !meshRenderer.materials[0].IsKeywordEnabled("RENDER_IN_GRAYSCALE"); // all materials of model have the same setting - so just material[0] is tested
                        }
                        else
                        {
                            model.discovered = false;
                            model.visitedInThisRun = false;
                        }
                        models.Add(model);
                    }

                    blockElement.models = models;
                    blockElements.Add(blockElement);
                }
                automapGeometryBlockState.blockElements = blockElements;
                automapGeometryDungeonState.blocks.Add(automapGeometryBlockState);
            }

            // replace or add discovery state for current dungeon
            DFLocation dfLocation = GameManager.Instance.PlayerGPS.CurrentLocation;
            string locationStringIdentifier = string.Format("{0}/{1}", dfLocation.RegionName, dfLocation.Name);
            if (dictAutomapDungeonsDiscoveryState.ContainsKey(locationStringIdentifier))
            {
                dictAutomapDungeonsDiscoveryState[locationStringIdentifier] = automapGeometryDungeonState;
            }
            else
            {
                dictAutomapDungeonsDiscoveryState.Add(locationStringIdentifier, automapGeometryDungeonState);
            }

            // make list out of dictionary and sort by last time visited - then get rid of dungeons in dictionary that weren't visited lately
            List<KeyValuePair<string, AutomapGeometryDungeonState>> sortedList = new List<KeyValuePair<string, AutomapGeometryDungeonState>>(dictAutomapDungeonsDiscoveryState);
            sortedList.Sort(
                delegate (KeyValuePair<string, AutomapGeometryDungeonState> firstPair,
                KeyValuePair<string, AutomapGeometryDungeonState> nextPair)
                {
                    return nextPair.Value.timeInSecondsLastVisited.CompareTo(firstPair.Value.timeInSecondsLastVisited);
                }
            );

            //if there are more dungeons remembered than allowed - get rid of dungeons in dictionary that weren't visited lately
            if (sortedList.Count > updatedNumberOfDungeonMemorized)
            {
                ulong timeInSecondsLimit = sortedList[updatedNumberOfDungeonMemorized - 1].Value.timeInSecondsLastVisited;

                foreach (KeyValuePair<string, AutomapGeometryDungeonState> entry in sortedList) // use sorted list and iterate over it - remove elements of dictionary
                {
                    if (entry.Value.timeInSecondsLastVisited < timeInSecondsLimit)
                    {
                        dictAutomapDungeonsDiscoveryState.Remove(entry.Key); // remove this dungeon's entry from dictionary
                    }
                }
            }
        }
        void updateMeshRendererDungeonState(ref MeshRenderer meshRenderer, AutomapGeometryDungeonState automapGeometryDungeonState, int indexBlock, int indexElement, int indexModel, bool forceNotVisitedInThisRun)
        {
            if (automapGeometryDungeonState.blocks[indexBlock].blockElements[indexElement].models[indexModel].discovered == true)
            {
                meshRenderer.enabled = true;

                if ((!forceNotVisitedInThisRun)&&(automapGeometryDungeonState.blocks[indexBlock].blockElements[indexElement].models[indexModel].visitedInThisRun))
                {
                    Material[] materials = meshRenderer.materials;
                    foreach (Material mat in meshRenderer.materials)
                    {
                        mat.DisableKeyword("RENDER_IN_GRAYSCALE");
                    }
                    meshRenderer.materials = materials;
                }
                else
                {
                    Material[] materials = meshRenderer.materials;
                    foreach (Material mat in meshRenderer.materials)
                    {
                        mat.EnableKeyword("RENDER_IN_GRAYSCALE");
                    }
                    meshRenderer.materials = materials;
                }
            }
            else
            {
                meshRenderer.enabled = false;
            }
        }
        /// <summary>
        /// restores discovery state from automapGeometryDungeonState onto gameobjectGeometry
        /// automapGeometryDungeonState is loaded from dictionary of dungeon states of visited dungeons
        /// this class is mapping the value of field "discovered" (AutomapGeometryDungeonState.AutomapGeometryBlockState.AutomapGeometryBlockElementState.AutomapGeometryModelState)
        /// inside object automapGeometryDungeonState to the objects inside the 4th hierarchy level of GameObject gameObjectGeometry (which are the actual models)
        /// in such way that the MeshRenderer enabled state for these objects match the value of field "discovered"
        /// </summary>
        /// <param name="forceNotVisitedInThisRun"> if set to true geometry is restored and its state is forced to not being visited in this run </param>
        private void restoreStateAutomapDungeon(bool forceNotVisitedInThisRun = false)
        {
            Transform location = gameobjectGeometry.transform.GetChild(0);

            HideAll(); // clear discovery state of geometry as initial starting point

            DFLocation dfLocation = GameManager.Instance.PlayerGPS.CurrentLocation;
            string locationStringIdentifier = string.Format("{0}/{1}", dfLocation.RegionName, dfLocation.Name);
            if (dictAutomapDungeonsDiscoveryState.ContainsKey(locationStringIdentifier))
            {
                automapGeometryDungeonState = dictAutomapDungeonsDiscoveryState[locationStringIdentifier];
            }
            else
            {
                automapGeometryDungeonState = null;
            }

            if (automapGeometryDungeonState == null)
                return;

            gameobjectBeaconEntrancePosition.SetActive(automapGeometryDungeonState.entranceDiscovered); // set entrance marker discovery state

            if (location.name != automapGeometryDungeonState.locationName)
                return;

            for (int indexBlock = 0; indexBlock < location.childCount; indexBlock++)
            {
                Transform currentBlock = location.GetChild(indexBlock);

                if (currentBlock.name != automapGeometryDungeonState.blocks[indexBlock].blockName)
                    return;

                for (int indexElement = 0; indexElement < currentBlock.childCount; indexElement++)
                {
                    Transform currentTransformElement = currentBlock.GetChild(indexElement);

                    for (int indexModel = 0; indexModel < currentTransformElement.childCount; indexModel++)
                    {
                        Transform currentTransformModel = currentTransformElement.GetChild(indexModel);

                        MeshRenderer meshRenderer = currentTransformModel.GetComponent<MeshRenderer>();
                        if (meshRenderer)
                        {
                            updateMeshRendererDungeonState(ref meshRenderer, automapGeometryDungeonState, indexBlock, indexElement, indexModel, forceNotVisitedInThisRun);
                        }
                    }
                }
            }
        }