private string GetBiomeArea(Heightmap.BiomeArea biomeArea) { StringBuilder biomeAreas = new StringBuilder("<ul>"); foreach (Heightmap.BiomeArea area in Enum.GetValues(typeof(Heightmap.BiomeArea))) { if (area == Heightmap.BiomeArea.Everything || (biomeArea & area) == 0) { continue; } biomeAreas.Append($"<li>{area}</li>"); } biomeAreas.Append("</ul>"); return(biomeAreas.ToString()); }
static bool FindNewSpawnPoint(Heightmap.Biome requestedBiome, Heightmap.BiomeArea requestedBiomeArea, float minXDistance, float maxXDistance, float minZDistance, float maxZDistance, bool ignoreWaterDepthCheck, bool IgnoreWorldGeneratorConstraints, int MaxSpawnPointChecks, out Vector3 point) { Vector3 tempNewSpawnPosition = Vector3.zero; bool found = false; int x = 0; float y = 0; int z = 0; Vector3 tempLocation = new Vector3(x, y, z); // for maxDistance to the worldSize value... float maxDistance = WorldGenerator.worldSize; float minDistance = 0; // if not ignoring WorldGenerator constraints on biomes, then enforce them if (IgnoreWorldGeneratorConstraints) { Plugin.Log("Ignoring WorldGenerator Min/Max Constraints"); } else { switch (requestedBiome) { case Heightmap.Biome.AshLands: minDistance = WorldGenerator.ashlandsMinDistance; break; case Heightmap.Biome.BlackForest: maxDistance = WorldGenerator.maxDeepForestDistance; minDistance = WorldGenerator.minDeepForestDistance; break; case Heightmap.Biome.DeepNorth: minDistance = WorldGenerator.deepNorthMinDistance; break; case Heightmap.Biome.Meadows: maxDistance = WorldGenerator.meadowsMaxDistance; break; case Heightmap.Biome.Mountain: minDistance = WorldGenerator.m_instance.m_minMountainDistance; break; case Heightmap.Biome.Swamp: maxDistance = WorldGenerator.maxMarshDistance; minDistance = WorldGenerator.minMarshDistance; break; } } // make sure max is less than max distance and more than minX float maxX = UtilityClass.Clamp(maxXDistance, minXDistance, maxDistance); float maxZ = UtilityClass.Clamp(maxZDistance, minZDistance, maxDistance); // make sure Min#Distance > 0 float minX = (minXDistance > 0) ? minXDistance : 0; float minZ = (minZDistance > 0) ? minZDistance : 0; // clamp minDinstance between actual minDistance for biome and maxdistance minX = UtilityClass.Clamp(minX, minDistance, maxDistance); minZ = UtilityClass.Clamp(minZ, minDistance, maxDistance); if (maxXDistance != maxX) { Plugin.LogVerbose($"Modified MaxXDistance from {maxXDistance} to {maxX}"); } if (maxZDistance != maxZ) { Plugin.LogVerbose($"Modified MaxZDistance from {maxZDistance} to {maxZ}"); } if (minXDistance != minX) { Plugin.LogVerbose($"Modified MinXDistance from {minXDistance} to {minX}"); } if (minZDistance != minZ) { Plugin.LogVerbose($"Modified MinZDistance from {minZDistance} to {minZ}"); } Plugin.Log($"Looking for Biome: {requestedBiome} - Type {requestedBiomeArea}"); Plugin.Log($"Looking minX Distance: {minX}, maxX Distance:{maxX}"); Plugin.Log($"Looking minZ Distance: {minZ}, maxZ Distance:{maxZ}"); int spawnPointCheckCount = 0; while (tempNewSpawnPosition == Vector3.zero && spawnPointCheckCount <= MaxSpawnPointChecks) { spawnPointCheckCount++; // set this as our bounds x = UnityEngine.Random.Range((int)Math.Round(minX), (int)Math.Round(maxX)); z = UnityEngine.Random.Range((int)Math.Round(minZ), (int)Math.Round(maxZ)); // using the range above, now randomly decide to to switch one to negative or leave positive. This provides a potential range of distance in the spawn away from 0,0 // Int based Random.Range the max is exclusive (unlike float) if (UnityEngine.Random.Range(1, 3) == 1) { x = -x; } if (UnityEngine.Random.Range(1, 3) == 1) { z = -z; } Plugin.LogVerbose($"SpawnCheckCount: {spawnPointCheckCount}, X:{x} , z:{z}"); // note: In Vector3 X is LEFT to RIGHT on map? Y is HEIGHT and Z is TOP/BOTTOM??? tempLocation.x = x; tempLocation.z = z; Heightmap.Biome foundBiome = Heightmap.Biome.None; Heightmap.BiomeArea foundBiomeArea = Heightmap.BiomeArea.Everything; //try to find a valid height line... float height = WorldGenerator.instance.GetHeight(tempLocation.x, tempLocation.z); Plugin.LogVerbose($"Initial coords: {tempLocation}"); // only search here if the height is heigher than the water level as it still has a tendency to pull other biomes if (height > ZoneSystem.instance.m_waterLevel || ignoreWaterDepthCheck) // && height < WorldGenerator.mountainBaseHeightMin * 85) { tempLocation.y = height; //This is less churn foundBiome = WorldGenerator.instance.GetBiome(tempLocation); foundBiomeArea = WorldGenerator.instance.GetBiomeArea(tempLocation); Plugin.Log($"found {foundBiome}, {foundBiomeArea} @ {tempLocation}"); // match on the returned biome type unless the player didn't care and chose Everything // match on biome and biometype but don't match on actual biome of None.... // Settings.Biome.Value.None will match on anything, Settings.biome.BiomesMax will match on anything if (foundBiome != Heightmap.Biome.None && (foundBiome == requestedBiome || Settings.Biome.Value == Heightmap.Biome.None || Settings.Biome.Value == Heightmap.Biome.BiomesMax) && (foundBiomeArea == requestedBiomeArea || Settings.BiomeAreaType.Value == Heightmap.BiomeArea.Everything)) { Plugin.Log($"found new spawnpoint {foundBiome} @ {tempLocation}"); tempNewSpawnPosition = tempLocation; found = true; } } } point = tempNewSpawnPosition; return(found); }
static bool Prefix(out Vector3 point, out bool usedLogoutPoint, float dt, Game __instance, ref bool __result) { Plugin.Log("FindSpawnPoint_Patch"); point = Vector3.zero; usedLogoutPoint = false; try { UnityEngine.Random.InitState(DateTime.Now.Year + DateTime.Now.Day + DateTime.Now.Month + DateTime.Now.Millisecond); // let the original code run if (!Settings.MMRandomStartPositionEnabled.Value) { return(true); } Plugin.LogVerbose("Does Player HaveLogoutPoint?"); if (__instance.m_playerProfile.HaveLogoutPoint()) { return(true); } Plugin.LogVerbose("Does Player HaveCustomSpawnPoint?"); if (__instance.m_playerProfile.HaveCustomSpawnPoint() && !Settings.RandomSpawnOnDeath.Value) { return(true); } // get the HomePoint for the player. It's Vector3.Zero by default Plugin.LogVerbose("Does Player GetHomePoint?"); if (!Settings.RandomSpawnOnDeath.Value) { spawnPoint = __instance.m_playerProfile.GetHomePoint(); } Plugin.LogVerbose($"HomePoint is {spawnPoint}"); // if we've executed on this more than MaxSpawnPointChecks times, just return true if (SpawnCheckCount > Settings.MaxSpawnPointChecks.Value) { Plugin.Log($"Breaking out, SpawnCheckCount is {SpawnCheckCount}"); return(true); } if (spawnPoint == Vector3.zero) { int x = 0; float y = 0; int z = 0; Vector3 tempLocation = new Vector3(x, y, z); // meadows won't spawn out farther than this distance from the middle of the map so no point supporting a number higher than this float maxDistance = WorldGenerator.worldSize; float minDistance = 0; // if not ignoring WorldGenerator constraints on biomes, then enforce them if (Settings.IgnoreWorldGeneratorConstraints.Value) { Plugin.Log("Ignoring WorldGenerator Min/Max Constraints"); } else { switch (Settings.Biome.Value) { case Heightmap.Biome.AshLands: minDistance = WorldGenerator.ashlandsMinDistance; break; case Heightmap.Biome.BlackForest: maxDistance = WorldGenerator.maxDeepForestDistance; minDistance = WorldGenerator.minDeepForestDistance; break; case Heightmap.Biome.DeepNorth: minDistance = WorldGenerator.deepNorthMinDistance; break; case Heightmap.Biome.Meadows: maxDistance = WorldGenerator.meadowsMaxDistance; break; case Heightmap.Biome.Mountain: minDistance = WorldGenerator.m_instance.m_minMountainDistance; break; case Heightmap.Biome.Swamp: maxDistance = WorldGenerator.maxMarshDistance; minDistance = WorldGenerator.minMarshDistance; break; } } // make sure max is less than max distance and more than minX float maxX = UtilityClass.Clamp(Settings.MaxXDistance.Value, Settings.MinXDistance.Value, maxDistance); float maxZ = UtilityClass.Clamp(Settings.MaxZDistance.Value, Settings.MinZDistance.Value, maxDistance); // make sure Min#Distance > 0 float minX = (Settings.MinXDistance.Value > 0) ? Settings.MinXDistance.Value : 0; float minZ = (Settings.MinZDistance.Value > 0) ? Settings.MinZDistance.Value : 0; // clamp minDinstance between actual minDistance for biome and maxdistance minX = UtilityClass.Clamp(minX, minDistance, maxDistance); minZ = UtilityClass.Clamp(minZ, minDistance, maxDistance); if (Settings.MaxXDistance.Value != maxX) { Plugin.LogVerbose($"Modified MaxXDistance from {Settings.MaxXDistance.Value} to {maxX}"); } if (Settings.MaxZDistance.Value != maxZ) { Plugin.LogVerbose($"Modified MaxZDistance from {Settings.MaxZDistance.Value} to {maxZ}"); } if (Settings.MinXDistance.Value != minX) { Plugin.LogVerbose($"Modified MinXDistance from {Settings.MinXDistance.Value} to {minX}"); } if (Settings.MinZDistance.Value != minZ) { Plugin.LogVerbose($"Modified MinZDistance from {Settings.MinZDistance.Value} to {minZ}"); } Plugin.Log($"Looking for Biome: {Settings.Biome.Value} - Type {Settings.BiomeAreaType.Value}"); Plugin.Log($"Looking minX Distance: {minX}, maxX Distance:{maxX}"); Plugin.Log($"Looking minZ Distance: {minZ}, maxZ Distance:{maxZ}"); while (spawnPoint == Vector3.zero) { if (SpawnCheckCount > Settings.MaxSpawnPointChecks.Value) { Plugin.Log($"Breaking out SpawnCheck is {SpawnCheckCount}"); return(true); } SpawnCheckCount++; // set this as our bounds x = UnityEngine.Random.Range((int)Math.Round(minX), (int)Math.Round(maxX)); z = UnityEngine.Random.Range((int)Math.Round(minZ), (int)Math.Round(maxZ)); // using the range above, now randomly decide to to switch one to negative or leave positive. This provides a potential range of distance in the spawn away from 0,0 // Int based Random.Range the max is exclusive (unlike float) if (UnityEngine.Random.Range(1, 3) == 1) { x = -x; } if (UnityEngine.Random.Range(1, 3) == 1) { z = -z; } Plugin.LogVerbose($"SpawnCheckCount: {SpawnCheckCount}, X:{x} , z:{z}"); // note: In Vector3 X is LEFT to RIGHT on map? Y is HEIGHT and Z is TOP/BOTTOM??? tempLocation.x = x; tempLocation.z = z; Heightmap.Biome biome = Heightmap.Biome.None; Heightmap.BiomeArea biomeArea = Heightmap.BiomeArea.Everything; //try to find a valid height line... float height = WorldGenerator.instance.GetHeight(tempLocation.x, tempLocation.z); Plugin.LogVerbose($"Initial coords: {tempLocation}"); // only search here if the height is heigher than the water level as it still has a tendency to pull other biomes if (height > ZoneSystem.instance.m_waterLevel || Settings.IgnoreWaterDepthCheck.Value) // && height < WorldGenerator.mountainBaseHeightMin * 85) { tempLocation.y = height; //This is less churn biome = WorldGenerator.instance.GetBiome(tempLocation); biomeArea = WorldGenerator.instance.GetBiomeArea(tempLocation); Plugin.Log($"found {biome}, {biomeArea} @ {tempLocation}"); // match on the returned biome type unless the player didn't care and chose Everything // match on biome and biometype but don't match on actual biome of None.... // Settings.Biome.Value.None will match on anything, Settings.biome.BiomesMax will match on anything if (biome != Heightmap.Biome.None && (biome == Settings.Biome.Value || Settings.Biome.Value == Heightmap.Biome.None || Settings.Biome.Value == Heightmap.Biome.BiomesMax) && (biomeArea == Settings.BiomeAreaType.Value || Settings.BiomeAreaType.Value == Heightmap.BiomeArea.Everything)) { Plugin.Log($"found new spawnpoint {biome} @ {tempLocation}"); spawnPoint = tempLocation; __instance.m_playerProfile.SetHomePoint(spawnPoint + Vector3.up * 2f); // register a starter location nearby // just a method to dump these starter locations out when verbose is enabled and only then if (Settings.PluginLoggingLevel.Value == Plugin.LoggingLevel.Verbose) { Plugin.LogVerbose("Dumping Zones Coordinates"); foreach (var zone in ZoneSystem.instance.m_generatedZones) { Plugin.LogVerbose($"Zone Vector2i: {zone}"); Plugin.LogVerbose($" Zone Position: {ZoneSystem.instance.GetZonePos(zone)}"); } Plugin.LogVerbose("Dumping ZoneLocation Names"); foreach (ZoneSystem.ZoneLocation item in ZoneSystem.instance.m_locations) { Plugin.LogVerbose($"ZoneLocation Name: {item.m_prefabName}"); //if (item.m_prefabName == "StartTemple") if (item.m_prefabName == "Eikthyrnir" || item.m_prefabName == "StartTemple") { //Remove previous locations of this? //ZoneSystem.instance.RemoveUnplacedLocations(item); //item.m_unique = false; //ZoneSystem.instance.RegisterLocation(item, spawnPoint, true); } } } } } } } point = spawnPoint + Vector3.up * 2f; ZNet.instance.SetReferencePosition(point); __result = ZNetScene.instance.IsAreaReady(point); Plugin.Log($"Waiting for area to be ready. Spawnpoint {point}"); return(false); } catch (Exception ex) { // do nothing, just swallow it up Plugin.LogError(ex.ToString()); } return(true); }