/// <summary> /// Draws and consume building from pool. /// Checking if buildings are ordered by special type. /// </summary> static bool GetNextBuildingFromPool(List <BuildingPoolItem> namedBuildingPool, DFLocation.BuildingTypes buildingType, out BuildingPoolItem itemOut) { itemOut = new BuildingPoolItem(); for (int i = 0; i < namedBuildingPool.Count; i++) { if (!namedBuildingPool[i].used && namedBuildingPool[i].buildingData.BuildingType == buildingType) { itemOut = namedBuildingPool[i]; itemOut.used = true; return(true); } } return(false); }
/// <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(); }
/// <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(); }