Пример #1
0
        /// <summary>
        /// Checks if player in same world cell as Place this Person was assigned to.
        /// Does not care about specific building/dungeon or interior/exterior, just matching location mapID.
        /// Does not care if player actually inside bounds, just if inside same world cell.
        /// </summary>
        /// <returns>True if player in same world cell as location.</returns>
        public bool IsPlayerInSameLocationWorldCell()
        {
            // Get Place resource
            Place place = ParentQuest.GetPlace(lastAssignedPlaceSymbol);

            if (place == null)
            {
                return(false);
            }

            // Compare mapID of player location and Place
            DFLocation location = GameManager.Instance.PlayerGPS.CurrentLocation;

            if (location.Loaded)
            {
                if (location.MapTableData.MapId == place.SiteDetails.mapId)
                {
                    return(true);
                }
            }

            return(false);
        }
Пример #2
0
        private void LayoutDungeon(ref DFLocation location)
        {
            //// Start timing
            //Stopwatch stopwatch = Stopwatch.StartNew();
            //long startTime = stopwatch.ElapsedMilliseconds;

            // Create dungeon layout
            foreach (var block in location.Dungeon.Blocks)
            {
                GameObject go = RDBLayout.CreateGameObject(block.BlockName, block.IsStartingBlock, DungeonTextureTable, Summary.DungeonType, Summary.ID);
                go.transform.parent   = this.transform;
                go.transform.position = new Vector3(block.X * RDBLayout.RDBSide, 0, block.Z * RDBLayout.RDBSide);

                DaggerfallRDBBlock daggerfallBlock = go.GetComponent <DaggerfallRDBBlock>();
                if (block.IsStartingBlock)
                {
                    FindStartMarker(daggerfallBlock);
                }
            }

            //// Show timer
            //long totalTime = stopwatch.ElapsedMilliseconds - startTime;
            //DaggerfallUnity.LogMessage(string.Format("Time to layout dungeon: {0}ms", totalTime), true);
        }
Пример #3
0
        private void LayoutDungeon(ref DFLocation location)
        {
#if SHOW_LAYOUT_TIMES
            // Start timing
            System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew();
            long startTime = stopwatch.ElapsedMilliseconds;
#endif

            // Create dungeon layout
            foreach (var block in location.Dungeon.Blocks)
            {
                GameObject go = GameObjectHelper.CreateRDBBlockGameObject(
                    block.BlockName,
                    DungeonTextureTable,
                    block.IsStartingBlock,
                    Summary.DungeonType,
                    RandomMonsterPower,
                    RandomMonsterVariance,
                    (int)DateTime.Now.Ticks /*Summary.ID*/,      // TODO: Add more options for seed
                    dfUnity.Option_DungeonBlockPrefab);
                go.transform.parent   = this.transform;
                go.transform.position = new Vector3(block.X * RDBLayout.RDBSide, 0, block.Z * RDBLayout.RDBSide);

                DaggerfallRDBBlock daggerfallBlock = go.GetComponent <DaggerfallRDBBlock>();
                if (block.IsStartingBlock)
                {
                    FindMarkers(daggerfallBlock);
                }
            }

#if SHOW_LAYOUT_TIMES
            // Show timer
            long totalTime = stopwatch.ElapsedMilliseconds - startTime;
            DaggerfallUnity.LogMessage(string.Format("Time to layout dungeon: {0}ms", totalTime), true);
#endif
        }
        public static TransactionResult PurchaseHouse(BuildingSummary house, int regionIndex)
        {
            if (house.buildingKey < 1)
            {
                return(TransactionResult.NONE);
            }

            int          amount       = GetHousePrice(house);
            PlayerEntity playerEntity = GameManager.Instance.PlayerEntity;
            var          playerGold   = playerEntity.GetGoldAmount();
            var          accountGold  = BankAccounts[regionIndex].accountGold;

            if (amount > playerGold + accountGold)
            {
                return(TransactionResult.NOT_ENOUGH_GOLD);
            }

            amount = playerEntity.DeductGoldAmount(amount);
            bankAccounts[regionIndex].accountGold -= amount;

            // Set player owned house for this region
            DFLocation location = GameManager.Instance.PlayerGPS.CurrentLocation;
            int        mapID    = location.MapTableData.MapId;

            houses[regionIndex].location    = location.Name;
            houses[regionIndex].mapID       = mapID;
            houses[regionIndex].buildingKey = house.buildingKey;

            // Ensure building is discovered
            GameManager.Instance.PlayerGPS.DiscoverBuilding(house.buildingKey);

            // Add interior scene to permanent list
            SaveLoadManager.StateManager.AddPermanentScene(DaggerfallInterior.GetSceneName(mapID, house.buildingKey));

            return(TransactionResult.PURCHASED_HOUSE);
        }
        // Note will load ALL of the new location files in the region that have the variant
        public static bool LoadNewDFLocationVariant(int regionIndex, int locationIndex, string variant)
        {
            int        locationKey = MakeLocationKey(regionIndex, locationIndex);
            DFLocation dfLocation;

            if (locations.TryGetValue(locationKey.ToString(), out dfLocation))
            {
                string locationVariantKey = locationKey.ToString() + variant;
                if (!locations.ContainsKey(locationVariantKey))
                {
                    // Seek from loose files
                    string   locationPattern = string.Format("locationnew-*-{0}{1}.json", regionIndex, variant);
                    string[] fileNames       = Directory.GetFiles(worldDataPath, locationPattern);
                    foreach (string fileName in fileNames)
                    {
                        string     locationReplacementJson = File.ReadAllText(Path.Combine(worldDataPath, fileName));
                        DFLocation variantLocation         = (DFLocation)SaveLoadManager.Deserialize(typeof(DFLocation), locationReplacementJson);
                        AddNewDFLocationVariant(locationIndex, locationVariantKey, ref variantLocation);
                        return(true);
                    }
                    // Seek from mods
                    string           locationExtension = string.Format("-{0}{1}.json", regionIndex, variant);
                    List <TextAsset> assets            = ModManager.Instance.FindAssets <TextAsset>(worldData, locationExtension);
                    if (assets != null)
                    {
                        foreach (TextAsset locationReplacementJsonAsset in assets)
                        {
                            DFLocation variantLocation = (DFLocation)SaveLoadManager.Deserialize(typeof(DFLocation), locationReplacementJsonAsset.text);
                            AddNewDFLocationVariant(locationIndex, locationVariantKey, ref variantLocation);
                            return(true);
                        }
                    }
                }
            }
            return(false);
        }
        /// <summary>
        /// Gets a DFLocation representation of a location.
        /// </summary>
        /// <param name="region">Index of region.</param>
        /// <param name="location">Index of location.</param>
        /// <returns>DFLocation.</returns>
        public DFLocation GetLocation(int region, int location)
        {
            // Load the region
            if (!LoadRegion(region))
                return new DFLocation();

            // Read location
            DFLocation dfLocation = new DFLocation();
            if (!ReadLocation(region, location, ref dfLocation))
                return new DFLocation();

            // Store indices
            dfLocation.RegionIndex = region;
            dfLocation.LocationIndex = location;

            return dfLocation;
        }
        /// <summary>
        /// initializes resources (mapDistanceSquaredFromWater, mapDistanceSquaredFromLocations, mapMultipliers) and smoothes small height map
        /// </summary>
        public static void InitImprovedWorldTerrain(ContentReader contentReader)
        {
            if (!init)
            {
                #if CREATE_PERSISTENT_LOCATION_RANGE_MAPS
                {
                    int width  = WoodsFile.mapWidthValue;
                    int height = WoodsFile.mapHeightValue;

                    mapLocationRangeX = new byte[width * height];
                    mapLocationRangeY = new byte[width * height];

                    //int y = 204;
                    //int x = 718;
                    for (int y = 0; y < height; y++)
                    {
                        for (int x = 0; x < width; x++)
                        {
                            //MapPixelData MapData = TerrainHelper.GetMapPixelData(contentReader, x, y);
                            //if (MapData.hasLocation)
                            //{
                            //    int locationRangeX = (int)MapData.locationRect.xMax - (int)MapData.locationRect.xMin;
                            //    int locationRangeY = (int)MapData.locationRect.yMax - (int)MapData.locationRect.yMin;
                            //}

                            ContentReader.MapSummary mapSummary;
                            int  regionIndex = -1, mapIndex = -1;
                            bool hasLocation = contentReader.HasLocation(x, y, out mapSummary);
                            if (hasLocation)
                            {
                                regionIndex = mapSummary.RegionIndex;
                                mapIndex    = mapSummary.MapIndex;
                                DFLocation location       = contentReader.MapFileReader.GetLocation(regionIndex, mapIndex);
                                byte       locationRangeX = location.Exterior.ExteriorData.Width;
                                byte       locationRangeY = location.Exterior.ExteriorData.Height;

                                mapLocationRangeX[y * width + x] = locationRangeX;
                                mapLocationRangeY[y * width + x] = locationRangeY;
                            }
                        }
                    }

                    // save to files
                    FileStream ostream;
                    ostream = new FileStream(Path.Combine(Application.dataPath, out_filepathMapLocationRangeX), FileMode.Create, FileAccess.Write);
                    BinaryWriter writerMapLocationRangeX = new BinaryWriter(ostream, Encoding.UTF8);
                    writerMapLocationRangeX.Write(mapLocationRangeX, 0, width * height);
                    writerMapLocationRangeX.Close();
                    ostream.Close();

                    ostream = new FileStream(Path.Combine(Application.dataPath, out_filepathMapLocationRangeY), FileMode.Create, FileAccess.Write);
                    BinaryWriter writerMapLocationRangeY = new BinaryWriter(ostream, Encoding.UTF8);
                    writerMapLocationRangeY.Write(mapLocationRangeY, 0, width * height);
                    writerMapLocationRangeY.Close();
                    ostream.Close();
                }
                #else
                {
                    int width  = WoodsFile.mapWidthValue;
                    int height = WoodsFile.mapHeightValue;

                    mapLocationRangeX = new byte[width * height];
                    mapLocationRangeY = new byte[width * height];

                    MemoryStream istream;
                    TextAsset    assetMapLocationRangeX = Resources.Load <TextAsset>(filenameMapLocationRangeX);
                    if (assetMapLocationRangeX != null)
                    {
                        istream = new MemoryStream(assetMapLocationRangeX.bytes);
                        BinaryReader readerMapLocationRangeX = new BinaryReader(istream, Encoding.UTF8);
                        readerMapLocationRangeX.Read(mapLocationRangeX, 0, width * height);
                        readerMapLocationRangeX.Close();
                        istream.Close();
                    }

                    TextAsset assetMapLocationRangeY = Resources.Load <TextAsset>(filenameMapLocationRangeY);
                    if (assetMapLocationRangeY)
                    {
                        istream = new MemoryStream(assetMapLocationRangeY.bytes);
                        BinaryReader readerMapLocationRangeY = new BinaryReader(istream, Encoding.UTF8);
                        readerMapLocationRangeY.Read(mapLocationRangeY, 0, width * height);
                        readerMapLocationRangeY.Close();
                        istream.Close();
                    }

                    //FileStream istream;
                    //istream = new FileStream(filepathMapLocationRangeX, FileMode.Open, FileAccess.Read);
                    //BinaryReader readerMapLocationRangeX = new BinaryReader(istream, Encoding.UTF8);
                    //readerMapLocationRangeX.Read(mapLocationRangeX, 0, width * height);
                    //readerMapLocationRangeX.Close();
                    //istream.Close();

                    //istream = new FileStream(filepathMapLocationRangeY, FileMode.Open, FileAccess.Read);
                    //BinaryReader readerMapLocationRangeY = new BinaryReader(istream, Encoding.UTF8);
                    //readerMapLocationRangeY.Read(mapLocationRangeY, 0, width * height);
                    //readerMapLocationRangeY.Close();
                    //istream.Close();
                }
                #endif

                if (mapDistanceSquaredFromWater == null)
                {
                    byte[] heightMapArray = contentReader.WoodsFileReader.Buffer.Clone() as byte[];
                    int    width          = WoodsFile.mapWidthValue;
                    int    height         = WoodsFile.mapHeightValue;
                    for (int y = 0; y < height; y++)
                    {
                        for (int x = 0; x < width; x++)
                        {
                            if (heightMapArray[y * width + x] <= 2)
                            {
                                heightMapArray[y * width + x] = 1;
                            }
                            else
                            {
                                heightMapArray[y * width + x] = 0;
                            }
                        }
                    }
                    //now set image borders to "water" (this is a workaround to prevent mountains to become too high in north-east and south-east edge of map)
                    for (int y = 0; y < height; y++)
                    {
                        heightMapArray[y * width + 0]         = 1;
                        heightMapArray[y * width + width - 1] = 1;
                    }
                    for (int x = 0; x < width; x++)
                    {
                        heightMapArray[0 * width + x]            = 1;
                        heightMapArray[(height - 1) * width + x] = 1;
                    }

                    mapDistanceSquaredFromWater = imageDistanceTransform(heightMapArray, width, height, 1);

                    heightMapArray = null;
                }

                if (mapDistanceSquaredFromLocations == null)
                {
                    int width  = WoodsFile.mapWidthValue;
                    int height = WoodsFile.mapHeightValue;
                    mapLocations = new byte[width * height];

                    for (int y = 0; y < height; y++)
                    {
                        for (int x = 0; x < width; x++)
                        {
                            ContentReader.MapSummary summary;
                            if (contentReader.HasLocation(x + 1, y + 1, out summary))
                            {
                                mapLocations[y * width + x] = 1;
                            }
                            else
                            {
                                mapLocations[y * width + x] = 0;
                            }
                        }
                    }
                    mapDistanceSquaredFromLocations = imageDistanceTransform(mapLocations, width, height, 1);
                }



                if (mapMultipliers == null)
                {
                    int width  = WoodsFile.mapWidthValue;
                    int height = WoodsFile.mapHeightValue;
                    mapMultipliers = new float[width * height];

                    // compute the multiplier and store it in mapMultipliers
                    for (int y = 0; y < height; y++)
                    {
                        for (int x = 0; x < width; x++)
                        {
                            float distanceFromWater    = (float)Math.Sqrt(mapDistanceSquaredFromWater[y * WoodsFile.mapWidthValue + x]);
                            float distanceFromLocation = (float)Math.Sqrt(mapDistanceSquaredFromLocations[y * WoodsFile.mapWidthValue + x]);
                            float multiplierLocation   = (distanceFromLocation * extraExaggerationFactorLocationDistance + 1.0f); // terrain distant from location gets extra exaggeration
                            if (distanceFromWater < minDistanceFromWaterForExtraExaggeration)                                     // except if it is near water
                            {
                                multiplierLocation = 1.0f;
                            }
                            mapMultipliers[y * width + x] = (Math.Min(maxHeightsExaggerationMultiplier, multiplierLocation * Math.Max(1.0f, distanceFromWater * exaggerationFactorWaterDistance)));
                        }
                    }

                    // multipliedMap gets smoothed
                    float[] newmapMultipliers = mapMultipliers.Clone() as float[];
                    float[,] weights = { { 0.0625f, 0.125f, 0.0625f }, { 0.125f, 0.25f, 0.125f }, { 0.0625f, 0.125f, 0.0625f } };
                    for (int y = 1; y < height - 1; y++)
                    {
                        for (int x = 1; x < width - 1; x++)
                        {
                            if (mapDistanceSquaredFromLocations[y * width + x] <= 2) // at and around locations ( <= 2 ... only map pixels in 8-connected neighborhood (distanceFromLocationMaps stores squared distances...))
                            {
                                newmapMultipliers[y * width + x] =
                                    weights[0, 0] * mapMultipliers[(y - 1) * width + (x - 1)] + weights[0, 1] * mapMultipliers[(y - 1) * width + (x)] + weights[0, 2] * mapMultipliers[(y - 1) * width + (x + 1)] +
                                    weights[1, 0] * mapMultipliers[(y - 0) * width + (x - 1)] + weights[1, 1] * mapMultipliers[(y - 0) * width + (x)] + weights[1, 2] * mapMultipliers[(y - 0) * width + (x + 1)] +
                                    weights[2, 0] * mapMultipliers[(y + 1) * width + (x - 1)] + weights[2, 1] * mapMultipliers[(y + 1) * width + (x)] + weights[2, 2] * mapMultipliers[(y + 1) * width + (x + 1)];
                            }
                        }
                    }
                    mapMultipliers = newmapMultipliers;

                    newmapMultipliers = null;
                    weights           = null;
                }

                //the height map gets smoothed as well
                {
                    int    width           = WoodsFile.mapWidthValue;
                    int    height          = WoodsFile.mapHeightValue;
                    byte[] heightMapBuffer = contentReader.WoodsFileReader.Buffer.Clone() as byte[];
                    int[,] intWeights = { { 1, 2, 1 }, { 2, 4, 2 }, { 1, 2, 1 } };
                    for (int y = 1; y < height - 1; y++)
                    {
                        for (int x = 1; x < width - 1; x++)
                        {
                            if (mapDistanceSquaredFromWater[y * width + x] > 0) // check if squared distance from water is greater than zero -> if it is no water pixel
                            {
                                int value =
                                    intWeights[0, 0] * (int)heightMapBuffer[(y - 1) * width + (x - 1)] + intWeights[0, 1] * (int)heightMapBuffer[(y - 1) * width + (x)] + intWeights[0, 2] * (int)heightMapBuffer[(y - 1) * width + (x + 1)] +
                                    intWeights[1, 0] * (int)heightMapBuffer[(y - 0) * width + (x - 1)] + intWeights[1, 1] * (int)heightMapBuffer[(y - 0) * width + (x)] + intWeights[1, 2] * (int)heightMapBuffer[(y - 0) * width + (x + 1)] +
                                    intWeights[2, 0] * (int)heightMapBuffer[(y + 1) * width + (x - 1)] + intWeights[2, 1] * (int)heightMapBuffer[(y + 1) * width + (x)] + intWeights[2, 2] * (int)heightMapBuffer[(y + 1) * width + (x + 1)];

                                heightMapBuffer[y * width + x] = (byte)(value / 16);
                            }
                        }
                    }
                    contentReader.WoodsFileReader.Buffer = heightMapBuffer;

                    heightMapBuffer = null;
                    intWeights      = null;
                }

                // build tree coverage map
                if (mapTreeCoverage == null)
                {
                    int width  = WoodsFile.mapWidthValue;
                    int height = WoodsFile.mapHeightValue;
                    mapTreeCoverage = new byte[width * height];

                    #if !LOAD_TREE_COVERAGE_MAP
                    {
                        float startTreeCoverageAtElevation = ImprovedTerrainSampler.baseHeightScale * 2.0f; // ImprovedTerrainSampler.scaledBeachElevation;
                        float minTreeCoverageSaturated     = ImprovedTerrainSampler.baseHeightScale * 6.0f;
                        float maxTreeCoverageSaturated     = ImprovedTerrainSampler.baseHeightScale * 60.0f;
                        float endTreeCoverageAtElevation   = ImprovedTerrainSampler.baseHeightScale * 80.0f;
                        //float maxElevation = 0.0f;
                        for (int y = 0; y < height; y++)
                        {
                            for (int x = 0; x < width; x++)
                            {
                                int   readIndex = (height - 1 - y) * width + x;
                                float w         = 0.0f;

                                //float elevation = ((float)contentReader.WoodsFileReader.Buffer[(height - 1 - y) * width + x]) / 255.0f; // *mapMultipliers[index];
                                float elevation = ((float)contentReader.WoodsFileReader.Buffer[readIndex]) * mapMultipliers[readIndex];

                                //maxElevation = Math.Max(maxElevation, elevation);
                                if ((elevation > minTreeCoverageSaturated) && (elevation < maxTreeCoverageSaturated))
                                {
                                    w = 1.0f;
                                }
                                else if ((elevation >= startTreeCoverageAtElevation) && (elevation <= minTreeCoverageSaturated))
                                {
                                    w = (elevation - startTreeCoverageAtElevation) / (minTreeCoverageSaturated - startTreeCoverageAtElevation);
                                }
                                else if ((elevation >= maxTreeCoverageSaturated) && (elevation <= endTreeCoverageAtElevation))
                                {
                                    w = 1.0f - ((elevation - maxTreeCoverageSaturated) / (endTreeCoverageAtElevation - maxTreeCoverageSaturated));
                                }

                                //w = 0.65f * w + 0.35f * Math.Min(6.0f, (float)Math.Sqrt(mapDistanceSquaredFromLocations[y * width + x])) / 6.0f;

                                mapTreeCoverage[(y) * width + x] = Convert.ToByte(w * 255.0f);

                                //if (elevation>0.05f)
                                //    mapTreeCoverage[index] = Convert.ToByte(250); //w * 255.0f);
                                //else mapTreeCoverage[index] = Convert.ToByte(0);

                                //if (elevation >= startTreeCoverageAtElevation)
                                //{
                                //    mapTreeCoverage[(y) * width + x] = Convert.ToByte(255.0f);
                                //} else{
                                //    mapTreeCoverage[(y) * width + x] = Convert.ToByte(0.0f);
                                //}
                            }
                        }
                    }
                    #else
                    {
                        MemoryStream istream;
                        TextAsset    assetMapTreeCoverage = Resources.Load <TextAsset>(filenameTreeCoverageMap);
                        if (assetMapTreeCoverage)
                        {
                            istream = new MemoryStream(assetMapTreeCoverage.bytes);
                            BinaryReader readerMapTreeCoverage = new BinaryReader(istream, Encoding.UTF8);
                            readerMapTreeCoverage.Read(mapTreeCoverage, 0, width * height);
                            readerMapTreeCoverage.Close();
                            istream.Close();
                        }
                    }
                    #endif

                    #if CREATE_PERSISTENT_TREE_COVERAGE_MAP
                    {
                        FileStream   ostream = new FileStream(Path.Combine(Application.dataPath, out_filepathOutTreeCoverageMap), FileMode.Create, FileAccess.Write);
                        BinaryWriter writerMapTreeCoverage = new BinaryWriter(ostream, Encoding.UTF8);
                        writerMapTreeCoverage.Write(mapTreeCoverage, 0, width * height);
                        writerMapTreeCoverage.Close();
                        ostream.Close();
                    }
                    #endif
                    //Debug.Log(string.Format("max elevation: {0}", maxElevation));
                }

                init = true;
            }
        }
 /// <summary>
 /// Gets the scene name for the dungeon at the given location.
 /// </summary>
 public static string GetSceneName(DFLocation location)
 {
     return(string.Format("DaggerfallDungeon [Region={0}, Name={1}]", location.RegionName, location.Name));
 }
Пример #9
0
        /// <summary>
        /// Opens the save game index specified.
        /// </summary>
        /// <param name="save">Save index</param>
        /// <param name="loadingInGame">True if the save game is being loaded for regular play, false if loading for Save Explorer.</param>
        /// <returns>True if successful.</returns>
        public bool OpenSave(int save, bool loadingInGame = true)
        {
            if (!HasSave(save))
            {
                return(false);
            }

            if (!LoadSaveImage(save))
            {
                throw new Exception("Could not open SaveImage for index " + save);
            }

            if (!LoadSaveName(save))
            {
                throw new Exception("Could not open SaveName for index " + save);
            }

            saveTree = new SaveTree();
            if (!saveTree.Open(Path.Combine(saveGameDict[save], SaveTree.Filename)))
            {
                throw new Exception("Could not open SaveTree for index " + save);
            }

            saveVars = new SaveVars();
            if (!saveVars.Open(Path.Combine(saveGameDict[save], SaveVars.Filename)))
            {
                throw new Exception("Could not open SaveVars for index " + save);
            }

            mapSave = new BsaFile();
            if (!mapSave.Load(Path.Combine(saveGameDict[save], "MAPSAVE.SAV"), FileUsage.UseMemory, true))
            {
                throw new Exception("Could not open MapSave for index " + save);
            }

            if (loadingInGame) // Only check MAPSAVE if loading in-game, not if viewing in Save Explorer. There is a noticeable delay for
                               // Save Explorer as the classic saves are loaded, and a null exception if the Save Explorer is opened
                               // without the game running in the editor, due to PlayerGPS.dfUnity not being instantiated.
                               // Save Explorer currently has no use for MAPSAVE data. This code should be revisited (speed up MAPSAVE processing,
                               // fix null exception, remove this bool check) if MAPSAVE-related functionality is added to Save Explorer.
            {
                PlayerGPS gps = GameManager.Instance.PlayerGPS;
                gps.ClearDiscoveryData();
                for (int regionIndex = 0; regionIndex < 62; regionIndex++)
                {
                    // Generate name from region index
                    string name = string.Format("MAPSAVE.{0:000}", regionIndex);

                    // Get record index
                    int index = mapSave.GetRecordIndex(name);
                    if (index == -1)
                    {
                        return(false);
                    }

                    // Read MAPSAVE data
                    byte[] data = mapSave.GetRecordBytes(index);

                    // Parse MAPSAVE data for discovered locations
                    DFRegion regionData    = DaggerfallUnity.Instance.ContentReader.MapFileReader.GetRegion(regionIndex);
                    int      locationCount = Math.Min(data.Length, (int)regionData.LocationCount);
                    for (int i = 0; i < locationCount; i++)
                    {
                        // If a location is marked as discovered in classic but not DF Unity, discover it for DF Unity
                        if ((data[i] & 0x40) != 0 && !regionData.MapTable[i].Discovered)
                        {
                            DFLocation location = DaggerfallUnity.Instance.ContentReader.MapFileReader.GetLocation(regionIndex, i);
                            gps.DiscoverLocation(regionData.Name, location.Name);
                        }
                    }
                }
            }

            rumorFile = new RumorFile();
            if (!rumorFile.Load(Path.Combine(saveGameDict[save], "RUMOR.DAT"), FileUsage.UseMemory, true))
            {
                UnityEngine.Debug.Log("Could not open RUMOR.DAT for index " + save);
            }

            for (int i = 0; i < rumorFile.rumors.Count; i++)
            {
                GameManager.Instance.TalkManager.ImportClassicRumor(rumorFile.rumors[i]);
            }

            bioFile = new BioFile();
            if (!bioFile.Load(Path.Combine(saveGameDict[save], "BIO.DAT")))
            {
                UnityEngine.Debug.Log("Could not open BIO.DAT for index " + save);
            }

            return(true);
        }
Пример #10
0
        // Set location tilemap data
        public static void SetLocationTiles(ref MapPixelData mapPixel)
        {
            // Get location
            DaggerfallUnity dfUnity  = DaggerfallUnity.Instance;
            DFLocation      location = dfUnity.ContentReader.MapFileReader.GetLocation(mapPixel.mapRegionIndex, mapPixel.mapLocationIndex);

            // Position tiles inside terrain area
            DFPosition tilePos = TerrainHelper.GetLocationTerrainTileOrigin(location);

            // Full 8x8 locations have "terrain blend space" around walls to smooth down random terrain towards flat area.
            // This is indicated by texture index > 55 (ground texture range is 0-55), larger values indicate blend space.
            // We need to know rect of actual city area so we can use blend space outside walls.
            int xmin = int.MaxValue, ymin = int.MaxValue;
            int xmax = 0, ymax = 0;

            // Iterate blocks of this location
            for (int blockY = 0; blockY < location.Exterior.ExteriorData.Height; blockY++)
            {
                for (int blockX = 0; blockX < location.Exterior.ExteriorData.Width; blockX++)
                {
                    // Get block data
                    DFBlock block;
                    string  blockName = dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, blockX, blockY);
                    if (!dfUnity.ContentReader.GetBlock(blockName, out block))
                    {
                        continue;
                    }

                    // Copy ground tile info
                    for (int tileY = 0; tileY < RMBLayout.RMBTilesPerBlock; tileY++)
                    {
                        for (int tileX = 0; tileX < RMBLayout.RMBTilesPerBlock; tileX++)
                        {
                            DFBlock.RmbGroundTiles tile = block.RmbBlock.FldHeader.GroundData.GroundTiles[tileX, (RMBLayout.RMBTilesPerBlock - 1) - tileY];
                            int xpos = tilePos.X + blockX * RMBLayout.RMBTilesPerBlock + tileX;
                            int ypos = tilePos.Y + blockY * RMBLayout.RMBTilesPerBlock + tileY;

                            if (tile.TextureRecord < 56)
                            {
                                // Track interior bounds of location tiled area
                                if (xpos < xmin)
                                {
                                    xmin = xpos;
                                }
                                if (xpos > xmax)
                                {
                                    xmax = xpos;
                                }
                                if (ypos < ymin)
                                {
                                    ymin = ypos;
                                }
                                if (ypos > ymax)
                                {
                                    ymax = ypos;
                                }

                                // Store texture data from block
                                mapPixel.tilemapData[JobA.Idx(xpos, ypos, MapsFile.WorldMapTileDim)] = tile.TileBitfield == 0 ? byte.MaxValue : tile.TileBitfield;
                            }
                        }
                    }
                }
            }

            // Update location rect with extra clearance
            int  extraClearance = location.MapTableData.LocationType == DFRegion.LocationTypes.TownCity ? 3 : 2;
            Rect locationRect   = new Rect();

            locationRect.xMin     = xmin - extraClearance;
            locationRect.xMax     = xmax + extraClearance;
            locationRect.yMin     = ymin - extraClearance;
            locationRect.yMax     = ymax + extraClearance;
            mapPixel.locationRect = locationRect;
        }
        /// <summary>
        /// Reads information from CLIMATE.PAK and POLITIC.PAK.
        /// </summary>
        /// <param name="dfLocation">DFLocation.</param>
        private void ReadClimatePoliticData(ref DFLocation dfLocation)
        {
            DFPosition pos = LongitudeLatitudeToMapPixel((int)dfLocation.MapTableData.Longitude, (int)dfLocation.MapTableData.Latitude);

            // Read politic data. This should always equal region index + 128.
            dfLocation.Politic = politicPak.GetValue(pos.X, pos.Y);

            // Read climate data
            int worldClimate = climatePak.GetValue(pos.X, pos.Y);
            dfLocation.Climate = MapsFile.GetWorldClimateSettings(worldClimate);
        }
        private static bool AddLocationToRegion(int regionIndex, ref DFRegion dfRegion, ref List <string> mapNames, ref List <DFRegion.RegionMapTable> mapTable, ref DFLocation dfLocation)
        {
            // Copy the location id for ReadLocationIdFast() to use instead of peeking the classic data files
            dfLocation.MapTableData.LocationId = dfLocation.Exterior.RecordElement.Header.LocationId;

            // Add location to region using next index value
            int locationIndex = (int)dfRegion.LocationCount++;

            mapNames.Add(dfLocation.Name);
            dfLocation.LocationIndex = locationIndex;
            dfLocation.Exterior.RecordElement.Header.Unknown2 = (uint)locationIndex;
            mapTable.Add(dfLocation.MapTableData);
            dfRegion.MapIdLookup.Add(dfLocation.MapTableData.MapId, locationIndex);
            dfRegion.MapNameLookup.Add(dfLocation.Name, locationIndex);

            // Store location replacement/addition
            locations[MakeLocationKey(regionIndex, locationIndex).ToString()] = dfLocation;

            // Assign any new blocks in this location a block index if they haven't already been assigned
            return(AssignBlockIndices(ref dfLocation));
        }
Пример #13
0
        /// <summary>
        /// Performs a fully standalone in-place location layout.
        /// This method is used only by editor layouts, not by streaming world.
        /// </summary>
        private void LayoutLocation(ref DFLocation location)
        {
#if SHOW_LAYOUT_TIMES
            // Start timing
            System.Diagnostics.Stopwatch stopwatch = System.Diagnostics.Stopwatch.StartNew();
            long startTime = stopwatch.ElapsedMilliseconds;
#endif

            // Get city dimensions
            int width  = location.Exterior.ExteriorData.Width;
            int height = location.Exterior.ExteriorData.Height;

            // Create billboard batch game objects for this location
            //TextureAtlasBuilder miscBillboardAtlas = null;
            summary.NatureBillboardBatch = null;
            DaggerfallBillboardBatch lightsBillboardBatch  = null;
            DaggerfallBillboardBatch animalsBillboardBatch = null;
            //DaggerfallBillboardBatch miscBillboardBatch = null;
            if (dfUnity.Option_BatchBillboards)
            {
                //miscBillboardAtlas = dfUnity.MaterialReader.MiscBillboardAtlas;
                int natureArchive = ClimateSwaps.GetNatureArchive(CurrentNatureSet, CurrentSeason);
                summary.NatureBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(natureArchive, transform);
                lightsBillboardBatch         = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.LightsTextureArchive, transform);
                animalsBillboardBatch        = GameObjectHelper.CreateBillboardBatchGameObject(TextureReader.AnimalsTextureArchive, transform);
                //miscBillboardBatch = GameObjectHelper.CreateBillboardBatchGameObject(miscBillboardAtlas.AtlasMaterial, transform);
            }

            // Import blocks
            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    if (dfUnity.Option_BatchBillboards)
                    {
                        // Set block origin for billboard batches
                        // This causes next additions to be offset by this position
                        Vector3 blockOrigin = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide));
                        summary.NatureBillboardBatch.BlockOrigin = blockOrigin;
                        lightsBillboardBatch.BlockOrigin         = blockOrigin;
                        animalsBillboardBatch.BlockOrigin        = blockOrigin;
                        //miscBillboardBatch.BlockOrigin = blockOrigin;
                    }

                    string     blockName = dfUnity.ContentReader.BlockFileReader.CheckName(dfUnity.ContentReader.MapFileReader.GetRmbBlockName(ref location, x, y));
                    GameObject go        = GameObjectHelper.CreateRMBBlockGameObject(
                        blockName,
                        x,
                        y,
                        dfUnity.Option_RMBGroundPlane,
                        dfUnity.Option_CityBlockPrefab,
                        summary.NatureBillboardBatch,
                        lightsBillboardBatch,
                        animalsBillboardBatch,
                        null, //miscBillboardAtlas,
                        null, //miscBillboardBatch,
                        CurrentNatureSet,
                        CurrentSeason);
                    go.transform.parent   = this.transform;
                    go.transform.position = new Vector3((x * RMBLayout.RMBSide), 0, (y * RMBLayout.RMBSide));
                }
            }

            // Apply batches
            if (summary.NatureBillboardBatch)
            {
                summary.NatureBillboardBatch.Apply();
            }
            if (lightsBillboardBatch)
            {
                lightsBillboardBatch.Apply();
            }
            if (animalsBillboardBatch)
            {
                animalsBillboardBatch.Apply();
            }
            //if (miscBillboardBatch) miscBillboardBatch.Apply();

            // Enumerate start marker game objects
            EnumerateStartMarkers();

#if SHOW_LAYOUT_TIMES
            // Show timer
            long totalTime = stopwatch.ElapsedMilliseconds - startTime;
            DaggerfallUnity.LogMessage(string.Format("Time to layout location: {0}ms", totalTime), true);
#endif
        }
Пример #14
0
        /// <summary>
        /// Opens the save game index specified.
        /// </summary>
        /// <param name="save">Save index</param>
        /// <returns>True if successful.</returns>
        public bool OpenSave(int save)
        {
            if (!HasSave(save))
            {
                return(false);
            }

            if (!LoadSaveImage(save))
            {
                throw new Exception("Could not open SaveImage for index " + save);
            }

            if (!LoadSaveName(save))
            {
                throw new Exception("Could not open SaveName for index " + save);
            }

            saveTree = new SaveTree();
            if (!saveTree.Open(Path.Combine(saveGameDict[save], SaveTree.Filename)))
            {
                throw new Exception("Could not open SaveTree for index " + save);
            }

            saveVars = new SaveVars();
            if (!saveVars.Open(Path.Combine(saveGameDict[save], SaveVars.Filename)))
            {
                throw new Exception("Could not open SaveVars for index " + save);
            }

            mapSave = new BsaFile();
            if (!mapSave.Load(Path.Combine(saveGameDict[save], "MAPSAVE.SAV"), FileUsage.UseMemory, true))
            {
                throw new Exception("Could not open MapSave for index " + save);
            }

            PlayerGPS gps = GameManager.Instance.PlayerGPS;

            gps.ClearDiscoveryData();
            for (int regionIndex = 0; regionIndex < 62; regionIndex++)
            {
                // Generate name from region index
                string name = string.Format("MAPSAVE.{0:000}", regionIndex);

                // Get record index
                int index = mapSave.GetRecordIndex(name);
                if (index == -1)
                {
                    return(false);
                }

                // Read MAPSAVE data
                byte[] data = mapSave.GetRecordBytes(index);

                // Parse MAPSAVE data for discovered locations
                DFRegion regionData = DaggerfallUnity.Instance.ContentReader.MapFileReader.GetRegion(regionIndex);
                for (int i = 0; i < regionData.LocationCount; i++)
                {
                    if ((data[i] & 0x40) != 0)
                    {
                        // Discover the location in DF Unity's data
                        if (regionData.MapTable[i].Discovered == false)
                        {
                            DFLocation location = DaggerfallUnity.Instance.ContentReader.MapFileReader.GetLocation(regionIndex, i);
                            gps.DiscoverLocation(regionData.Name, location.Name);
                        }
                    }
                }
            }

            return(true);
        }
        /// <summary>
        /// Reads MapPItem data.
        /// </summary>
        /// <param name="reader">A binary reader to data.</param>
        /// <param name="region">Region index.</param>
        /// <param name="location">Location Index.</param>
        /// <param name="dfLocation">Destination DFLocation.</param>
        private void ReadMapPItem(ref BinaryReader reader, int region, int location, ref DFLocation dfLocation)
        {
            // Position reader at location record by reading offset and adding to end of offset table
            reader.BaseStream.Position = location * 4;
            reader.BaseStream.Position = (regions[region].DFRegion.LocationCount * 4) + reader.ReadUInt32();

            // Store name
            dfLocation.Name = regions[region].DFRegion.MapNames[location];

            // Read LocationRecordElement
            ReadLocationRecordElement(ref reader, region, ref dfLocation.Exterior.RecordElement);

            // Read BuildingListHeader
            dfLocation.Exterior.BuildingCount = reader.ReadUInt16();
            dfLocation.Exterior.Unknown1 = new Byte[5];
            for (int i = 0; i < 5; i++) dfLocation.Exterior.Unknown1[i] = reader.ReadByte();

            // Read BuildingData
            dfLocation.Exterior.Buildings = new DFLocation.BuildingData[dfLocation.Exterior.BuildingCount];
            for (int building = 0; building < dfLocation.Exterior.BuildingCount; building++)
            {
                dfLocation.Exterior.Buildings[building].NameSeed = reader.ReadUInt16();
                dfLocation.Exterior.Buildings[building].NullValue1 = reader.ReadUInt64();
                dfLocation.Exterior.Buildings[building].NullValue2 = reader.ReadUInt64();
                dfLocation.Exterior.Buildings[building].FactionId = reader.ReadUInt16();
                dfLocation.Exterior.Buildings[building].Sector = reader.ReadInt16();
                dfLocation.Exterior.Buildings[building].LocationId = reader.ReadUInt16();
                dfLocation.Exterior.Buildings[building].BuildingType = (DFLocation.BuildingTypes)reader.ReadByte();
                dfLocation.Exterior.Buildings[building].Quality = reader.ReadByte();
            }

            // Read ExteriorData
            dfLocation.Exterior.ExteriorData.AnotherName = regions[region].MapPItem.ReadCStringSkip(reader, 0, 32);
            dfLocation.Exterior.ExteriorData.MapId = reader.ReadInt32();
            dfLocation.Exterior.ExteriorData.LocationId = reader.ReadUInt32();
            dfLocation.Exterior.ExteriorData.Width = reader.ReadByte();
            dfLocation.Exterior.ExteriorData.Height = reader.ReadByte();
            dfLocation.Exterior.ExteriorData.Unknown2 = reader.ReadBytes(7);
            dfLocation.Exterior.ExteriorData.BlockIndex = reader.ReadBytes(64);
            dfLocation.Exterior.ExteriorData.BlockNumber = reader.ReadBytes(64);
            dfLocation.Exterior.ExteriorData.BlockCharacter = reader.ReadBytes(64);
            dfLocation.Exterior.ExteriorData.Unknown3 = reader.ReadBytes(34);
            dfLocation.Exterior.ExteriorData.NullValue1 = reader.ReadUInt64();
            dfLocation.Exterior.ExteriorData.NullValue2 = reader.ReadByte();
            dfLocation.Exterior.ExteriorData.Unknown4 = new UInt32[22];
            for (int i = 0; i < 22; i++) dfLocation.Exterior.ExteriorData.Unknown4[i] = reader.ReadUInt32();
            dfLocation.Exterior.ExteriorData.NullValue3 = reader.ReadBytes(40);
            dfLocation.Exterior.ExteriorData.Unknown5 = reader.ReadUInt32();
        }
        /// <summary>
        /// Reads MapDItem data.
        /// </summary>
        /// <param name="reader">A binary reader to data.</param>
        /// <param name="region">Region index.</param>
        /// <param name="location">Location index.</param>
        /// <param name="dfLocation">Destination DFLocation.</param>
        private void ReadMapDItem(ref BinaryReader reader, int region, int location, ref DFLocation dfLocation)
        {
            // Exit if no data
            dfLocation.HasDungeon = false;
            if (reader.BaseStream.Length == 0)
                return;

            // Find dungeon offset
            bool found = false;
            UInt32 locationId = dfLocation.Exterior.RecordElement.Header.LocationId;
            UInt32 dungeonCount = reader.ReadUInt32();
            DungeonOffset dungeonOffset = new DungeonOffset();
            for (int i = 0; i < dungeonCount; i++)
            {
                // Search for dungeon offset matching location id
                dungeonOffset.Offset = reader.ReadUInt32();
                dungeonOffset.IsDungeon = reader.ReadUInt16();
                dungeonOffset.ExteriorLocationId = reader.ReadUInt16();
                if (dungeonOffset.ExteriorLocationId == locationId)
                {
                    found = true;
                    break;
                }
            }

            // Exit if correct offset not found
            if (!found)
                return;

            // Position reader at dungeon record by reading offset and adding to end of offset table
            reader.BaseStream.Position = 4 + dungeonCount * 8 + dungeonOffset.Offset;

            // Read LocationRecordElement
            ReadLocationRecordElement(ref reader, region, ref dfLocation.Dungeon.RecordElement);

            // Read DungeonHeader
            dfLocation.Dungeon.Header.NullValue1 = reader.ReadUInt16();
            dfLocation.Dungeon.Header.Unknown1 = reader.ReadUInt32();
            dfLocation.Dungeon.Header.Unknown2 = reader.ReadUInt32();
            dfLocation.Dungeon.Header.BlockCount = reader.ReadUInt16();
            dfLocation.Dungeon.Header.Unknown3 = reader.ReadBytes(5);

            // Read DungeonBlock elements
            dfLocation.Dungeon.Blocks = new DFLocation.DungeonBlock[dfLocation.Dungeon.Header.BlockCount];
            for (int i = 0; i < dfLocation.Dungeon.Header.BlockCount; i++)
            {
                // Read data
                dfLocation.Dungeon.Blocks[i].X = reader.ReadSByte();
                dfLocation.Dungeon.Blocks[i].Z = reader.ReadSByte();
                dfLocation.Dungeon.Blocks[i].BlockNumberStartIndexBitfield = reader.ReadUInt16();

                // Decompose bitfield
                UInt16 bitfield = dfLocation.Dungeon.Blocks[i].BlockNumberStartIndexBitfield;
                dfLocation.Dungeon.Blocks[i].BlockNumber = (UInt16)(bitfield & 0x3ff);
                dfLocation.Dungeon.Blocks[i].IsStartingBlock = ((bitfield & 0x400) == 0x400) ? true : false;
                dfLocation.Dungeon.Blocks[i].BlockIndex = (Byte)(bitfield >> 11);

                // Compose block name
                dfLocation.Dungeon.Blocks[i].BlockName = String.Format("{0}{1:0000000}.RDB", rdbBlockLetters[dfLocation.Dungeon.Blocks[i].BlockIndex], dfLocation.Dungeon.Blocks[i].BlockNumber);
            }

            // Set dungeon flag
            dfLocation.HasDungeon = true;
        }
        /// <summary>
        /// Read LocationRecordElementData common to both MapPItem and MapDItem
        /// </summary>
        /// <param name="reader">A binary reader to data.</param>
        /// <param name="region">Region index.</param>
        /// <param name="recordElement">Destination DFLocation.LocationRecordElement.</param>
        private void ReadLocationRecordElement(ref BinaryReader reader, int region, ref DFLocation.LocationRecordElement recordElement)
        {
            // Read LocationDoorElement
            UInt32 doorCount = reader.ReadUInt32();
            recordElement.DoorCount = doorCount;
            recordElement.Doors = new DFLocation.LocationDoorElement[doorCount];
            for (int door = 0; door < doorCount; door++)
            {
                recordElement.Doors[door].BuildingDataIndex = reader.ReadUInt16();
                recordElement.Doors[door].NullValue = reader.ReadByte();
                recordElement.Doors[door].Mask = reader.ReadByte();
                recordElement.Doors[door].Unknown1 = reader.ReadByte();
                recordElement.Doors[door].Unknown2 = reader.ReadByte();
            }

            // Read LocationRecordElementHeader
            recordElement.Header.AlwaysOne1 = reader.ReadUInt32();
            recordElement.Header.NullValue1 = reader.ReadUInt16();
            recordElement.Header.NullValue2 = reader.ReadByte();
            recordElement.Header.X = reader.ReadInt32();
            recordElement.Header.NullValue3 = reader.ReadUInt32();
            recordElement.Header.Y = reader.ReadInt32();
            recordElement.Header.IsExterior = reader.ReadUInt16();
            recordElement.Header.NullValue4 = reader.ReadUInt16();
            recordElement.Header.Unknown1 = reader.ReadUInt32();
            recordElement.Header.Unknown2 = reader.ReadUInt32();
            recordElement.Header.AlwaysOne2 = reader.ReadUInt16();
            recordElement.Header.LocationId = reader.ReadUInt16();
            recordElement.Header.NullValue5 = reader.ReadUInt32();
            recordElement.Header.IsInterior = reader.ReadUInt16();
            recordElement.Header.ExteriorLocationId = reader.ReadUInt32();
            recordElement.Header.NullValue6 = reader.ReadBytes(26);
            recordElement.Header.LocationName = regions[region].MapPItem.ReadCStringSkip(reader, 0, 32);
            recordElement.Header.Unknown3 = reader.ReadBytes(9);
        }
        /// <summary>
        /// Read a location from the currently loaded region.
        /// </summary>
        /// <param name="region">Region index.</param>
        /// <param name="location">Location index.</param>
        /// <param name="dfLocation">DFLocation object to receive data.</param>
        /// <returns>True if successful, otherwise false.</returns>
        private bool ReadLocation(int region, int location, ref DFLocation dfLocation)
        {
            try
            {
                // Store parent region name
                dfLocation.RegionName = RegionNames[region];

                // Read MapPItem for this location
                BinaryReader reader = regions[region].MapPItem.GetReader();
                ReadMapPItem(ref reader, region, location, ref dfLocation);

                // Read MapDItem for this location
                reader = regions[region].MapDItem.GetReader();
                ReadMapDItem(ref reader, region, location, ref dfLocation);

                // Copy RegionMapTable data to this location
                dfLocation.MapTableData = regions[region].DFRegion.MapTable[location];

                // Read climate and politic data
                ReadClimatePoliticData(ref dfLocation);

                // Set loaded flag
                dfLocation.Loaded = true;
            }
            catch (Exception e)
            {
                Console.WriteLine(e.Message);
                return false;
            }

            return true;
        }
Пример #19
0
        /// <summary>
        /// Gets all RMB blocks from a location populated with building data from MAPS.BSA.
        /// This method is using "best effort" process at this point in time.
        /// However, it does yield very accurate results most of the time.
        /// Please use exception handling when calling this method for now.
        /// It will be progressed over time.
        /// </summary>
        /// <param name="location">Location to use.</param>
        /// <param name="blocksOut">Array of blocks populated with data from MAPS.BSA.</param>
        public static void GetLocationBuildingData(DFLocation location, out DFBlock[] blocksOut)
        {
            List <BuildingPoolItem> namedBuildingPool = new List <BuildingPoolItem>();
            List <DFBlock>          blocks            = new List <DFBlock>();

            // Get content reader
            ContentReader contentReader = DaggerfallUnity.Instance.ContentReader;

            if (contentReader == null)
            {
                throw new Exception("GetCompleteBuildingData() could not find ContentReader.");
            }

            // Store named buildings in pool for distribution
            for (int i = 0; i < location.Exterior.BuildingCount; i++)
            {
                DFLocation.BuildingData building = location.Exterior.Buildings[i];
                if (IsNamedBuilding(building.BuildingType))
                {
                    BuildingPoolItem bpi = new BuildingPoolItem();
                    bpi.buildingData = building;
                    bpi.used         = false;
                    namedBuildingPool.Add(bpi);
                }
            }

            // Get building summary of all blocks in this location
            int width  = location.Exterior.ExteriorData.Width;
            int height = location.Exterior.ExteriorData.Height;

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    // Get block name
                    string blockName = contentReader.BlockFileReader.CheckName(contentReader.MapFileReader.GetRmbBlockName(ref location, x, y));

                    // Get block data
                    DFBlock block;
                    if (!contentReader.GetBlock(blockName, out block))
                    {
                        throw new Exception("GetCompleteBuildingData() could not read block " + blockName);
                    }

                    // Assign building data for this block
                    BuildingReplacementData buildingReplacementData;
                    for (int i = 0; i < block.RmbBlock.SubRecords.Length; i++)
                    {
                        DFLocation.BuildingData building = block.RmbBlock.FldHeader.BuildingDataList[i];
                        if (IsNamedBuilding(building.BuildingType))
                        {
                            // Check for replacement building data and use it if found
                            if (WorldDataReplacement.GetBuildingReplacementData(blockName, block.Index, i, out buildingReplacementData))
                            {
                                // Use custom building values from replacement data, don't use pool or maps file
                                building.NameSeed     = location.Exterior.Buildings[0].NameSeed;
                                building.FactionId    = buildingReplacementData.FactionId;
                                building.BuildingType = (DFLocation.BuildingTypes)buildingReplacementData.BuildingType;
                                building.LocationId   = location.Exterior.Buildings[0].LocationId;
                                building.Quality      = buildingReplacementData.Quality;
                            }
                            else
                            {
                                // Try to find next building and merge data
                                BuildingPoolItem item;
                                if (!GetNextBuildingFromPool(namedBuildingPool, building.BuildingType, out item))
                                {
                                    Debug.LogFormat("End of city building list reached without finding building type {0} in location {1}.{2}", building.BuildingType, location.RegionName, location.Name);
                                }
                                else
                                {
                                    // Copy found city building data to block level
                                    building.NameSeed   = item.buildingData.NameSeed;
                                    building.FactionId  = item.buildingData.FactionId;
                                    building.Sector     = item.buildingData.Sector;
                                    building.LocationId = item.buildingData.LocationId;
                                    building.Quality    = item.buildingData.Quality;
                                }
                            }
                            // Set whatever building data we could find
                            block.RmbBlock.FldHeader.BuildingDataList[i] = building;
                        }
                    }

                    // Save block data
                    blocks.Add(block);
                }
            }

            // Send blocks array back to caller
            blocksOut = blocks.ToArray();
        }
 private static void AddNewDFLocationVariant(int locationIndex, string locationVariantKey, ref DFLocation variantLocation)
 {
     variantLocation.LocationIndex = locationIndex;
     variantLocation.Exterior.RecordElement.Header.Unknown2 = (uint)locationIndex;
     locations.Add(locationVariantKey, variantLocation);
 }
        /// <summary>
        /// Resolve block name for exterior block.
        /// </summary>
        /// <param name="dfLocation">DFLocation to resolve block name.</param>
        /// <param name="X">Block X coordinate.</param>
        /// <param name="Y">Block Y coordinate.</param>
        /// <returns>Block name.</returns>
        public string GetRmbBlockName(ref DFLocation dfLocation, int X, int Y)
        {
            string letters = string.Empty;
            string numbers = string.Empty;

            // Get indices
            int  offset         = Y * dfLocation.Exterior.ExteriorData.Width + X;
            byte blockIndex     = dfLocation.Exterior.ExteriorData.BlockIndex[offset];
            byte blockNumber    = dfLocation.Exterior.ExteriorData.BlockNumber[offset];
            byte blockCharacter = dfLocation.Exterior.ExteriorData.BlockCharacter[offset];

            // Get prefix
            string prefix = RmbBlockPrefixes[blockIndex];

            // Get letters and numbers
            if (blockIndex == 13 || blockIndex == 14)
            {
                // Handle temple logic
                if (7 < blockCharacter)
                {
                    letters = "GA";
                }
                else
                {
                    letters = "AA";
                }
                numbers = RmbTempleNumbers[blockCharacter & 7];
            }
            else
            {
                // Numbers are uniform in non-temple blocks
                numbers = string.Format("{0:00}", blockNumber);

                // Letters have some special cases
                byte q = (byte)(blockCharacter / 16);
                if (dfLocation.Name == "Wayrest")
                {
                    // Handle Wayrest exceptions
                    if (prefix == "CUST")
                    {
                        q = 0;
                    }
                    else
                    if (q > 0)
                    {
                        q--;
                    }
                }
                else if (dfLocation.Name == "Sentinel")
                {
                    // Handle Sentinel exceptions
                    if (prefix == "CUST")
                    {
                        q = 8;
                    }
                }
                else
                {
                    // Default
                    if (prefix == "CUST")
                    {
                        q = 0;
                    }
                }

                // Resolve letters
                letters = RmbBlockLetters[q];
            }

            return(prefix + letters + numbers + ".RMB");
        }
Пример #22
0
        void Update()
        {
            if (!DaggerfallUnity.Settings.Nystul_RealtimeReflections)
            {
                return;
            }

            GameObject goPlayerAdvanced = GameObject.Find("PlayerAdvanced");

            PlayerGPS playerGPS = GameObject.Find("PlayerAdvanced").GetComponent <PlayerGPS>();

            if (!playerGPS)
            {
                return;
            }

            if (GameManager.Instance.IsPlayerInside)
            {
                RaycastHit hit;
                float      distanceToGround = 0;

                if (Physics.Raycast(goPlayerAdvanced.transform.position, -Vector3.up, out hit, 100.0F))
                {
                    distanceToGround = hit.distance;
                }
                reflectionPlaneBottom.transform.position = goPlayerAdvanced.transform.position - new Vector3(0.0f, distanceToGround, 0.0f);

                float distanceLevelBelow = getDistanceToLowerLevel(goPlayerAdvanced);
                //Debug.Log(string.Format("distance to lower level: {0}", distanceLevelBelow));
                reflectionPlaneSeaLevel.transform.position = goPlayerAdvanced.transform.position - new Vector3(0.0f, distanceLevelBelow, 0.0f);
            }
            else
            //if (!GameManager.Instance.IsPlayerInside)
            {
                Terrain terrainInstancePlayerTerrain = null;

                int referenceLocationX = playerGPS.CurrentMapPixel.X;
                int referenceLocationY = playerGPS.CurrentMapPixel.Y;

                ContentReader.MapSummary mapSummary;
                // if there is no location at current player position...
                if (!DaggerfallUnity.Instance.ContentReader.HasLocation(referenceLocationX, referenceLocationY, out mapSummary))
                {
                    // search for largest location in local 8-neighborhood and take this as reference location for location reflection plane
                    int maxLocationArea = -1;
                    for (int y = -1; y <= +1; y++)
                    {
                        for (int x = -1; x <= +1; x++)
                        {
                            if (DaggerfallUnity.Instance.ContentReader.HasLocation(playerGPS.CurrentMapPixel.X + x, playerGPS.CurrentMapPixel.Y + y, out mapSummary))
                            {
                                DFLocation location       = DaggerfallUnity.Instance.ContentReader.MapFileReader.GetLocation(mapSummary.RegionIndex, mapSummary.MapIndex);
                                byte       locationRangeX = location.Exterior.ExteriorData.Width;
                                byte       locationRangeY = location.Exterior.ExteriorData.Height;
                                int        locationArea   = locationRangeX * locationRangeY;

                                if (locationArea > maxLocationArea)
                                {
                                    referenceLocationX = playerGPS.CurrentMapPixel.X + x;
                                    referenceLocationY = playerGPS.CurrentMapPixel.Y + y;
                                    maxLocationArea    = locationArea;
                                }
                            }
                        }
                    }
                }

                GameObject go = GameObject.Find("StreamingTarget");
                if (go == null)
                {
                    return;
                }

                foreach (Transform child in go.transform)
                {
                    DaggerfallTerrain dfTerrain = child.GetComponent <DaggerfallTerrain>();
                    if (!dfTerrain)
                    {
                        continue;
                    }

                    if ((dfTerrain.MapPixelX != referenceLocationX) || (dfTerrain.MapPixelY != referenceLocationY))
                    {
                        continue;
                    }


                    Terrain terrainInstance = child.GetComponent <Terrain>();
                    terrainInstancePlayerTerrain = terrainInstance;

                    if ((terrainInstance) && (terrainInstance.terrainData))
                    {
                        float   scale      = terrainInstance.terrainData.heightmapScale.x;
                        float   xSamplePos = DaggerfallUnity.Instance.TerrainSampler.HeightmapDimension * 0.55f;
                        float   ySamplePos = DaggerfallUnity.Instance.TerrainSampler.HeightmapDimension * 0.55f;
                        Vector3 pos        = new Vector3(xSamplePos * scale, 0, ySamplePos * scale);
                        float   height     = terrainInstance.SampleHeight(pos + terrainInstance.transform.position);

                        float positionY = height + terrainInstance.transform.position.y;
                        reflectionPlaneBottom.transform.position = new Vector3(goPlayerAdvanced.transform.position.x + terrainInstance.transform.position.x, positionY, goPlayerAdvanced.transform.position.z + terrainInstance.transform.position.z);
                    }
                }

                if (!terrainInstancePlayerTerrain)
                {
                    return;
                }

                //Debug.Log(string.Format("playerGPS: {0}, plane: {1}", goPlayerAdvanced.transform.position.y, reflectionPlaneBottom.transform.position.y));
                if (playerGPS.transform.position.y < reflectionPlaneBottom.transform.position.y)
                {
                    RaycastHit hit;
                    float      distanceToGround = 0;

                    if (Physics.Raycast(goPlayerAdvanced.transform.position, -Vector3.up, out hit, 100.0F))
                    {
                        distanceToGround = hit.distance;
                    }

                    //Debug.Log(string.Format("distance to ground: {0}", distanceToGround));
                    reflectionPlaneBottom.transform.position = goPlayerAdvanced.transform.position - new Vector3(0.0f, distanceToGround, 0.0f);
                }

                StreamingWorld streamingWorld            = GameObject.Find("StreamingWorld").GetComponent <StreamingWorld>();
                Vector3        vecWaterHeight            = new Vector3(0.0f, (DaggerfallUnity.Instance.TerrainSampler.OceanElevation) * streamingWorld.TerrainScale, 0.0f); // water height level on y-axis (+1.0f some coastlines are incorrect otherwise)
                Vector3        vecWaterHeightTransformed = terrainInstancePlayerTerrain.transform.TransformPoint(vecWaterHeight);                                           // transform to world coordinates
                //Debug.Log(string.Format("x,y,z: {0}, {1}, {2}", vecWaterHeight.x, vecWaterHeight.y, vecWaterHeight.z));
                //Debug.Log(string.Format("transformed x,y,z: {0}, {1}, {2}", vecWaterHeightTransformed.x, vecWaterHeightTransformed.y, vecWaterHeightTransformed.z));
                reflectionPlaneSeaLevel.transform.position = new Vector3(goPlayerAdvanced.transform.position.x, vecWaterHeightTransformed.y, goPlayerAdvanced.transform.position.z);
            }

            if (GameManager.Instance.IsPlayerInside && !playerInside)
            {
                playerInside = true; // player now inside

                mirrorRefl.CurrentBackgroundSettings         = MirrorReflection.EnvironmentSetting.IndoorSetting;
                mirrorReflSeaLevel.CurrentBackgroundSettings = MirrorReflection.EnvironmentSetting.IndoorSetting;

                if (useDeferredReflections)
                {
                    componentDefferedPlanarReflections.enabled = true;
                }
            }
            else if (!GameManager.Instance.IsPlayerInside && playerInside)
            {
                playerInside = false; // player now outside

                mirrorRefl.CurrentBackgroundSettings         = MirrorReflection.EnvironmentSetting.OutdoorSetting;
                mirrorReflSeaLevel.CurrentBackgroundSettings = MirrorReflection.EnvironmentSetting.OutdoorSetting;

                if (useDeferredReflections)
                {
                    componentDefferedPlanarReflections.enabled = false;
                }
            }
        }
        /// <summary>
        /// Resolve block name for exterior block.
        /// </summary>
        /// <param name="dfLocation">DFLocation to resolve block name.</param>
        /// <param name="x">Block X coordinate.</param>
        /// <param name="y">Block Y coordinate.</param>
        /// <returns>Block name.</returns>
        public string GetRmbBlockName(ref DFLocation dfLocation, int x, int y)
        {
            string letters = string.Empty;
            string numbers = string.Empty;

            // Get indices
            int offset = y * dfLocation.Exterior.ExteriorData.Width + x;
            byte blockIndex = dfLocation.Exterior.ExteriorData.BlockIndex[offset];
            byte blockNumber = dfLocation.Exterior.ExteriorData.BlockNumber[offset];
            byte blockCharacter = dfLocation.Exterior.ExteriorData.BlockCharacter[offset];

            // Get prefix
            string prefix = rmbBlockPrefixes[blockIndex];

            // Get letters and numbers
            if (blockIndex == 13 || blockIndex == 14)
            {
                // Handle temple logic
                if (7 < blockCharacter) letters = "GA"; else letters = "AA";
                numbers = rmbTempleNumbers[blockCharacter & 7];
            }
            else
            {
                // Numbers are uniform in non-temple blocks
                numbers = string.Format("{0:00}", blockNumber);

                // Letters have some special cases
                byte q = (byte)(blockCharacter / 16);
                if (dfLocation.Name == "Wayrest")
                {
                    // Handle Wayrest exceptions
                    if (prefix == "CUST")
                        q = 0;
                    else
                        if (q > 0) q--;
                }
                else if (dfLocation.Name == "Sentinel")
                {
                    // Handle Sentinel exceptions
                    if (prefix == "CUST")
                        q = 8;
                }
                else
                {
                    // Default
                    if (prefix == "CUST")
                        q = 0;
                }

                // Resolve letters
                letters = rmbBlockLetters[q];
            }

            return prefix + letters + numbers + ".RMB";
        }
Пример #24
0
        // Start new character to location specified in INI
        void StartNewCharacter()
        {
            DaggerfallUnity.ResetUID();
            RaiseOnNewGameEvent();
            DaggerfallUI.Instance.PopToHUD();
            ResetWeaponManager();

            // Must have a character document
            if (characterDocument == null)
            {
                characterDocument = new CharacterDocument();
            }

            // Assign character sheet
            PlayerEntity playerEntity = FindPlayerEntity();

            playerEntity.AssignCharacter(characterDocument);

            // Set game time
            DaggerfallUnity.Instance.WorldTime.Now.SetClassicGameStartTime();

            // Get start parameters
            DFPosition mapPixel       = new DFPosition(DaggerfallUnity.Settings.StartCellX, DaggerfallUnity.Settings.StartCellY);
            bool       startInDungeon = DaggerfallUnity.Settings.StartInDungeon;

            // Read location if any
            DFLocation location = new DFLocation();

            ContentReader.MapSummary mapSummary;
            bool hasLocation = DaggerfallUnity.Instance.ContentReader.HasLocation(mapPixel.X, mapPixel.Y, out mapSummary);

            if (hasLocation)
            {
                if (!DaggerfallUnity.Instance.ContentReader.GetLocation(mapSummary.RegionIndex, mapSummary.MapIndex, out location))
                {
                    hasLocation = false;
                }
            }

            if (NoWorld)
            {
                playerEnterExit.DisableAllParents();
            }
            else
            {
                // Start at specified location
                StreamingWorld streamingWorld = FindStreamingWorld();
                if (hasLocation && startInDungeon && location.HasDungeon)
                {
                    if (streamingWorld)
                    {
                        streamingWorld.TeleportToCoordinates(mapPixel.X, mapPixel.Y);
                        streamingWorld.suppressWorld = true;
                    }
                    playerEnterExit.EnableDungeonParent();
                    playerEnterExit.StartDungeonInterior(location);
                }
                else
                {
                    playerEnterExit.EnableExteriorParent();
                    if (streamingWorld)
                    {
                        streamingWorld.SetAutoReposition(StreamingWorld.RepositionMethods.Origin, Vector3.zero);
                        streamingWorld.suppressWorld = false;
                    }
                }
            }

            // Assign starting gear to player entity
            DaggerfallUnity.Instance.ItemHelper.AssignStartingGear(playerEntity);

            // Start game
            GameManager.Instance.PauseGame(false);
            DaggerfallUI.Instance.FadeHUDFromBlack();
            DaggerfallUI.PostMessage(PostStartMessage);

            if (OnStartGame != null)
            {
                OnStartGame(this, null);
            }
        }
Пример #25
0
        public bool IsNotAtPort()
        {
            DFLocation location = GameManager.Instance.PlayerGPS.CurrentLocation;

            return(location.Loaded == false || (location.Exterior.ExteriorData.PortTownAndUnknown == 0 && !TravelOptionsMapWindow.HasPortExtra(location.MapTableData)));
        }
Пример #26
0
        /// <summary>
        /// Gets all RMB blocks from a location populated with building data from MAPS.BSA.
        /// This method is using "best effort" process at this point in time.
        /// However, it does yield very accurate results most of the time.
        /// Please use exception handling when calling this method for now.
        /// It will be progressed over time.
        /// </summary>
        /// <param name="location">Location to use.</param>
        /// <param name="blocksOut">Array of blocks populated with data from MAPS.BSA.</param>
        public static void GetLocationBuildingData(DFLocation location, out DFBlock[] blocksOut)
        {
            List <BuildingPoolItem> namedBuildingPool = new List <BuildingPoolItem>();
            List <DFBlock>          blocks            = new List <DFBlock>();

            // Get content reader
            ContentReader contentReader = DaggerfallUnity.Instance.ContentReader;

            if (contentReader == null)
            {
                throw new Exception("GetCompleteBuildingData() could not find ContentReader.");
            }

            // Store named buildings in pool for distribution
            for (int i = 0; i < location.Exterior.BuildingCount; i++)
            {
                DFLocation.BuildingData building = location.Exterior.Buildings[i];
                if (IsNamedBuilding(building.BuildingType))
                {
                    BuildingPoolItem bpi = new BuildingPoolItem();
                    bpi.buildingData = building;
                    bpi.used         = false;
                    namedBuildingPool.Add(bpi);
                }
            }

            // Get building summary of all blocks in this location
            int width  = location.Exterior.ExteriorData.Width;
            int height = location.Exterior.ExteriorData.Height;

            for (int y = 0; y < height; y++)
            {
                for (int x = 0; x < width; x++)
                {
                    // Get block name
                    string blockName = contentReader.BlockFileReader.CheckName(contentReader.MapFileReader.GetRmbBlockName(ref location, x, y));

                    // Get block data
                    DFBlock block;
                    if (!contentReader.GetBlock(blockName, out block))
                    {
                        throw new Exception("GetCompleteBuildingData() could not read block " + blockName);
                    }

                    // Make a copy of the building data array for our block copy since we're modifying it
                    DFLocation.BuildingData[] buildingArray = new DFLocation.BuildingData[block.RmbBlock.FldHeader.BuildingDataList.Length];
                    Array.Copy(block.RmbBlock.FldHeader.BuildingDataList, buildingArray, block.RmbBlock.FldHeader.BuildingDataList.Length);
                    block.RmbBlock.FldHeader.BuildingDataList = buildingArray;

                    // Assign building data for this block
                    BuildingReplacementData buildingReplacementData;
                    for (int i = 0; i < block.RmbBlock.SubRecords.Length; i++)
                    {
                        DFLocation.BuildingData building = block.RmbBlock.FldHeader.BuildingDataList[i];
                        if (IsNamedBuilding(building.BuildingType))
                        {
                            // Try to find next building and merge data
                            BuildingPoolItem item;
                            if (!GetNextBuildingFromPool(namedBuildingPool, building.BuildingType, out item))
                            {
                                Debug.LogFormat("End of city building list reached without finding building type {0} in location {1}.{2}", building.BuildingType, location.RegionName, location.Name);
                            }

                            // Copy found city building data to block level
                            building.NameSeed   = item.buildingData.NameSeed;
                            building.FactionId  = item.buildingData.FactionId;
                            building.Sector     = item.buildingData.Sector;
                            building.LocationId = item.buildingData.LocationId;
                            building.Quality    = item.buildingData.Quality;

                            // Check for replacement building data and use it if found
                            if (WorldDataReplacement.GetBuildingReplacementData(blockName, block.Index, i, out buildingReplacementData))
                            {
                                // Use custom building values from replacement data, but only use up pool item if factionId is zero
                                if (buildingReplacementData.FactionId != 0)
                                {
                                    // Don't use up pool item and set factionId, NameSeed, Quality from replacement data
                                    item.used          = false;
                                    building.FactionId = buildingReplacementData.FactionId;
                                    building.Quality   = buildingReplacementData.Quality;
                                    building.NameSeed  = (ushort)(buildingReplacementData.NameSeed + location.LocationIndex);   // Vary name seed by location
                                }
                                // Always override type
                                building.BuildingType = (DFLocation.BuildingTypes)buildingReplacementData.BuildingType;
                            }

                            // Matched to classic: special handling for some Order of the Raven buildings
                            if (block.RmbBlock.FldHeader.OtherNames != null &&
                                block.RmbBlock.FldHeader.OtherNames[i] == "KRAVE01.HS2")
                            {
                                building.BuildingType = DFLocation.BuildingTypes.GuildHall;
                                building.FactionId    = 414;
                            }

                            // Set whatever building data we could find
                            block.RmbBlock.FldHeader.BuildingDataList[i] = building;
                        }
                    }

                    // Save block data
                    blocks.Add(block);
                }
            }

            // Send blocks array back to caller
            blocksOut = blocks.ToArray();
        }
Пример #27
0
        // Set texture and height data for city tiles
        public static void SetLocationTiles(ContentReader contentReader, ref MapPixelData mapPixel)
        {
            const int tileDim  = 16;
            const int chunkDim = 8;

            // Get location
            DFLocation location = contentReader.MapFileReader.GetLocation(mapPixel.mapRegionIndex, mapPixel.mapLocationIndex);

            // Centre location tiles inside terrain area
            int startX = ((chunkDim * tileDim) - location.Exterior.ExteriorData.Width * tileDim) / 2;
            int startY = ((chunkDim * tileDim) - location.Exterior.ExteriorData.Height * tileDim) / 2;

            // Full 8x8 locations have "terrain blend space" around walls to smooth down random terrain towards flat area.
            // This is indicated by texture index > 55 (ground texture range is 0-55), larger values indicate blend space.
            // We need to know rect of actual city area so we can use blend space outside walls.
            int xmin = terrainSampleDim, ymin = terrainSampleDim;
            int xmax = 0, ymax = 0;

            // Iterate blocks of this location
            for (int blockY = 0; blockY < location.Exterior.ExteriorData.Height; blockY++)
            {
                for (int blockX = 0; blockX < location.Exterior.ExteriorData.Width; blockX++)
                {
                    // Get block data
                    DFBlock block;
                    string  blockName = contentReader.MapFileReader.GetRmbBlockName(ref location, blockX, blockY);
                    if (!contentReader.GetBlock(blockName, out block))
                    {
                        continue;
                    }

                    // Copy ground tile info
                    for (int tileY = 0; tileY < tileDim; tileY++)
                    {
                        for (int tileX = 0; tileX < tileDim; tileX++)
                        {
                            DFBlock.RmbGroundTiles tile = block.RmbBlock.FldHeader.GroundData.GroundTiles[tileX, (tileDim - 1) - tileY];
                            int xpos   = startX + blockX * tileDim + tileX;
                            int ypos   = startY + blockY * tileDim + tileY;
                            int offset = (ypos * terrainSampleDim) + xpos;

                            int record = tile.TextureRecord;
                            if (tile.TextureRecord < 56)
                            {
                                // Track interior bounds of location tiled area
                                if (xpos < xmin)
                                {
                                    xmin = xpos;
                                }
                                if (xpos > xmax)
                                {
                                    xmax = xpos;
                                }
                                if (ypos < ymin)
                                {
                                    ymin = ypos;
                                }
                                if (ypos > ymax)
                                {
                                    ymax = ypos;
                                }

                                // Store texture data from block
                                mapPixel.samples[offset].record   = record;
                                mapPixel.samples[offset].flip     = tile.IsFlipped;
                                mapPixel.samples[offset].rotate   = tile.IsRotated;
                                mapPixel.samples[offset].location = true;
                            }
                        }
                    }
                }
            }

            // Update location rect with extra clearance
            const int extraClearance = 2;
            Rect      locationRect = new Rect();

            locationRect.xMin     = xmin - extraClearance;
            locationRect.xMax     = xmax + extraClearance;
            locationRect.yMin     = ymin - extraClearance;
            locationRect.yMax     = ymax + extraClearance;
            mapPixel.locationRect = locationRect;
        }
        /// <summary>
        /// Reads MapDItem data.
        /// </summary>
        /// <param name="Reader">A binary reader to data.</param>
        /// <param name="Region">Region index.</param>
        /// <param name="Location">Location index.</param>
        /// <param name="dfLocation">Destination DFLocation.</param>
        private void ReadMapDItem(ref BinaryReader Reader, int Region, int Location, ref DFLocation dfLocation)
        {
            // Exit if no data
            dfLocation.HasDungeon = false;
            if (Reader.BaseStream.Length == 0)
            {
                return;
            }

            // Find dungeon offset
            bool          found         = false;
            UInt32        locationId    = dfLocation.Exterior.RecordElement.Header.LocationId;
            UInt32        dungeonCount  = Reader.ReadUInt32();
            DungeonOffset dungeonOffset = new DungeonOffset();

            for (int i = 0; i < dungeonCount; i++)
            {
                // Search for dungeon offset matching location id
                dungeonOffset.Offset             = Reader.ReadUInt32();
                dungeonOffset.IsDungeon          = Reader.ReadUInt16();
                dungeonOffset.ExteriorLocationId = Reader.ReadUInt16();
                if (dungeonOffset.ExteriorLocationId == locationId)
                {
                    found = true;
                    break;
                }
            }

            // Exit if correct offset not found
            if (!found)
            {
                return;
            }

            // Position reader at dungeon record by reading offset and adding to end of offset table
            Reader.BaseStream.Position = 4 + dungeonCount * 8 + dungeonOffset.Offset;

            // Read LocationRecordElement
            ReadLocationRecordElement(ref Reader, Region, ref dfLocation.Dungeon.RecordElement);

            // Read DungeonHeader
            dfLocation.Dungeon.Header.NullValue1 = Reader.ReadUInt16();
            dfLocation.Dungeon.Header.Unknown1   = Reader.ReadUInt32();
            dfLocation.Dungeon.Header.Unknown2   = Reader.ReadUInt32();
            dfLocation.Dungeon.Header.BlockCount = Reader.ReadUInt16();
            dfLocation.Dungeon.Header.Unknown3   = Reader.ReadBytes(5);

            // Read DungeonBlock elements
            dfLocation.Dungeon.Blocks = new DFLocation.DungeonBlock[dfLocation.Dungeon.Header.BlockCount];
            for (int i = 0; i < dfLocation.Dungeon.Header.BlockCount; i++)
            {
                // Read data
                dfLocation.Dungeon.Blocks[i].X = Reader.ReadSByte();
                dfLocation.Dungeon.Blocks[i].Z = Reader.ReadSByte();
                dfLocation.Dungeon.Blocks[i].BlockNumberStartIndexBitfield = Reader.ReadUInt16();

                // Decompose bitfield
                UInt16 bitfield = dfLocation.Dungeon.Blocks[i].BlockNumberStartIndexBitfield;
                dfLocation.Dungeon.Blocks[i].BlockNumber     = (UInt16)(bitfield & 0x3ff);
                dfLocation.Dungeon.Blocks[i].IsStartingBlock = ((bitfield & 0x400) == 0x400) ? true : false;
                dfLocation.Dungeon.Blocks[i].BlockIndex      = (Byte)(bitfield >> 11);

                // Compose block name
                dfLocation.Dungeon.Blocks[i].BlockName = String.Format("{0}{1:0000000}.RDB", RdbBlockLetters[dfLocation.Dungeon.Blocks[i].BlockIndex], dfLocation.Dungeon.Blocks[i].BlockNumber);
            }

            // Set dungeon flag
            dfLocation.HasDungeon = true;
        }
Пример #29
0
        /// <summary>
        /// Transition player through a dungeon entrance door into dungeon interior.
        /// </summary>
        /// <param name="doorOwner">Parent transform owning door array.</param>
        /// <param name="door">Exterior door player clicked on.</param>
        public void TransitionDungeonInterior(Transform doorOwner, StaticDoor door, DFLocation location, bool doFade = false)
        {
            // Ensure we have component references
            if (!ReferenceComponents())
            {
                return;
            }

            // Reset dungeon block on entering dungeon
            lastPlayerDungeonBlockIndex = -1;
            playerDungeonBlockData      = new DFLocation.DungeonBlock();

            // Override location if specified
            if (OverrideLocation != null)
            {
                DFLocation overrideLocation = dfUnity.ContentReader.MapFileReader.GetLocation(OverrideLocation.Summary.RegionName, OverrideLocation.Summary.LocationName);
                if (overrideLocation.Loaded)
                {
                    location = overrideLocation;
                }
            }

            // Raise event
            RaiseOnPreTransitionEvent(TransitionType.ToDungeonInterior, door);

            // Layout dungeon
            GameObject newDungeon = GameObjectHelper.CreateDaggerfallDungeonGameObject(location, DungeonParent.transform);

            newDungeon.hideFlags = defaultHideFlags;
            dungeon = newDungeon.GetComponent <DaggerfallDungeon>();

            // Find start marker to position player
            if (!dungeon.StartMarker)
            {
                // Could not find a start marker
                Destroy(newDungeon);
                return;
            }

            EnableDungeonParent();

            // Set to start position
            MovePlayerToMarker(dungeon.StartMarker);

            // Find closest dungeon exit door to orient player
            StaticDoor[] doors = DaggerfallStaticDoors.FindDoorsInCollections(dungeon.StaticDoorCollections, DoorTypes.DungeonExit);
            if (doors != null && doors.Length > 0)
            {
                Vector3 doorPos;
                int     doorIndex;
                if (DaggerfallStaticDoors.FindClosestDoorToPlayer(transform.position, doors, out doorPos, out doorIndex))
                {
                    // Set player facing away from door
                    PlayerMouseLook playerMouseLook = GameManager.Instance.PlayerMouseLook;
                    if (playerMouseLook)
                    {
                        Vector3 normal = DaggerfallStaticDoors.GetDoorNormal(doors[doorIndex]);
                        playerMouseLook.SetFacing(normal);
                    }
                }
            }

            // Raise event
            RaiseOnTransitionDungeonInteriorEvent(door, dungeon);

            // Fade in from black
            if (doFade)
            {
                DaggerfallUI.Instance.FadeHUDFromBlack();
            }
        }
Пример #30
0
 /// <summary>
 /// Gets the scene name for the interior behind the given door.
 /// </summary>
 public static string GetSceneName(DFLocation location, StaticDoor door)
 {
     return(GetSceneName(location.MapTableData.MapId, door.buildingKey));
 }
Пример #31
0
        /// <summary>
        /// Builds a new scene containing a location exterior.
        /// </summary>
        /// <param name="regionName">Region name.</param>
        /// <param name="locationName">Location name.</param>
        public void CreateExteriorLocationScene(string regionName, string locationName)
        {
            // Check if resulting scene will be the same
            if (this.location.RegionName == regionName &&
                this.location.Name == locationName &&
                this.sceneType == SceneTypes.Exterior)
            {
                return;
            }

            // Create location node
            host.Core.Scene.ResetScene();
            host.Core.Renderer.BackgroundColor = generalBackgroundColor;
            LocationNode node = host.Core.SceneBuilder.CreateExteriorLocationNode(regionName, locationName);

            if (node == null)
            {
                return;
            }

            // Store data
            this.location  = node.Location;
            this.sceneType = SceneTypes.Exterior;
            base.Climate   = location.Climate;

            // Add node to scene
            host.Core.Scene.AddNode(null, node);

            // Update scene so bounds are correct
            host.Core.Scene.Update(TimeSpan.MinValue);

            // Set custom movement bounds
            Vector3     center         = host.Core.Scene.Root.TransformedBounds.Center;
            float       radius         = host.Core.Scene.Root.TransformedBounds.Radius;
            BoundingBox movementBounds = new BoundingBox(
                new Vector3(center.X - radius, cameraFloorHeight, center.Z - radius),
                new Vector3(center.X + radius, cameraCeilingHeight, center.Z + radius));

            topDownCamera.MovementBounds = movementBounds;
            freeCamera.MovementBounds    = movementBounds;

            // Position top-down camera
            topDownCamera.CentreInBounds(topDownCameraStartHeight);

            // Position free camera
            freeCamera.Reference = Vector3.Forward;
            freeCamera.CentreInBounds(freeCamera.EyeHeight);
            freeCamera.Position = new Vector3(
                freeCamera.Position.X, freeCamera.Position.Y, 0f);

            // Update camera
            UpdateCamera();

            // Set sky
            if (host.Core.Renderer.Sky != null)
            {
                host.Core.Renderer.Sky.SkyIndex = node.Location.Climate.SkyArchive;
            }

            // Set status message
            currentStatus = string.Format("Exploring {0} (Exterior).", locationName);
        }
Пример #32
0
        private void UpdateWorldInfo(int x, int y)
        {
            // Requires DaggerfallUnity to be ready
            if (!ReadyCheck())
            {
                return;
            }

            // Requires MAPS.BSA connection
            if (dfUnity.ContentReader.MapFileReader == null)
            {
                return;
            }

            // Get climate and politic data
            currentClimateIndex = dfUnity.ContentReader.MapFileReader.GetClimateIndex(x, y);
            currentPoliticIndex = dfUnity.ContentReader.MapFileReader.GetPoliticIndex(x, y);
            climateSettings     = MapsFile.GetWorldClimateSettings(currentClimateIndex);
            if (currentPoliticIndex >= 128)
            {
                regionName = dfUnity.ContentReader.MapFileReader.GetRegionName(currentPoliticIndex - 128);
            }
            else if (currentPoliticIndex == 64)
            {
                regionName = "Ocean";
            }
            else
            {
                regionName = "Unknown";
            }

            // Get region data
            currentRegion = dfUnity.ContentReader.MapFileReader.GetRegion(CurrentRegionIndex);

            // Get location data
            ContentReader.MapSummary mapSummary;
            if (dfUnity.ContentReader.HasLocation(x, y, out mapSummary))
            {
                currentLocation    = dfUnity.ContentReader.MapFileReader.GetLocation(mapSummary.RegionIndex, mapSummary.MapIndex);
                hasCurrentLocation = true;
                CalculateWorldLocationRect();
            }
            else
            {
                currentLocation    = new DFLocation();
                hasCurrentLocation = false;
                ClearWorldLocationRect();
            }

            // Get location type
            if (hasCurrentLocation)
            {
                if (currentRegion.MapTable == null)
                {
                    DaggerfallUnity.LogMessage(string.Format("PlayerGPS: Location {0} in region{1} has a null MapTable.", currentLocation.Name, currentLocation.RegionName));
                }
                else
                {
                    currentLocationType = currentRegion.MapTable[mapSummary.MapIndex].LocationType;
                }
            }
        }
Пример #33
0
        void Update()
        {
            if (uiCloseWhenTop && DaggerfallUI.UIManager.TopWindow == travelControlUI)
            {
                uiCloseWhenTop = false;
                travelControlUI.CloseWindow();
            }

            if (playerAutopilot != null)
            {
                // Ensure only the travel UI is showing, stop travel if not, but allow registered window and travel map to not halt travel.
                if (GameManager.Instance.IsPlayerOnHUD ||
                    (DaggerfallUI.UIManager.TopWindow != travelControlUI &&
                     DaggerfallUI.UIManager.TopWindow != noStopForUIWindow &&
                     DaggerfallUI.UIManager.TopWindow != DaggerfallUI.Instance.DfTravelMapWindow))
                {
                    Debug.Log("Other UI activity detected, stopping travel.");
                    InterruptTravel();
                    uiCloseWhenTop = travelControlUI.isShowing;
                    return;
                }

                // Run updates for playerAutopilot and HUD
                playerAutopilot.Update();
                DaggerfallUI.Instance.DaggerfallHUD.HUDVitals.Update();

                if (DestinationName == null && followKeyCode != KeyCode.None && !InputManager.Instance.IsPaused && InputManager.Instance.GetKeyDown(followKeyCode))
                {
                    if (travelControlUI.isShowing)
                    {
                        travelControlUI.CloseWindow();
                    }
                }

                // If circumnavigating a location, check for path crossings
                PlayerGPS playerGPS = GameManager.Instance.PlayerGPS;
                if (circumnavigatePathsDataPt != 0)
                {
                    byte crossed = IsPlayerOnPath(playerGPS, circumnavigatePathsDataPt);
                    if (crossed != 0 && crossed != lastCrossed)
                    {
                        lastCrossed = crossed;
                        if (travelControlUI.isShowing)
                        {
                            travelControlUI.CloseWindow();
                        }
                        return;
                    }
                    lastCrossed = crossed;
                }

                // If travelling cautiously, check health and fatigue levels
                if (DestinationCautious)
                {
                    if (GameManager.Instance.PlayerEntity.CurrentHealthPercent * 100 < CautiousHealthMinPc)
                    {
                        StopTravelWithMessage(MsgLowHealth);
                        return;
                    }
                    if (GameManager.Instance.PlayerEntity.CurrentFatigue < DaggerfallEntity.FatigueMultiplier * CautiousFatigueMin)
                    {
                        StopTravelWithMessage(MsgLowFatigue);
                        return;
                    }
                }

                // If location pause set to nearby and travelling to destination, check for a nearby location and stop if found
                if (locationPause == LocPauseNear && DestinationName != null && playerGPS.HasCurrentLocation && !playerGPS.CurrentLocation.Equals(lastLocation) && playerGPS.CurrentLocation.Name != DestinationName)
                {
                    // Store location so it doesn't trigger again and ensure discovered
                    lastLocation = playerGPS.CurrentLocation;
                    playerGPS.DiscoverLocation(playerGPS.CurrentLocation.RegionName, playerGPS.CurrentLocation.Name);

                    StopTravelWithMessage(string.Format(MsgNearLocation, LocationTypeString(), playerGPS.CurrentLocation.Name));
                    return;
                }

                // Check for ocean climate.
                if (playerGPS.CurrentClimateIndex == (int)MapsFile.Climates.Ocean)
                {
                    StopTravelWithMessage(MsgOcean);
                    return;
                }

                // Handle encounters.
                if (ignoreEncounters && Time.unscaledTime >= ignoreEncountersTime)
                {
                    ignoreEncounters = false;
                }
                if (!ignoreEncounters && GameManager.Instance.AreEnemiesNearby())
                {
                    // This happens when DFU spawns enemies nearby, however quest trigger encounters fire the OnEncounter event first so this code is never reached.
                    Debug.Log("Enountered enemies during travel.");
                    travelControlUI.CloseWindow();
                    if (DestinationCautious)
                    {
                        AttemptAvoidEncounter();
                    }
                    else
                    {
                        DaggerfallUI.MessageBox(MsgEnemies);
                    }
                    return;
                }

                // Check for diseases.
                var currentDiseaseCount = GameManager.Instance.PlayerEffectManager.DiseaseCount;
                if (currentDiseaseCount != diseaseCount)
                {
                    if (currentDiseaseCount > diseaseCount)
                    {
                        Debug.Log("New disease detected, interrupting travel!");
                        InterruptTravel();
                        DaggerfallUI.Instance.CreateHealthStatusBox(DaggerfallUI.Instance.UserInterfaceManager.TopWindow).Show();
                    }
                    diseaseCount = currentDiseaseCount;
                }
            }
            else if (followKeyCode != KeyCode.None && !InputManager.Instance.IsPaused && InputManager.Instance.GetKeyDown(followKeyCode) && GameManager.Instance.IsPlayerOnHUD)
            {
                if (GameManager.Instance.PlayerEnterExit.IsPlayerInside)
                {
                    return;
                }
                if (GameManager.Instance.AreEnemiesNearby())
                {
                    DaggerfallUI.MessageBox(TextManager.Instance.GetLocalizedText("cannotTravelWithEnemiesNearby"));
                }
                else
                {
                    FollowPath();
                }
            }
        }
        // Start new character to location specified in INI
        void StartNewCharacter()
        {
            DaggerfallUnity.ResetUID();
            QuestMachine.Instance.ClearState();
            RaiseOnNewGameEvent();
            DaggerfallUI.Instance.PopToHUD();
            ResetWeaponManager();
            SaveLoadManager.ClearSceneCache(true);
            GameManager.Instance.GuildManager.ClearMembershipData();

            // Must have a character document
            if (characterDocument == null)
            {
                characterDocument = new CharacterDocument();
            }

            // Assign character sheet
            PlayerEntity playerEntity = FindPlayerEntity();

            playerEntity.AssignCharacter(characterDocument);

            // Set game time
            DaggerfallUnity.Instance.WorldTime.Now.SetClassicGameStartTime();

            // Set time tracked in playerEntity
            playerEntity.LastGameMinutes = DaggerfallUnity.Instance.WorldTime.DaggerfallDateTime.ToClassicDaggerfallTime();

            // Get start parameters
            DFPosition mapPixel       = new DFPosition(DaggerfallUnity.Settings.StartCellX, DaggerfallUnity.Settings.StartCellY);
            bool       startInDungeon = DaggerfallUnity.Settings.StartInDungeon;

            // Read location if any
            DFLocation location = new DFLocation();

            ContentReader.MapSummary mapSummary;
            bool hasLocation = DaggerfallUnity.Instance.ContentReader.HasLocation(mapPixel.X, mapPixel.Y, out mapSummary);

            if (hasLocation)
            {
                if (!DaggerfallUnity.Instance.ContentReader.GetLocation(mapSummary.RegionIndex, mapSummary.MapIndex, out location))
                {
                    hasLocation = false;
                }
            }

            if (NoWorld)
            {
                playerEnterExit.DisableAllParents();
            }
            else
            {
                // Start at specified location
                StreamingWorld streamingWorld = FindStreamingWorld();
                if (hasLocation && startInDungeon && location.HasDungeon)
                {
                    if (streamingWorld)
                    {
                        streamingWorld.TeleportToCoordinates(mapPixel.X, mapPixel.Y);
                        streamingWorld.suppressWorld = true;
                    }
                    playerEnterExit.EnableDungeonParent();
                    playerEnterExit.StartDungeonInterior(location);
                }
                else
                {
                    playerEnterExit.EnableExteriorParent();
                    if (streamingWorld)
                    {
                        streamingWorld.SetAutoReposition(StreamingWorld.RepositionMethods.Origin, Vector3.zero);
                        streamingWorld.suppressWorld = false;
                    }
                }
            }

            // Assign starting gear to player entity
            DaggerfallUnity.Instance.ItemHelper.AssignStartingGear(playerEntity, characterDocument.classIndex, characterDocument.isCustom);

            // Assign starting spells to player entity
            SetStartingSpells(playerEntity);

            // Apply biography effects to player entity
            BiogFile.ApplyEffects(characterDocument.biographyEffects, playerEntity);

            // Setup bank accounts and houses
            Banking.DaggerfallBankManager.SetupAccounts();
            Banking.DaggerfallBankManager.SetupHouses();

            // Initialize region data
            playerEntity.InitializeRegionData();

            // Randomize weathers
            GameManager.Instance.WeatherManager.SetClimateWeathers();

            // Start game
            GameManager.Instance.PauseGame(false);
            DaggerfallUI.Instance.FadeBehaviour.FadeHUDFromBlack();
            DaggerfallUI.PostMessage(PostStartMessage);

            lastStartMethod = StartMethods.NewCharacter;

            // Offer main quest during pre-alpha
            QuestMachine.Instance.InstantiateQuest("__MQSTAGE00");

            // Launch startup optional quest
            if (!string.IsNullOrEmpty(LaunchQuest))
            {
                QuestMachine.Instance.InstantiateQuest(LaunchQuest);
                LaunchQuest = string.Empty;
            }
            // Launch any InitAtGameStart quests
            GameManager.Instance.QuestListsManager.InitAtGameStartQuests();

            if (OnStartGame != null)
            {
                OnStartGame(this, null);
            }
        }
        /// <summary>
        /// Checks a region for added location data. (i.e. new locations)
        /// </summary>
        /// <param name="regionIndex">Region index</param>
        /// <param name="locationIndex">Location index</param>
        /// <param name="dfRegion">DFRegion data output updated with any added locations found</param>
        /// <returns>True if added blocks had indices assigned, false otherwise</returns>
        public static bool GetDFRegionAdditionalLocationData(int regionIndex, ref DFRegion dfRegion)
        {
            if (DaggerfallUnity.Settings.AssetInjection && ModManager.Instance != null)
            {
                // If found, return a previously cached DFRegion
                if (regions.ContainsKey(regionIndex))
                {
                    if (regions[regionIndex].LocationCount != noReplacementRegion.LocationCount)
                    {
                        dfRegion = regions[regionIndex];
                        return(true);
                    }
                    return(false);
                }
                // Setup local lists for the region arrays and record the location count from data
                uint          dataLocationCount         = dfRegion.LocationCount;
                List <string> mapNames                  = new List <string>(dfRegion.MapNames);
                List <DFRegion.RegionMapTable> mapTable = new List <DFRegion.RegionMapTable>(dfRegion.MapTable);
                bool newBlocksAssigned                  = false;

                // Seek from loose files
                string   locationPattern = string.Format("locationnew-*-{0}.json", regionIndex);
                string[] fileNames       = Directory.GetFiles(worldDataPath, locationPattern);
                foreach (string fileName in fileNames)
                {
                    string     locationReplacementJson = File.ReadAllText(Path.Combine(worldDataPath, fileName));
                    DFLocation dfLocation = (DFLocation)SaveLoadManager.Deserialize(typeof(DFLocation), locationReplacementJson);
                    newBlocksAssigned = AddLocationToRegion(regionIndex, ref dfRegion, ref mapNames, ref mapTable, ref dfLocation);
                }
                // Seek from mods
                string           locationExtension = string.Format("-{0}.json", regionIndex);
                List <TextAsset> assets            = ModManager.Instance.FindAssets <TextAsset>(worldData, locationExtension);
                if (assets != null)
                {
                    foreach (TextAsset locationReplacementJsonAsset in assets)
                    {
                        if (locationReplacementJsonAsset.name.StartsWith("locationnew-"))
                        {
                            DFLocation dfLocation = (DFLocation)SaveLoadManager.Deserialize(typeof(DFLocation), locationReplacementJsonAsset.text);
                            newBlocksAssigned &= AddLocationToRegion(regionIndex, ref dfRegion, ref mapNames, ref mapTable, ref dfLocation);
                        }
                    }
                }
                // If found any new locations for this region,
                if (dfRegion.LocationCount > dataLocationCount)
                {
                    // Update the region arrays from local lists
                    dfRegion.MapNames = mapNames.ToArray();
                    dfRegion.MapTable = mapTable.ToArray();

#if !UNITY_EDITOR  // Cache region data for added locations if new blocks have been assigned indices (unless running in editor)
                    if (newBlocksAssigned)
                    {
                        regions.Add(regionIndex, dfRegion);
                    }
#endif
                    Debug.LogFormat("Added {0} new DFLocation's to region {1}, indexes: {2} - {3}",
                                    dfRegion.LocationCount - dataLocationCount, regionIndex, dataLocationCount, dfRegion.LocationCount - 1);
                    return(true);
                }

#if !UNITY_EDITOR // Cache that there's no replacement region data, so only look for added locations once per region (unless running in editor)
                regions.Add(regionIndex, noReplacementRegion);
#endif
            }
            return(false);
        }
 private void PlayerGPS_OnEnterLocationRect(DFLocation location)
 {
     RevealGuildHallOnMap();
 }
        /// <summary>
        /// Checks for replacement location data.
        /// </summary>
        /// <param name="regionIndex">Region index</param>
        /// <param name="locationIndex">Location index</param>
        /// <param name="dfLocation">DFLocation data output</param>
        /// <returns>True if replacement data found, false otherwise</returns>
        public static bool GetDFLocationReplacementData(int regionIndex, int locationIndex, out DFLocation dfLocation)
        {
            if (DaggerfallUnity.Settings.AssetInjection)
            {
                int    locationKey = MakeLocationKey(regionIndex, locationIndex);
                bool   newLocation;
                string variant            = WorldDataVariants.GetLocationVariant(locationKey, out newLocation);
                string locationVariantKey = locationKey.ToString() + variant;

                // If it's a new location variant, pre-load it into cache
                if (newLocation && !locations.ContainsKey(locationVariantKey))
                {
                    if (!LoadNewDFLocationVariant(regionIndex, locationIndex, variant))
                    {
                        locationVariantKey = locationKey.ToString();    // Fall back to non-variant if load fails
                    }
                }
                // If found, return a previously cached DFLocation
                if (locations.ContainsKey(locationVariantKey))
                {
                    dfLocation = locations[locationVariantKey];
                    return(dfLocation.LocationIndex != noReplacementLocation.LocationIndex);
                }

                string fileName = GetDFLocationReplacementFilename(regionIndex, locationIndex, variant);
                TextAsset locationReplacementJsonAsset;

                // Seek from loose files
                if (File.Exists(Path.Combine(worldDataPath, fileName)))
                {
                    string locationReplacementJson = File.ReadAllText(Path.Combine(worldDataPath, fileName));
                    dfLocation = (DFLocation)SaveLoadManager.Deserialize(typeof(DFLocation), locationReplacementJson);
                }
                // Seek from mods
                else if (ModManager.Instance != null && ModManager.Instance.TryGetAsset(fileName, false, out locationReplacementJsonAsset))
                {
                    dfLocation = (DFLocation)SaveLoadManager.Deserialize(typeof(DFLocation), locationReplacementJsonAsset.text);
                }
                else
                {
#if !UNITY_EDITOR // Cache that there's no replacement location data, for non-variant. So only look for replaced locations once (unless running in editor)
                    if (variant == WorldDataVariants.NoVariant)
                    {
                        locations.Add(locationVariantKey, noReplacementLocation);
                    }
#endif
                    dfLocation = noReplacementLocation;
                    return(false);
                }
                // Assign any new blocks in this location a block index if they haven't already been assigned
                if (AssignBlockIndices(ref dfLocation))
                {
#if !UNITY_EDITOR   // Cache location data for replaced locations if new blocks have been assigned indices (unless running in editor)
                    locations.Add(locationVariantKey, dfLocation);
#endif
                }
                Debug.LogFormat("Found DFLocation override, region:{0}, index:{1} variant:{2}", regionIndex, locationIndex, variant);
                return(true);
            }
            dfLocation = noReplacementLocation;
            return(false);
        }