/// <summary> /// Get a new Material based on climate. /// </summary> /// <param name="key">Material key.</param> /// <param name="climate">New climate base.</param> /// <param name="season">New season.</param> /// <param name="windowStyle">New window style.</param> /// <returns>New material.</returns> public Material ChangeClimate(int key, ClimateBases climate, ClimateSeason season, WindowStyle windowStyle) { // Ready check if (!IsReady) { return(null); } // Reverse key and apply climate int archive, record, frame; ReverseTextureKey(key, out archive, out record, out frame); archive = ClimateSwaps.ApplyClimate(archive, record, climate, season); // Get new material Material material; CachedMaterial cm = GetMaterialFromCache(archive, record, out material); // Handle windows if (cm.isWindow) { ChangeWindowEmissionColor(material, windowStyle); } return(material); }
public void SetLocation(DFLocation location, bool performLayout = true) { if (!ReadyCheck()) { return; } // Validate if (this.isSet) { throw new Exception("This location has already been set."); } if (!location.Loaded) { throw new Exception("DFLocation not loaded."); } // Set summary summary = new LocationSummary(); summary.MapID = location.MapTableData.MapId; summary.Longitude = (int)location.MapTableData.Longitude; summary.Latitude = (int)location.MapTableData.Latitude; DFPosition mapPixel = MapsFile.LongitudeLatitudeToMapPixel(summary.Longitude, summary.Latitude); DFPosition worldCoord = MapsFile.MapPixelToWorldCoord(mapPixel.X, mapPixel.Y); summary.MapPixelX = mapPixel.X; summary.MapPixelY = mapPixel.Y; summary.WorldCoordX = worldCoord.X; summary.WorldCoordZ = worldCoord.Y; summary.RegionName = location.RegionName; summary.LocationName = location.Name; summary.WorldClimate = (MapsFile.Climates)location.Climate.WorldClimate; summary.LocationType = location.MapTableData.LocationType; summary.DungeonType = location.MapTableData.DungeonType; summary.HasDungeon = location.HasDungeon; summary.Climate = ClimateSwaps.FromAPIClimateBase(location.Climate.ClimateType); summary.Nature = ClimateSwaps.FromAPITextureSet(location.Climate.NatureSet); summary.SkyBase = location.Climate.SkyBase; summary.BlockWidth = location.Exterior.ExteriorData.Width; summary.BlockHeight = location.Exterior.ExteriorData.Height; summary.LegacyLocation = location; // Assign starting climate CurrentSeason = ClimateSeason.Summer; CurrentClimate = summary.Climate; CurrentNatureSet = summary.Nature; // Perform layout if (performLayout) { LayoutLocation(ref location); ApplyClimateSettings(); } // Set location rect SetLocationRect(); // Seal location isSet = true; }
/// <summary> /// Get model name for current climate and season. /// </summary> private static string ClimateSeasonName(string modelName) { if (!GameManager.HasInstance) { return(modelName); } // Get climate ClimateBases climateBase = ClimateSwaps.FromAPIClimateBase(GameManager.Instance.PlayerGPS.ClimateSettings.ClimateType); string name = modelName + "_" + climateBase.ToString(); // Get season if (climateBase != ClimateBases.Desert) { if (DaggerfallUnity.Instance.WorldTime.Now.SeasonValue == DaggerfallDateTime.Seasons.Winter) { name += "Winter"; } else { name += "Summer"; } } return(name); }
/// <summary> /// Layout interior for automap based on data in exterior door and optional location for climate settings. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> /// <returns>True if successful.</returns> public bool DoLayoutAutomap(Transform doorOwner, StaticDoor door, ClimateBases climateBase) { if (dfUnity == null) { dfUnity = DaggerfallUnity.Instance; } // Use specified climate this.climateBase = climateBase; // Save exterior information this.entryDoor = door; this.doorOwner = doorOwner; // Get block data blockData = dfUnity.ContentReader.BlockFileReader.GetBlock(door.blockIndex); if (blockData.Type != DFBlock.BlockTypes.Rmb) { throw new Exception(string.Format("Could not load RMB block index {0}", door.blockIndex), null); } // Get record data recordData = blockData.RmbBlock.SubRecords[door.recordIndex]; if (recordData.Interior.Header.Num3dObjectRecords == 0) { throw new Exception(string.Format("No interior 3D models found for record index {0}", door.recordIndex), null); } // Layout interior data AddModels(mapBD); return(true); }
/// <summary> /// Layout interior based on data in exterior door and optional location for climate settings. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> /// <returns>True if successful.</returns> public bool DoLayout(Transform doorOwner, StaticDoor door, ClimateBases climateBase, PlayerGPS.DiscoveredBuilding buildingData) { if (dfUnity == null) { dfUnity = DaggerfallUnity.Instance; } // Use specified climate this.climateBase = climateBase; // Save exterior information this.entryDoor = door; this.doorOwner = doorOwner; AssignBlockData(door); // Layout interior data AddModels(buildingData); AddFlats(buildingData); AddPeople(buildingData); AddActionDoors(); AddSpawnPoints(); return(true); }
/// <summary> /// Rebuild materials back to default with no climate modifier. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param> /// <param name="climate">Climate to set.</param> /// <param name="season">Season to set.</param> /// <param name="windowStyle">Style of window to set.</param> public void SetClimate(ClimateBases climate, ClimateSeason season, WindowStyle windowStyle) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (defaultTextures.Count == 0) { return; } // Get new material array Material[] materials = new Material[defaultTextures.Count]; for (int i = 0; i < defaultTextures.Count; i++) { materials[i] = dfUnity.MaterialReader.ChangeClimate(defaultTextures[i], climate, season, windowStyle); } // Assign material array if (materials != null) { GetComponent <MeshRenderer>().sharedMaterials = materials; } // Store climate settings currentClimate = climate; currentSeason = season; currentWindowStyle = windowStyle; }
/// <summary> /// Set ground climate. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param> /// <param name="climate">Climate to set.</param> /// <param name="season">Season to set.</param> public void SetClimate(DaggerfallUnity dfUnity, ClimateBases climate, ClimateSeason season) { int archive = ClimateSwaps.GetGroundArchive(climate, season); SetClimate(dfUnity, archive, season); summary.climate = climate; }
/// <summary> /// Load assets for Winter. /// </summary> public void UpdateClimateWinter(ClimateBases currentClimate) { bool drawGrass = IsGrassTransitioning(); if (drawGrass) { SetGrassColor(); SetGrassSize(); } if (!NeedsUpdate(UpdateType.Winter, currentClimate)) { return; } switch (currentClimate) { case ClimateBases.Mountain: if (drawGrass) { SetGrass(brownGrass, brownGrassRealistic); } if (RealGrass.Instance.WaterPlants) { detailPrototypes[WaterPlants].prototype = LoadGameObject(plantsMountainWinter); } break; case ClimateBases.Swamp: if (drawGrass) { SetGrass(brownGrass, brownGrassRealistic); } if (RealGrass.Instance.WaterPlants) { detailPrototypes[WaterPlants].prototype = LoadGameObject(plantsSwampWinter); } break; case ClimateBases.Temperate: if (drawGrass) { SetGrass(greenGrass, greenGrassRealistic); } if (RealGrass.Instance.WaterPlants) { detailPrototypes[WaterPlants].prototype = LoadGameObject(plantsTemperateWinter); } break; default: Debug.LogError(string.Format("RealGrass: {0} is not a valid climate (Winter)", currentClimate)); RealGrass.Instance.ToggleMod(false); break; } }
/// <summary> /// Load assets for Summer. /// </summary> public void UpdateClimateSummer(ClimateBases currentClimate) { RefreshGrassDetails(); sizeColor.Refresh(currentGrassDetail, currentGrassAccent); if ((options.GrassStyle & GrassStyle.Mixed) == GrassStyle.Mixed) { SetGrassDetail(GrassDetails, grassDetails[currentGrassDetail], ref grassDetailPrefab); SetGrassDetail(GrassAccents, grassAccents[currentGrassAccent], ref grassAccentPrefab); } if (!NeedsUpdate(UpdateType.Summer, currentClimate)) { return; } ResetColor(DetailPrototypes[WaterPlants]); switch (currentClimate) { case ClimateBases.Mountain: SetGrass(brownGrass, realisticGrass); if (options.WaterPlants) { DetailPrototypes[WaterPlants].healthyColor = new Color(0.66f, 0.88f, 0.20f); DetailPrototypes[WaterPlants].dryColor = new Color(0.95f, 0.33f, 0.36f); DetailPrototypes[WaterPlants].prototype = LoadGameObject(plantsMountain); } break; case ClimateBases.Swamp: SetGrass(brownGrass, realisticGrass); if (options.WaterPlants) { DetailPrototypes[WaterPlants].prototype = LoadGameObject(plantsSwamp); } break; case ClimateBases.Temperate: SetGrass(greenGrass, realisticGrass); if (options.WaterPlants) { DetailPrototypes[WaterPlants].prototype = LoadGameObject(plantsTemperate); } break; default: throw new ArgumentException("Invalid climate", nameof(currentClimate)); } if (options.TerrainStones) { DetailPrototypes[Rocks].prototype = LoadGameObject(rock); DetailPrototypes[Rocks].healthyColor = new Color(0.70f, 0.70f, 0.70f); DetailPrototypes[Rocks].dryColor = new Color(0.40f, 0.40f, 0.40f); } }
/// <summary> /// Add simple ground plane to block layout. /// </summary> public static GameObject AddGroundPlane( ref DFBlock blockData, Transform parent = null, ClimateBases climateBase = ClimateBases.Temperate, ClimateSeason climateSeason = ClimateSeason.Summer) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) { return(null); } GameObject go = new GameObject("Ground"); if (parent != null) { go.transform.parent = parent; } // Assign components DaggerfallGroundPlane dfGround = go.AddComponent <DaggerfallGroundPlane>(); MeshFilter meshFilter = go.GetComponent <MeshFilter>(); // Assign climate and mesh Color32[] tileMap; Mesh mesh = dfUnity.MeshReader.GetSimpleGroundPlaneMesh( ref blockData, out tileMap, dfUnity.MeshReader.AddMeshTangents, dfUnity.MeshReader.AddMeshLightmapUVs); if (mesh) { meshFilter.sharedMesh = mesh; } // Assign tileMap and climate dfGround.tileMap = tileMap; dfGround.SetClimate(dfUnity, climateBase, climateSeason); // Assign collider if (dfUnity.Option_AddMeshColliders) { go.AddComponent <BoxCollider>(); } // Assign static if (dfUnity.Option_SetStaticFlags) { go.isStatic = true; } return(go); }
private Material GetMaterial(RuntimeMaterial runtimeMaterial, ClimateBases climate, ClimateSeason season) { int archive = runtimeMaterial.Archive; int record = runtimeMaterial.Record; if (runtimeMaterial.ApplyClimate) { archive = ClimateSwaps.ApplyClimate(archive, record, climate, season); } return(DaggerfallUnity.Instance.MaterialReader.GetMaterial(archive, record)); }
/// <summary> /// True if season or climate changed and detail prototypes should be updated. /// </summary> private bool NeedsUpdate(short updateType, ClimateBases climate) { int key = (updateType << 16) + (short)climate; if (key == currentkey) { return(false); } currentkey = key; return(true); }
private void ApplyMaterials(bool force, int[] dungeonTextureTable = null) { if (Materials == null || Materials.Length == 0) { return; } if (UseDungeonTextureTable && dungeonTextureTable == null) { return; } try { var meshRenderer = GetComponent <MeshRenderer>(); if (!meshRenderer) { Debug.LogErrorFormat("Failed to find MeshRenderer on {0}.", name); return; } DFLocation.ClimateBaseType climateBaseType = GameManager.Instance.PlayerGPS.ClimateSettings.ClimateType; ClimateBases climate = ClimateSwaps.FromAPIClimateBase(climateBaseType); ClimateSeason season = DaggerfallUnity.Instance.WorldTime.Now.SeasonValue == DaggerfallDateTime.Seasons.Winter ? ClimateSeason.Winter : ClimateSeason.Summer; Material[] materials = meshRenderer.sharedMaterials; for (int i = 0; i < Materials.Length; i++) { int index = Materials[i].Index; if (!force && materials[index]) { Debug.LogWarningFormat("A runtime material is being assigned to {0} (index {1}) but current material is not equal to null." + " Make sure you are not including unnecessary auto-generated materials.", meshRenderer.name, i); } materials[index] = GetMaterial(Materials[i], climateBaseType, climate, season, dungeonTextureTable); if (!materials[index]) { Debug.LogErrorFormat("Failed to find material for {0} (index {1}).", meshRenderer.name, i); } } meshRenderer.sharedMaterials = materials; } catch (Exception e) { Debug.LogException(e); } finally { hasAppliedMaterials = true; } }
/// <summary> /// Load assets for Winter. /// </summary> public void UpdateClimateWinter(ClimateBases currentClimate) { bool drawGrass = IsGrassTransitioning(); if (drawGrass) { sizeColor.Refresh(currentGrassDetail, currentGrassAccent); } if (!NeedsUpdate(UpdateType.Winter, currentClimate)) { return; } switch (currentClimate) { case ClimateBases.Mountain: if (drawGrass) { SetGrass(brownGrass, realisticGrass); } break; case ClimateBases.Swamp: if (drawGrass) { SetGrass(brownGrass, realisticGrass); } break; case ClimateBases.Temperate: if (drawGrass) { SetGrass(greenGrass, realisticGrass); } break; default: throw new ArgumentException("Invalid climate", nameof(currentClimate)); } if (options.TerrainStones) { DetailPrototypes[Rocks].prototype = LoadGameObject(rockWinter); DetailPrototypes[Rocks].healthyColor = new Color(0.70f, 0.70f, 0.70f); DetailPrototypes[Rocks].dryColor = new Color(0.40f, 0.40f, 0.40f); } }
/// <summary> /// Add simple ground plane to block layout. /// </summary> public static GameObject AddGroundPlane( ref DFBlock blockData, Transform parent = null, ClimateBases climateBase = ClimateBases.Temperate, ClimateSeason climateSeason = ClimateSeason.Summer) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (!dfUnity.IsReady) return null; GameObject go = new GameObject("Ground"); if (parent != null) go.transform.parent = parent; // Assign components DaggerfallGroundPlane dfGround = go.AddComponent<DaggerfallGroundPlane>(); MeshFilter meshFilter = go.GetComponent<MeshFilter>(); // Assign climate and mesh Color32[] tileMap; Mesh mesh = dfUnity.MeshReader.GetSimpleGroundPlaneMesh( ref blockData, out tileMap, dfUnity.MeshReader.AddMeshTangents, dfUnity.MeshReader.AddMeshLightmapUVs); if (mesh) { meshFilter.sharedMesh = mesh; } // Assign tileMap and climate dfGround.tileMap = tileMap; dfGround.SetClimate(dfUnity, climateBase, climateSeason); // Assign collider if (dfUnity.Option_AddMeshColliders) go.AddComponent<BoxCollider>(); // Assign static if (dfUnity.Option_SetStaticFlags) go.isStatic = true; return go; }
///<summary> /// Get all accepted names for a model ordered by priority. ///</summary> private static string[] GetName(uint modelID) { if (!GameManager.HasInstance) { return new string[] { modelID.ToString() } } ; ClimateBases climateBase = ClimateSwaps.FromAPIClimateBase(GameManager.Instance.PlayerGPS.ClimateSettings.ClimateType); string climateName = climateBase == ClimateBases.Desert ? string.Format("{0}_{1}", modelID, climateBase): string.Format("{0}_{1}{2}", modelID, climateBase, DaggerfallUnity.Instance.WorldTime.Now.SeasonValue); return(new string[] { climateName, modelID.ToString() }); }
/// <summary> /// Get ground archive based on climate. /// </summary> /// <param name="climateBase">Climate base.</param> /// <param name="climateSeason">Season.</param> /// <returns>Ground archive matching climate and season.</returns> public static int GetGroundArchive(ClimateBases climateBase, ClimateSeason climateSeason) { // Apply climate int archive; switch (climateBase) { case ClimateBases.Desert: archive = 2; break; case ClimateBases.Mountain: archive = 102; break; case ClimateBases.Temperate: archive = 302; break; case ClimateBases.Swamp: archive = 402; break; default: archive = 302; break; } // Modify for season switch (climateSeason) { case ClimateSeason.Winter: archive += 1; break; case ClimateSeason.Rain: archive += 2; break; } return(archive); }
/// <summary> /// Rebuild materials back to default with no climate modifier. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param> /// <param name="climate">Climate to set.</param> /// <param name="season">Season to set.</param> /// <param name="windowStyle">Style of window to set.</param> public void SetClimate(DaggerfallUnity dfUnity, ClimateBases climate, ClimateSeason season, WindowStyle windowStyle) { if (defaultTextures.Count == 0) { return; } // Get new material array Material[] materials = new Material[defaultTextures.Count]; for (int i = 0; i < defaultTextures.Count; i++) { materials[i] = dfUnity.MaterialReader.ChangeClimate(defaultTextures[i], climate, season, windowStyle); } // Assign material array if (materials != null) { GetComponent <MeshRenderer>().sharedMaterials = materials; } }
/// <summary> /// Convert DaggerfallUnity climate base to API equivalent. /// </summary> /// <param name="climate">ClimateBases.</param> /// <returns>DFLocation.ClimateBaseType.</returns> public static DFLocation.ClimateBaseType FromUnityClimateBase(ClimateBases climate) { switch (climate) { case ClimateBases.Desert: return(DFLocation.ClimateBaseType.Desert); case ClimateBases.Mountain: return(DFLocation.ClimateBaseType.Mountain); case ClimateBases.Temperate: return(DFLocation.ClimateBaseType.Temperate); case ClimateBases.Swamp: return(DFLocation.ClimateBaseType.Swamp); default: return(DFLocation.ClimateBaseType.None); } }
/// <summary> /// Layout interior for automap based on data in exterior door and optional location for climate settings. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> /// <returns>True if successful.</returns> public bool DoLayoutAutomap(Transform doorOwner, StaticDoor door, ClimateBases climateBase) { if (dfUnity == null) { dfUnity = DaggerfallUnity.Instance; } // Use specified climate this.climateBase = climateBase; // Save exterior information this.entryDoor = door; this.doorOwner = doorOwner; AssignBlockData(door); // Layout interior data AddModels(mapBD); return(true); }
private void Display3dModelSelection(int selectedIdx) { if (goModel) { Object.Destroy(goModel); goModel = null; } // Position camera and set model id uint modelId = 0; if (housesForSale == null) { camera.transform.position = new Vector3(0, 12, DaggerfallBankManager.GetShipCameraDist((ShipType)selectedIdx)); modelId = DaggerfallBankManager.GetShipModelId((ShipType)selectedIdx); } else { camera.transform.position = new Vector3(0, 3, -20); BuildingSummary house = housesForSale[selectedIdx]; modelId = house.ModelID; } // Inject custom GameObject if available else create standard mesh game object for the model goModel = MeshReplacement.ImportCustomGameobject(modelId, goBankPurchase.transform, new Matrix4x4()); if (goModel == null) { goModel = GameObjectHelper.CreateDaggerfallMeshGameObject(modelId, goBankPurchase.transform); } goModel.layer = layer; // Apply current climate ClimateBases climateBase = ClimateSwaps.FromAPIClimateBase(GameManager.Instance.PlayerGPS.ClimateSettings.ClimateType); ClimateSeason season = (DaggerfallUnity.WorldTime.Now.SeasonValue == DaggerfallDateTime.Seasons.Winter) ? ClimateSeason.Winter : ClimateSeason.Summer; DaggerfallMesh dfMesh = goModel.GetComponent <DaggerfallMesh>(); dfMesh.SetClimate(climateBase, season, WindowStyle.Day); }
/// <summary> /// Converts an archive index to new climate and season. /// Will return same index if climate or season not supported. /// </summary> /// <param name="archive">Archive index of starting texture.</param> /// <param name="climate">Climate base to apply.</param> /// <param name="season">Climate season to apply</param> /// <returns>Archive index of new texture.</returns> public static int ApplyClimate(int archive, int record, ClimateBases climate, ClimateSeason season) { // Get climate texture info ClimateTextureInfo ci = ClimateSwaps.GetClimateTextureInfo(archive); // Ignore non-climate textures if (ci.textureSet == DFLocation.ClimateTextureSet.None) { return(archive); } // Handle missing Swamp textures if (climate == ClimateBases.Swamp) { switch (ci.textureSet) { case DFLocation.ClimateTextureSet.Interior_TempleInt: case DFLocation.ClimateTextureSet.Interior_MarbleFloors: return(archive); } } // Bypass winter swaps in desert climates entirely // There are too many bad swaps, and you never see this variant in game if (climate == ClimateBases.Desert) { ci.supportsWinter = false; } // Handle swamp climate sets with missing winter textures if (climate == ClimateBases.Swamp) { switch (ci.textureSet) { case DFLocation.ClimateTextureSet.Exterior_Castle: case DFLocation.ClimateTextureSet.Exterior_MagesGuild: ci.supportsWinter = false; break; } } // Handle archives with missing winter textures if (archive == 82 && record > 1 || archive == 77) { ci.supportsWinter = false; } // Flag to suppress climate index // Certain textures have a winter variant but are climate-specific bool suppressClimateIndex = false; switch (archive) { case 75: case 76: case 77: case 79: case 80: case 82: case 83: suppressClimateIndex = true; break; } // Calculate new index int climateIndex = 0; if (archive < 500 && !suppressClimateIndex) { climateIndex = (int)FromUnityClimateBase(climate) + (int)ci.textureSet; if (season == ClimateSeason.Winter && ci.supportsWinter) { climateIndex += (int)DFLocation.ClimateWeather.Winter; } else if (season == ClimateSeason.Rain && ci.supportsRain) { climateIndex += (int)DFLocation.ClimateWeather.Rain; } } else { climateIndex = archive; if (season == ClimateSeason.Winter && ci.supportsWinter) { climateIndex += (int)DFLocation.ClimateWeather.Winter; } } return(climateIndex); }
/// <summary> /// Transition player through an exterior door into building interior. /// </summary> /// <param name="doorOwner">Parent transform owning door array..</param> /// <param name="door">Exterior door player clicked on.</param> public void TransitionInterior(Transform doorOwner, StaticDoor door, bool doFade = false, bool start = true) { // Ensure we have component references if (!ReferenceComponents()) { return; } // Copy owner position to door // This ensures the door itself is all we need to reposition interior // Useful when loading a save and doorOwner is null (as outside world does not exist) if (doorOwner) { door.ownerPosition = doorOwner.position; door.ownerRotation = doorOwner.rotation; } if (!start) { // Update scene cache from serializable state for exterior->interior transition SaveLoadManager.CacheScene(world.SceneName); // Explicitly deregister all stateful objects since exterior isn't destroyed SaveLoadManager.DeregisterAllSerializableGameObjects(true); // Clear all stateful objects from world loose object tracking world.ClearStatefulLooseObjects(); } // Raise event RaiseOnPreTransitionEvent(TransitionType.ToBuildingInterior, door); // Ensure expired rooms are removed GameManager.Instance.PlayerEntity.RemoveExpiredRentedRooms(); // Get climate ClimateBases climateBase = ClimateBases.Temperate; if (OverrideLocation) { climateBase = OverrideLocation.Summary.Climate; } else if (playerGPS) { climateBase = ClimateSwaps.FromAPIClimateBase(playerGPS.ClimateSettings.ClimateType); } // Layout interior // This needs to be done first so we know where the enter markers are GameObject newInterior = new GameObject(DaggerfallInterior.GetSceneName(playerGPS.CurrentLocation, door)); newInterior.hideFlags = defaultHideFlags; interior = newInterior.AddComponent <DaggerfallInterior>(); // Try to layout interior // If we fail for any reason, use that old chestnut "this house has nothing of value" try { interior.DoLayout(doorOwner, door, climateBase, buildingDiscoveryData); } catch { DaggerfallUI.AddHUDText(HardStrings.thisHouseHasNothingOfValue); Destroy(newInterior); return; } // Position interior directly inside of exterior // This helps with finding closest enter/exit point relative to player position interior.transform.position = door.ownerPosition + (Vector3)door.buildingMatrix.GetColumn(3); interior.transform.rotation = GameObjectHelper.QuaternionFromMatrix(door.buildingMatrix); // Position player above closest enter marker Vector3 marker; if (!interior.FindClosestEnterMarker(transform.position, out marker)) { // Could not find an enter marker, probably not a valid interior Destroy(newInterior); return; } // Enumerate all exterior doors belonging to this building DaggerfallStaticDoors exteriorStaticDoors = interior.ExteriorDoors; if (exteriorStaticDoors && doorOwner) { List <StaticDoor> buildingDoors = new List <StaticDoor>(); for (int i = 0; i < exteriorStaticDoors.Doors.Length; i++) { if (exteriorStaticDoors.Doors[i].recordIndex == door.recordIndex) { StaticDoor newDoor = exteriorStaticDoors.Doors[i]; newDoor.ownerPosition = doorOwner.position; newDoor.ownerRotation = doorOwner.rotation; buildingDoors.Add(newDoor); } } SetExteriorDoors(buildingDoors.ToArray()); } // Assign new interior to parent if (InteriorParent != null) { newInterior.transform.parent = InteriorParent.transform; } // Cache some information about this interior buildingType = interior.BuildingData.BuildingType; factionID = interior.BuildingData.FactionId; // Set player to marker position // TODO: Find closest door for player facing transform.position = marker + Vector3.up * (controller.height * 0.6f); SetStanding(); EnableInteriorParent(); // Add quest resources GameObjectHelper.AddQuestResourceObjects(SiteTypes.Building, interior.transform, interior.EntryDoor.buildingKey); // Update serializable state from scene cache for exterior->interior transition (unless new/load game) if (!start) { SaveLoadManager.RestoreCachedScene(interior.name); } // Raise event RaiseOnTransitionInteriorEvent(door, interior); // Fade in from black if (doFade) { DaggerfallUI.Instance.FadeHUDFromBlack(); } }
/// <summary> /// Load assets for Summer. /// </summary> public void UpdateClimateSummer(ClimateBases currentClimate) { RefreshGrassDetails(); SetGrassColor(); SetGrassSize(); if (RealGrass.Instance.RealisticGrass) { SetGrassDetail(GrassDetails, grassDetails[currentGrassDetail], ref grassDetailPrefab); SetGrassDetail(GrassAccents, grassAccents[currentGrassAccent], ref grassAccentPrefab); } if (RealGrass.Instance.Flowers) { detailPrototypes[Flowers].prototype = LoadGameObject(GetRandomFlowers()); } if (!NeedsUpdate(UpdateType.Summer, currentClimate)) { return; } switch (currentClimate) { case ClimateBases.Mountain: // Mountain SetGrass(brownGrass, realisticGrass); if (RealGrass.Instance.WaterPlants) { detailPrototypes[WaterPlants].prototype = LoadGameObject(plantsMountain); } break; case ClimateBases.Swamp: // Swamp SetGrass(brownGrass, realisticGrass); if (RealGrass.Instance.WaterPlants) { detailPrototypes[WaterPlants].prototype = LoadGameObject(plantsSwamp); } break; case ClimateBases.Temperate: // Temperate SetGrass(greenGrass, realisticGrass); if (RealGrass.Instance.WaterPlants) { detailPrototypes[WaterPlants].prototype = LoadGameObject(plantsTemperate); } break; default: Debug.LogError(string.Format("RealGrass: {0} is not a valid climate (Summer)", currentClimate)); RealGrass.Instance.ToggleMod(false); break; } if (RealGrass.Instance.TerrainStones) { detailPrototypes[Rocks].prototype = LoadGameObject(rock); detailPrototypes[Rocks].healthyColor = new Color(0.70f, 0.70f, 0.70f); detailPrototypes[Rocks].dryColor = new Color(0.40f, 0.40f, 0.40f); } }
/// <summary> /// Convert DaggerfallUnity climate base to API equivalent. /// </summary> /// <param name="climate">ClimateBases.</param> /// <returns>DFLocation.ClimateBaseType.</returns> public static DFLocation.ClimateBaseType FromUnityClimateBase(ClimateBases climate) { switch (climate) { case ClimateBases.Desert: return DFLocation.ClimateBaseType.Desert; case ClimateBases.Mountain: return DFLocation.ClimateBaseType.Mountain; case ClimateBases.Temperate: return DFLocation.ClimateBaseType.Temperate; case ClimateBases.Swamp: return DFLocation.ClimateBaseType.Swamp; default: return DFLocation.ClimateBaseType.None; } }
/// <summary> /// Set density for Winter. /// </summary> public void SetDensityWinter(Terrain terrain, Color32[] tilemap, ClimateBases currentClimate) { float grassChance = GetWinterGrassChance(); bool shores = currentClimate != ClimateBases.Mountain; for (int y = 0; y < tilemapSize; y++) { for (int x = 0; x < tilemapSize; x++) { switch (tilemap[(y * tilemapSize) + x].r) { case 4: case 5: case 6: case 7: if (terrainStones) { int rocks = RandomRocks(); if (rocks != 0) { var index = RandomPosition(y, x); Rocks[index.First, index.Second] = rocks; } } break; // Grass growing/dying case 8: case 9: case 10: case 11: if (grassChance != 0) { if (Random.value < grassChance) { Grass[y * 2, x * 2] = RandomThick(); Grass[y * 2, (x * 2) + 1] = RandomThick(); } if (Random.value < grassChance) { Grass[(y * 2) + 1, x * 2] = RandomThick(); Grass[(y * 2) + 1, (x * 2) + 1] = RandomThick(); } } if (terrainStones) { int rocks = RandomRocks(); if (rocks != 0) { var index = RandomPosition(y, x); Rocks[index.First, index.Second] = rocks; } } break; // Left side case 84: if (waterPlants && shores) { WaterPlants[(y * 2) + 1, x * 2] = RandomWaterPlants(); WaterPlants[y * 2, x * 2] = RandomWaterPlants(); } if (terrainStones) { Rocks[y * 2, x * 2] = RandomRocksDesert(); } break; // Lower side case 85: if (waterPlants && shores) { WaterPlants[y * 2, (x * 2) + 1] = RandomWaterPlants(); WaterPlants[y * 2, (x * 2)] = RandomWaterPlants(); } if (terrainStones) { Rocks[y * 2, x * 2] = RandomRocksDesert(); } break; // Right side case 86: if (waterPlants && shores) { WaterPlants[(y * 2) + 1, (x * 2) + 1] = RandomWaterPlants(); WaterPlants[y * 2, (x * 2) + 1] = RandomWaterPlants(); } if (terrainStones) { Rocks[(y * 2) + 1, (x * 2) + 1] = RandomRocksDesert(); } break; // Upper side case 87: if (waterPlants && shores) { WaterPlants[(y * 2) + 1, (x * 2) + 1] = RandomWaterPlants(); WaterPlants[(y * 2) + 1, x * 2] = RandomWaterPlants(); } if (terrainStones) { Rocks[(y * 2) + 1, (x * 2) + 1] = RandomRocksDesert(); } break; // Corners case 88: if (waterPlants && shores) { WaterPlants[y * 2, x * 2] = RandomWaterPlants(); WaterPlants[(y * 2) + 1, (x * 2) + 1] = RandomWaterPlants(); } if (terrainStones) { Rocks[y * 2, x * 2] = RandomRocksDesert(); } break; case 89: if (waterPlants && shores) { WaterPlants[y * 2, (x * 2) + 1] = RandomWaterPlants(); WaterPlants[(y * 2) + 1, x * 2] = RandomWaterPlants(); } if (terrainStones) { Rocks[y * 2, (x * 2) + 1] = RandomRocksDesert(); } break; case 90: if (waterPlants && shores) { WaterPlants[y * 2, x * 2] = RandomWaterPlants(); WaterPlants[(y * 2) + 1, (x * 2) + 1] = RandomWaterPlants(); } if (terrainStones) { Rocks[y * 2, x * 2] = RandomRocksDesert(); } break; case 91: if (waterPlants && shores) { WaterPlants[y * 2, (x * 2) + 1] = RandomWaterPlants(); WaterPlants[(y * 2) + 1, x * 2] = RandomWaterPlants(); } if (terrainStones) { Rocks[y * 2, (x * 2) + 1] = RandomRocksDesert(); } break; } } } }
/// <summary> /// Get a new Material based on climate. /// </summary> /// <param name="key">Material key.</param> /// <param name="climate">New climate base.</param> /// <param name="season">New season.</param> /// <param name="windowStyle">New window style.</param> /// <returns>New material.</returns> public Material ChangeClimate(int key, ClimateBases climate, ClimateSeason season, WindowStyle windowStyle) { // Ready check if (!IsReady) return null; // Reverse key and apply climate int archive, record, frame; ReverseTextureKey(key, out archive, out record, out frame); archive = ClimateSwaps.ApplyClimate(archive, record, climate, season); // Get new material Material material; CachedMaterial cm = GetMaterialFromCache(archive, record, out material); // Handle windows if (cm.isWindow) { ChangeWindowEmissionColor(material, windowStyle); } return material; }
/// <summary> /// Transition player through an exterior door into building interior. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> public void TransitionInterior(Transform doorOwner, StaticDoor door) { // Ensure we have component references if (!ReferenceComponents()) { return; } // Get current climate ClimateBases climateBase = ClimateBases.Temperate; if (playerGPS) { climateBase = ClimateSwaps.FromAPIClimateBase(playerGPS.ClimateSettings.ClimateType); } // Layout interior // This needs to be done first so we know where the enter markers are GameObject newInterior = new GameObject(string.Format("DaggerfallInterior [Block={0}, Record={1}]", door.blockIndex, door.recordIndex)); newInterior.hideFlags = HideFlags.HideAndDontSave; interior = newInterior.AddComponent <DaggerfallInterior>(); interior.DoLayout(doorOwner, door, climateBase); // Position interior directly inside of exterior // This helps with finding closest enter/exit point relative to player position interior.transform.position = doorOwner.position + (Vector3)door.buildingMatrix.GetColumn(3); interior.transform.rotation = GameObjectHelper.QuaternionFromMatrix(door.buildingMatrix); // Position player above closest enter marker Vector3 marker; if (!interior.FindClosestEnterMarker(transform.position, out marker)) { // Could not find an enter marker, probably not a valid interior Destroy(newInterior); return; } // Assign new interior to parent if (InteriorParent != null) { newInterior.transform.parent = InteriorParent.transform; } // Disable exterior parent if (ExteriorParent != null) { ExteriorParent.SetActive(false); } // Enable interior parent if (InteriorParent != null) { InteriorParent.SetActive(true); } // Cache some information about this interior buildingType = interior.BuildingData.BuildingType; // Set player to marker position // Not sure how to set facing here as player transitions to a marker, not a door // Could always find closest door and use that transform.position = marker + Vector3.up * (controller.height * 0.6f); SetStanding(); // Player is now inside building isPlayerInside = true; }
private Material GetMaterial(RuntimeMaterial runtimeMaterial, DFLocation.ClimateBaseType climateBaseType, ClimateBases climate, ClimateSeason season, int[] dungeonTextureTable) { int archive = runtimeMaterial.Archive; int record = runtimeMaterial.Record; if (dungeonTextureTable != null) { archive = DungeonTextureTables.ApplyTextureTable(archive, dungeonTextureTable, climateBaseType); } else if (runtimeMaterial.ApplyClimate) { archive = ClimateSwaps.ApplyClimate(archive, record, climate, season); } return(DaggerfallUnity.Instance.MaterialReader.GetMaterial(archive, record)); }
/// <summary> /// Add Grass and other details on terrain. /// </summary> private void AddTerrainDetails(DaggerfallTerrain daggerTerrain, TerrainData terrainData) { #if TEST_PERFORMANCE var stopwatch = new System.Diagnostics.Stopwatch(); stopwatch.Start(); #endif UnityEngine.Random.InitState(TerrainHelper.MakeTerrainKey(daggerTerrain.MapPixelX, daggerTerrain.MapPixelY)); // Terrain settings terrainData.SetDetailResolution(256, 8); terrainData.wavingGrassTint = Color.gray; Terrain terrain = daggerTerrain.gameObject.GetComponent <Terrain>(); terrain.detailObjectDistance = options.DetailObjectDistance; terrain.detailObjectDensity = options.DetailObjectDensity; // Get the current season and climate var currentSeason = DaggerfallUnity.Instance.WorldTime.Now.SeasonValue; ClimateBases climate = GetClimate(daggerTerrain.MapData.worldClimate); // Update detail layers Color32[] tilemap = daggerTerrain.TileMap; densityManager.InitDetailsLayers(); switch (climate) { case ClimateBases.Temperate: case ClimateBases.Mountain: case ClimateBases.Swamp: if (currentSeason != DaggerfallDateTime.Seasons.Winter) { detailPrototypesManager.UpdateClimateSummer(climate); densityManager.SetDensitySummer(terrain, tilemap, climate); } else { detailPrototypesManager.UpdateClimateWinter(climate); densityManager.SetDensityWinter(terrain, tilemap, climate); } break; case ClimateBases.Desert: detailPrototypesManager.UpdateClimateDesert(); densityManager.SetDensityDesert(tilemap); break; } // Assign detail prototypes to the terrain terrainData.detailPrototypes = detailPrototypesManager.DetailPrototypes; // Assign detail layers to the terrain terrainData.SetDetailLayer(0, 0, detailPrototypesManager.Grass, densityManager.Grass); if ((options.GrassStyle & GrassStyle.Mixed) == GrassStyle.Mixed && climate != ClimateBases.Desert) { terrainData.SetDetailLayer(0, 0, detailPrototypesManager.GrassDetails, densityManager.GrassDetails); terrainData.SetDetailLayer(0, 0, detailPrototypesManager.GrassAccents, densityManager.GrassAccents); } if (options.WaterPlants) { terrainData.SetDetailLayer(0, 0, detailPrototypesManager.WaterPlants, densityManager.WaterPlants); } if (options.TerrainStones) { terrainData.SetDetailLayer(0, 0, detailPrototypesManager.Rocks, densityManager.Rocks); } #if TEST_PERFORMANCE stopwatch.Stop(); Debug.LogFormat("RealGrass - Time elapsed: {0} ms.", stopwatch.Elapsed.Milliseconds); #endif }
public void SetLocation(DFLocation location, bool performLayout = true) { if (!ReadyCheck()) return; // Validate if (this.isSet) throw new Exception("This location has already been set."); if (!location.Loaded) throw new Exception("DFLocation not loaded."); // Set summary summary = new LocationSummary(); summary.ID = location.MapTableData.MapId; summary.Longitude = (int)location.MapTableData.Longitude; summary.Latitude = (int)location.MapTableData.Latitude; DFPosition mapPixel = MapsFile.LongitudeLatitudeToMapPixel(summary.Longitude, summary.Latitude); DFPosition worldCoord = MapsFile.MapPixelToWorldCoord(mapPixel.X, mapPixel.Y); summary.MapPixelX = mapPixel.X; summary.MapPixelY = mapPixel.Y; summary.WorldCoordX = worldCoord.X; summary.WorldCoordZ = worldCoord.Y; summary.RegionName = location.RegionName; summary.LocationName = location.Name; summary.WorldClimate = location.Climate.WorldClimate; summary.LocationType = location.MapTableData.LocationType; summary.DungeonType = location.MapTableData.DungeonType; summary.HasDungeon = location.HasDungeon; summary.Climate = ClimateSwaps.FromAPIClimateBase(location.Climate.ClimateType); summary.Nature = ClimateSwaps.FromAPITextureSet(location.Climate.NatureSet); summary.SkyBase = location.Climate.SkyBase; summary.BlockWidth = location.Exterior.ExteriorData.Width; summary.BlockHeight = location.Exterior.ExteriorData.Height; // Assign starting climate CurrentSeason = ClimateSeason.Summer; CurrentClimate = summary.Climate; CurrentNatureSet = summary.Nature; // Perform layout if (performLayout) { LayoutLocation(ref location); ApplyClimateSettings(); } // Seal location isSet = true; }
/// <summary> /// Transition player through an exterior door into building interior. /// </summary> /// <param name="doorOwner">Parent transform owning door array..</param> /// <param name="door">Exterior door player clicked on.</param> public void TransitionInterior(Transform doorOwner, StaticDoor door, bool doFade = false) { // Ensure we have component references if (!ReferenceComponents()) { return; } // Copy owner position to door // This ensures the door itself is all we need to reposition interior // Useful when loading a save and doorOwner is null (as outside world does not exist) if (doorOwner) { door.ownerPosition = doorOwner.position; door.ownerRotation = doorOwner.rotation; } // Raise event RaiseOnPreTransitionEvent(TransitionType.ToBuildingInterior, door); // Get climate ClimateBases climateBase = ClimateBases.Temperate; if (OverrideLocation) { climateBase = OverrideLocation.Summary.Climate; } else if (playerGPS) { climateBase = ClimateSwaps.FromAPIClimateBase(playerGPS.ClimateSettings.ClimateType); } // Layout interior // This needs to be done first so we know where the enter markers are GameObject newInterior = new GameObject(string.Format("DaggerfallInterior [Block={0}, Record={1}]", door.blockIndex, door.recordIndex)); newInterior.hideFlags = defaultHideFlags; interior = newInterior.AddComponent <DaggerfallInterior>(); // Try to layout interior // If we fail for any reason, use that old chestnut "this house has nothing of value" try { interior.DoLayout(doorOwner, door, climateBase); } catch { DaggerfallUI.AddHUDText(UserInterfaceWindows.HardStrings.thisHouseHasNothingOfValue); Destroy(newInterior); return; } // Position interior directly inside of exterior // This helps with finding closest enter/exit point relative to player position interior.transform.position = door.ownerPosition + (Vector3)door.buildingMatrix.GetColumn(3); interior.transform.rotation = GameObjectHelper.QuaternionFromMatrix(door.buildingMatrix); // Position player above closest enter marker Vector3 marker; if (!interior.FindClosestEnterMarker(transform.position, out marker)) { // Could not find an enter marker, probably not a valid interior Destroy(newInterior); return; } // Enumerate all exterior doors belonging to this building DaggerfallStaticDoors exteriorStaticDoors = interior.ExteriorDoors; if (exteriorStaticDoors && doorOwner) { List <StaticDoor> buildingDoors = new List <StaticDoor>(); for (int i = 0; i < exteriorStaticDoors.Doors.Length; i++) { if (exteriorStaticDoors.Doors[i].recordIndex == door.recordIndex) { StaticDoor newDoor = exteriorStaticDoors.Doors[i]; newDoor.ownerPosition = doorOwner.position; newDoor.ownerRotation = doorOwner.rotation; buildingDoors.Add(newDoor); } } SetExteriorDoors(buildingDoors.ToArray()); } // Assign new interior to parent if (InteriorParent != null) { newInterior.transform.parent = InteriorParent.transform; } // Cache some information about this interior buildingType = interior.BuildingData.BuildingType; // Set player to marker position // TODO: Find closest door for player facing transform.position = marker + Vector3.up * (controller.height * 0.6f); SetStanding(); EnableInteriorParent(); // Raise event RaiseOnTransitionInteriorEvent(door, interior); // Fade in from black if (doFade) { DaggerfallUI.Instance.FadeHUDFromBlack(); } }
/// <summary> /// Layout interior based on data in exterior door and optional location for climate settings. /// </summary> /// <param name="doorOwner">Parent transform owning door array.</param> /// <param name="door">Exterior door player clicked on.</param> /// <returns>True if successful.</returns> public bool DoLayout(Transform doorOwner, StaticDoor door, ClimateBases climateBase) { if (dfUnity == null) dfUnity = DaggerfallUnity.Instance; // Use specified climate this.climateBase = climateBase; // Save exterior information this.entryDoor = door; this.doorOwner = doorOwner; // Get block data blockData = dfUnity.ContentReader.BlockFileReader.GetBlock(door.blockIndex); if (blockData.Type != DFBlock.BlockTypes.Rmb) throw new Exception(string.Format("Could not load RMB block index {0}", door.blockIndex), null); // Get record data recordData = blockData.RmbBlock.SubRecords[door.recordIndex]; if (recordData.Interior.Header.Num3dObjectRecords == 0) throw new Exception(string.Format("No interior 3D models found for record index {0}", door.recordIndex), null); // Layout interior data AddModels(); AddFlats(); AddPeople(); AddActionDoors(); return true; }
/// <summary> /// Rebuild materials back to default with no climate modifier. /// </summary> /// <param name="dfUnity">DaggerfallUnity singleton. Required for content readers and settings.</param> /// <param name="climate">Climate to set.</param> /// <param name="season">Season to set.</param> /// <param name="windowStyle">Style of window to set.</param> public void SetClimate(ClimateBases climate, ClimateSeason season, WindowStyle windowStyle) { DaggerfallUnity dfUnity = DaggerfallUnity.Instance; if (defaultTextures.Count == 0) return; // Get new material array Material[] materials = new Material[defaultTextures.Count]; for (int i = 0; i < defaultTextures.Count; i++) { materials[i] = dfUnity.MaterialReader.ChangeClimate(defaultTextures[i], climate, season, windowStyle); } // Assign material array if (materials != null) GetComponent<MeshRenderer>().sharedMaterials = materials; // Store climate settings currentClimate = climate; currentSeason = season; currentWindowStyle = windowStyle; }
/// <summary> /// Set density for Summer. /// </summary> public void SetDensitySummer(Terrain terrain, Color32[] tilemap, ClimateBases currentClimate) { float seasonalDetailsChance = GetGrassDetailsChance(); bool isNight = DaggerfallUnity.Instance.WorldTime.Now.IsNight; bool shores = currentClimate != ClimateBases.Mountain; for (int y = 0; y < tilemapSize; y++) { for (int x = 0; x < tilemapSize; x++) { switch (tilemap[(y * tilemapSize) + x].r) { case 4: case 5: case 6: case 7: if (terrainStones) { int rocks = RandomRocks(); if (rocks != 0) { var index = RandomPosition(y, x); Rocks[index.First, index.Second] = rocks; } } break; // Four corner tiles case 8: case 9: case 10: case 11: SetGrassDensity(y * 2, x * 2, RandomThick(), seasonalDetailsChance); SetGrassDensity(y * 2, (x * 2) + 1, RandomThick(), seasonalDetailsChance); SetGrassDensity((y * 2) + 1, x * 2, RandomThick(), seasonalDetailsChance); SetGrassDensity((y * 2) + 1, (x * 2) + 1, RandomThick(), seasonalDetailsChance); if (flowers) { int commonFlowers = RandomFlowers(); if (commonFlowers != 0) { var index = RandomPosition(y, x); Flowers[index.First, index.Second] = commonFlowers; } } if (terrainStones) { int rocks = RandomRocks(); if (rocks != 0) { var index = RandomPosition(y, x); Rocks[index.First, index.Second] = rocks; } } break; // Upper left corner case 40: case 224: case 164: case 176: case 181: Grass[(y * 2) + 1, x * 2] = RandomThin(); break; // Lower left corner case 41: case 221: case 165: case 177: case 182: Grass[y * 2, x * 2] = RandomThin(); break; // Lower right corner case 42: case 222: case 166: case 178: case 183: Grass[y * 2, (x * 2) + 1] = RandomThin(); break; // Upper right corner case 43: case 223: case 167: case 179: case 180: Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); break; // Left side case 44: case 66: case 160: case 168: Grass[(y * 2) + 1, x * 2] = RandomThin(); Grass[y * 2, x * 2] = RandomThin(); break; // Left side: grass and plants case 84: Grass[(y * 2) + 1, x * 2] = RandomThin(); Grass[y * 2, x * 2] = RandomThin(); if (waterPlants && shores) { WaterPlants[(y * 2) + 1, x * 2] = RandomWaterPlants(); WaterPlants[y * 2, x * 2] = RandomWaterPlants(); } if (terrainStones && Random.value < 0.3f) { Rocks[(y * 2) + 1, x * 2] = Random.Range(0, 4); Rocks[y * 2, x * 2] = Random.Range(0, 4); } break; // Lower side case 45: case 67: case 161: case 169: Grass[y * 2, (x * 2) + 1] = RandomThin(); Grass[y * 2, x * 2] = RandomThin(); break; // Lower side: grass and plants case 85: Grass[y * 2, (x * 2) + 1] = RandomThin(); Grass[y * 2, x * 2] = RandomThin(); if (waterPlants && shores) { WaterPlants[y * 2, (x * 2) + 1] = RandomWaterPlants(); WaterPlants[y * 2, (x * 2)] = RandomWaterPlants(); } if (terrainStones && Random.value < 0.3f) { Rocks[y * 2, (x * 2) + 1] = Random.Range(0, 4); Rocks[y * 2, (x * 2)] = Random.Range(0, 4); } break; // Right side case 46: case 64: case 162: case 170: Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); Grass[y * 2, (x * 2) + 1] = RandomThin(); break; // Right side: grass and plants case 86: Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); Grass[y * 2, (x * 2) + 1] = RandomThin(); if (waterPlants && shores) { WaterPlants[(y * 2) + 1, (x * 2) + 1] = RandomWaterPlants(); WaterPlants[y * 2, (x * 2) + 1] = RandomWaterPlants(); } if (terrainStones && Random.value < 0.3f) { Rocks[(y * 2) + 1, (x * 2) + 1] = Random.Range(0, 4); Rocks[y * 2, (x * 2) + 1] = Random.Range(0, 4); } break; // Upper side case 47: case 65: case 163: case 171: Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); Grass[(y * 2) + 1, x * 2] = RandomThin(); break; // Upper side: grass and plants case 87: Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); Grass[(y * 2) + 1, x * 2] = RandomThin(); if (waterPlants && shores) { WaterPlants[(y * 2) + 1, (x * 2) + 1] = RandomWaterPlants(); WaterPlants[(y * 2) + 1, x * 2] = RandomWaterPlants(); } if (terrainStones && Random.value < 0.3f) { Rocks[(y * 2) + 1, (x * 2) + 1] = Random.Range(0, 4); Rocks[(y * 2) + 1, x * 2] = Random.Range(0, 4); } break; // All expect lower right case 48: case 62: case 156: Grass[y * 2, x * 2] = RandomThin(); Grass[(y * 2) + 1, x * 2] = RandomThin(); Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); break; // All expect lower right: grass and plants case 88: Grass[y * 2, x * 2] = RandomThin(); Grass[(y * 2) + 1, x * 2] = RandomThin(); Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); if (waterPlants && shores) { WaterPlants[y * 2, x * 2] = RandomWaterPlants(); WaterPlants[(y * 2) + 1, (x * 2) + 1] = RandomWaterPlants(); } break; // All expect upper right case 49: case 63: case 157: Grass[y * 2, x * 2] = RandomThin(); Grass[y * 2, (x * 2) + 1] = RandomThin(); Grass[(y * 2) + 1, x * 2] = RandomThin(); break; // All expect upper right: grass and plants case 89: Grass[y * 2, x * 2] = RandomThin(); Grass[y * 2, (x * 2) + 1] = RandomThin(); Grass[(y * 2) + 1, x * 2] = RandomThin(); if (waterPlants && shores) { WaterPlants[y * 2, (x * 2) + 1] = RandomWaterPlants(); WaterPlants[(y * 2) + 1, x * 2] = RandomWaterPlants(); } break; // All expect upper left case 50: case 60: case 158: Grass[y * 2, x * 2] = RandomThin(); Grass[y * 2, (x * 2) + 1] = RandomThin(); Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); break; // All expect upper left: grass and plants case 90: Grass[y * 2, x * 2] = RandomThin(); Grass[y * 2, (x * 2) + 1] = RandomThin(); Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); if (waterPlants && shores) { WaterPlants[y * 2, x * 2] = RandomWaterPlants(); WaterPlants[(y * 2) + 1, (x * 2) + 1] = RandomWaterPlants(); } break; // All expect lower left case 51: case 61: case 159: Grass[y * 2, (x * 2) + 1] = RandomThin(); Grass[(y * 2) + 1, x * 2] = RandomThin(); Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); break; // All expect lower left: grass and plants case 91: Grass[y * 2, (x * 2) + 1] = RandomThin(); Grass[(y * 2) + 1, x * 2] = RandomThin(); Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); if (waterPlants && shores) { WaterPlants[y * 2, (x * 2) + 1] = RandomWaterPlants(); WaterPlants[(y * 2) + 1, x * 2] = RandomWaterPlants(); } break; // Left to right case 204: case 206: case 214: Grass[y * 2, x * 2] = RandomThin(); Grass[(y * 2) + 1, (x * 2) + 1] = RandomThin(); break; // Right to left case 205: case 207: case 213: Grass[(y * 2) + 1, x * 2] = RandomThin(); Grass[y * 2, (x * 2) + 1] = RandomThin(); break; // Swamp upper right corner case 81: if (waterPlants && shores) { WaterPlants[(y * 2), (x * 2)] = RandomWaterPlants(); } break; // Swamp lower left corner case 83: if (waterPlants && shores) { WaterPlants[(y * 2) + 1, (x * 2) + 1] = RandomWaterPlants(); } break; // In-water grass // case 0 is not enabled because is used for the sea case 1: case 2: case 3: if (waterPlants && Random.value < 0.6f) { switch (currentClimate) { case ClimateBases.Mountain: WaterPlants[(y * 2) + 1, (x * 2) + 1] = Random.Range(5, 10); break; case ClimateBases.Swamp: WaterPlants[(y * 2) + 1, (x * 2) + 1] = Random.Range(0, 3); WaterPlants[(y * 2), (x * 2) + 1] = Random.Range(0, 3); WaterPlants[(y * 2) + 1, (x * 2)] = Random.Range(0, 3); WaterPlants[(y * 2), (x * 2)] = Random.Range(0, 3); break; case ClimateBases.Temperate: WaterPlants[y * 2, x * 2] = Random.Range(2, 5); WaterPlants[(y * 2) + 1, (x * 2) + 1] = Random.Range(2, 5); WaterPlants[(y * 2) + 1, x * 2] = Random.Range(2, 5); WaterPlants[y * 2, (x * 2) + 1] = Random.Range(2, 5); break; } } // Insects if (RealGrass.Instance.FlyingInsects && Random.value < 0.2f) { RealGrass.Instance.DoInsects(isNight, GetTileWorldPosition(terrain, x, y)); } break; case 116: case 117: case 118: case 119: if (waterPlants && currentClimate == ClimateBases.Swamp) { WaterPlants[(y * 2) + 1, (x * 2) + 1] = Random.Range(0, 2); WaterPlants[(y * 2), (x * 2) + 1] = Random.Range(0, 2); WaterPlants[(y * 2) + 1, (x * 2)] = Random.Range(0, 2); WaterPlants[(y * 2), (x * 2)] = Random.Range(0, 2); } break; // Little stones case 216: case 217: case 218: case 219: if (terrainStones && Random.value > 0.8f) { Rocks[y * 2, x * 2] = Random.Range(1, 3); Rocks[(y * 2) + 1, (x * 2) + 1] = Random.Range(1, 3); } break; } if (RealGrass.Instance.FlyingInsects && Random.value < 0.001f) { RealGrass.Instance.DoInsects(isNight, GetTileWorldPosition(terrain, x, y)); } } } }
/// <summary> /// Converts an archive index to new climate and season. /// Will return same index if climate or season not supported. /// </summary> /// <param name="archive">Archive index of starting texture.</param> /// <param name="climate">Climate base to apply.</param> /// <param name="season">Climate season to apply</param> /// <returns>Archive index of new texture.</returns> public static int ApplyClimate(int archive, int record, ClimateBases climate, ClimateSeason season) { // Get climate texture info ClimateTextureInfo ci = ClimateSwaps.GetClimateTextureInfo(archive); // Ignore non-climate textures if (ci.textureSet == DFLocation.ClimateTextureSet.None) return archive; // Handle missing Swamp textures if (climate == ClimateBases.Swamp) { switch (ci.textureSet) { case DFLocation.ClimateTextureSet.Interior_TempleInt: case DFLocation.ClimateTextureSet.Interior_MarbleFloors: return archive; } } // Bypass winter swaps in desert climates entirely // There are too many bad swaps, and you never see this variant in game if (climate == ClimateBases.Desert) ci.supportsWinter = false; // Handle swamp climate sets with missing winter textures if (climate == ClimateBases.Swamp) { switch (ci.textureSet) { case DFLocation.ClimateTextureSet.Exterior_Castle: case DFLocation.ClimateTextureSet.Exterior_MagesGuild: ci.supportsWinter = false; break; } } // Handle archives with missing winter textures if (archive == 82 && record > 1 || archive == 77) { ci.supportsWinter = false; } // Flag to suppress climate index // Certain textures have a winter variant but are climate-specific bool suppressClimateIndex = false; switch (archive) { case 75: case 76: case 77: case 79: case 80: case 82: case 83: suppressClimateIndex = true; break; } // Calculate new index int climateIndex = 0; if (archive < 500 && !suppressClimateIndex) { climateIndex = (int)FromUnityClimateBase(climate) + (int)ci.textureSet; if (season == ClimateSeason.Winter && ci.supportsWinter) climateIndex += (int)DFLocation.ClimateWeather.Winter; else if (season == ClimateSeason.Rain && ci.supportsRain) climateIndex += (int)DFLocation.ClimateWeather.Rain; } else { climateIndex = archive; if (season == ClimateSeason.Winter && ci.supportsWinter) climateIndex += (int)DFLocation.ClimateWeather.Winter; } return climateIndex; }
/// <summary> /// Get ground archive based on climate. /// </summary> /// <param name="climateBase">Climate base.</param> /// <param name="climateSeason">Season.</param> /// <returns>Ground archive matching climate and season.</returns> public static int GetGroundArchive(ClimateBases climateBase, ClimateSeason climateSeason) { // Apply climate int archive; switch (climateBase) { case ClimateBases.Desert: archive = 2; break; case ClimateBases.Mountain: archive = 102; break; case ClimateBases.Temperate: archive = 302; break; case ClimateBases.Swamp: archive = 402; break; default: archive = 302; break; } // Modify for season switch (climateSeason) { case ClimateSeason.Winter: archive += 1; break; case ClimateSeason.Rain: archive += 2; break; } return archive; }