Beispiel #1
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();
        }
Beispiel #2
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();
        }
        // 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;
        }