/// <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); }
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); }
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)); }
/// <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); }
// 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)); }
/// <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 }
/// <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; }
/// <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"); }
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"; }
// 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); } }
public bool IsNotAtPort() { DFLocation location = GameManager.Instance.PlayerGPS.CurrentLocation; return(location.Loaded == false || (location.Exterior.ExteriorData.PortTownAndUnknown == 0 && !TravelOptionsMapWindow.HasPortExtra(location.MapTableData))); }
/// <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; }
/// <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> /// 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(); } }
/// <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)); }
/// <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); }
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; } } }
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); }