38f1ddb...178a853: v0.8.9.1, removed content folder
This commit is contained in:
@@ -1,64 +1,134 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Location
|
||||
{
|
||||
private string name;
|
||||
|
||||
private Vector2 mapPosition;
|
||||
|
||||
private LocationType type;
|
||||
|
||||
public List<LocationConnection> Connections;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
public Vector2 MapPosition
|
||||
{
|
||||
get { return mapPosition; }
|
||||
}
|
||||
private string baseName;
|
||||
private int nameFormatIndex;
|
||||
|
||||
public bool Discovered;
|
||||
|
||||
public LocationType Type
|
||||
|
||||
public int TypeChangeTimer;
|
||||
|
||||
public string Name { get; private set; }
|
||||
|
||||
public Vector2 MapPosition { get; private set; }
|
||||
|
||||
public LocationType Type { get; private set; }
|
||||
|
||||
public int PortraitId { get; private set; }
|
||||
|
||||
public int MissionsCompleted;
|
||||
|
||||
private List<Mission> availableMissions = new List<Mission>();
|
||||
public IEnumerable<Mission> AvailableMissions
|
||||
{
|
||||
get { return type; }
|
||||
get
|
||||
{
|
||||
CheckMissionCompleted();
|
||||
|
||||
for (int i = availableMissions.Count; i < Connections.Count * 2; i++)
|
||||
{
|
||||
int seed = (ToolBox.StringToInt(Name) + MissionsCompleted * 10 + i) % int.MaxValue;
|
||||
MTRandom rand = new MTRandom(seed);
|
||||
|
||||
LocationConnection connection = Connections[(MissionsCompleted + i) % Connections.Count];
|
||||
Location destination = connection.OtherLocation(this);
|
||||
|
||||
var mission = Mission.LoadRandom(new Location[] { this, destination }, rand, true, MissionType.Random, true);
|
||||
if (mission == null) { continue; }
|
||||
if (availableMissions.Any(m => m.Prefab == mission.Prefab)) { continue; }
|
||||
if (GameSettings.VerboseLogging && mission != null)
|
||||
{
|
||||
DebugConsole.NewMessage("Generated a new mission for a location connection (seed: " + seed.ToString("X") + ", type: " + mission.Name + ")", Color.White);
|
||||
}
|
||||
availableMissions.Add(mission);
|
||||
}
|
||||
|
||||
return availableMissions;
|
||||
}
|
||||
}
|
||||
|
||||
public Location(Vector2 mapPosition)
|
||||
public Mission SelectedMission
|
||||
{
|
||||
this.type = LocationType.Random();
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
this.name = RandomName(type);
|
||||
|
||||
this.mapPosition = mapPosition;
|
||||
|
||||
#if CLIENT
|
||||
if (type.HasHireableCharacters)
|
||||
public int SelectedMissionIndex
|
||||
{
|
||||
get { return availableMissions.IndexOf(SelectedMission); }
|
||||
set
|
||||
{
|
||||
hireManager = new HireManager();
|
||||
hireManager.GenerateCharacters(this, HireManager.MaxAvailableCharacters);
|
||||
if (value < 0 || value >= AvailableMissions.Count())
|
||||
{
|
||||
SelectedMission = null;
|
||||
return;
|
||||
}
|
||||
SelectedMission = availableMissions[value];
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public Location(Vector2 mapPosition, int? zone)
|
||||
{
|
||||
this.Type = LocationType.Random("", zone);
|
||||
this.Name = RandomName(Type);
|
||||
this.MapPosition = mapPosition;
|
||||
|
||||
PortraitId = ToolBox.StringToInt(Name);
|
||||
|
||||
Connections = new List<LocationConnection>();
|
||||
}
|
||||
|
||||
public static Location CreateRandom(Vector2 position)
|
||||
public static Location CreateRandom(Vector2 position, int? zone)
|
||||
{
|
||||
return new Location(position);
|
||||
return new Location(position, zone);
|
||||
}
|
||||
|
||||
public IEnumerable<Mission> GetMissionsInConnection(LocationConnection connection)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(Connections.Contains(connection));
|
||||
return AvailableMissions.Where(m => m.Locations[1] == connection.OtherLocation(this));
|
||||
}
|
||||
|
||||
public void ChangeType(LocationType newType)
|
||||
{
|
||||
if (newType == Type) return;
|
||||
|
||||
Type = newType;
|
||||
Name = Type.NameFormats[nameFormatIndex % Type.NameFormats.Count].Replace("[name]", baseName);
|
||||
}
|
||||
|
||||
public void CheckMissionCompleted()
|
||||
{
|
||||
foreach (Mission mission in availableMissions)
|
||||
{
|
||||
if (mission.Completed)
|
||||
{
|
||||
MissionsCompleted++;
|
||||
}
|
||||
}
|
||||
|
||||
availableMissions.RemoveAll(m => m.Completed);
|
||||
}
|
||||
|
||||
private string RandomName(LocationType type)
|
||||
{
|
||||
string randomName = ToolBox.GetRandomLine("Content/Map/locationNames.txt");
|
||||
int nameFormatIndex = Rand.Int(type.NameFormats.Count, Rand.RandSync.Server);
|
||||
return type.NameFormats[nameFormatIndex].Replace("[name]", randomName);
|
||||
baseName = type.GetRandomName();
|
||||
nameFormatIndex = Rand.Int(type.NameFormats.Count, Rand.RandSync.Server);
|
||||
return type.NameFormats[nameFormatIndex].Replace("[name]", baseName);
|
||||
}
|
||||
|
||||
public void Remove()
|
||||
{
|
||||
RemoveProjSpecific();
|
||||
}
|
||||
|
||||
partial void RemoveProjSpecific();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class LocationConnection
|
||||
{
|
||||
private Location[] locations;
|
||||
private Level level;
|
||||
|
||||
public Biome Biome;
|
||||
|
||||
public float Difficulty;
|
||||
|
||||
public List<Vector2[]> CrackSegments;
|
||||
|
||||
public bool Passed;
|
||||
|
||||
public Level Level
|
||||
{
|
||||
get { return level; }
|
||||
set { level = value; }
|
||||
}
|
||||
|
||||
public Vector2 CenterPos
|
||||
{
|
||||
get
|
||||
{
|
||||
return (locations[0].MapPosition + locations[1].MapPosition) / 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public Location[] Locations
|
||||
{
|
||||
get { return locations; }
|
||||
}
|
||||
|
||||
public float Length
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public LocationConnection(Location location1, Location location2)
|
||||
{
|
||||
locations = new Location[] { location1, location2 };
|
||||
|
||||
Length = Vector2.Distance(location1.MapPosition, location2.MapPosition);
|
||||
}
|
||||
|
||||
public Location OtherLocation(Location location)
|
||||
{
|
||||
if (locations[0] == location)
|
||||
{
|
||||
return locations[1];
|
||||
}
|
||||
else if (locations[1] == location)
|
||||
{
|
||||
return locations[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -9,28 +11,26 @@ namespace Barotrauma
|
||||
{
|
||||
class LocationType
|
||||
{
|
||||
private static List<LocationType> list = new List<LocationType>();
|
||||
//sum of the commonness-values of each location type
|
||||
private static int totalWeight;
|
||||
|
||||
private string name;
|
||||
|
||||
private int commonness;
|
||||
|
||||
public static readonly List<LocationType> List = new List<LocationType>();
|
||||
|
||||
private List<string> nameFormats;
|
||||
private List<string> names;
|
||||
|
||||
private Sprite symbolSprite;
|
||||
|
||||
private Sprite backGround;
|
||||
private readonly List<Sprite> portraits = new List<Sprite>();
|
||||
|
||||
//<name, commonness>
|
||||
private List<Tuple<JobPrefab, float>> hireableJobs;
|
||||
private float totalHireableWeight;
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return name; }
|
||||
}
|
||||
public Dictionary<int, float> CommonnessPerZone = new Dictionary<int, float>();
|
||||
|
||||
public readonly string Name;
|
||||
|
||||
public readonly string DisplayName;
|
||||
|
||||
public readonly List<LocationTypeChange> CanChangeTo = new List<LocationTypeChange>();
|
||||
|
||||
public List<string> NameFormats
|
||||
{
|
||||
@@ -47,55 +47,97 @@ namespace Barotrauma
|
||||
get { return symbolSprite; }
|
||||
}
|
||||
|
||||
public Sprite Background
|
||||
public Color SpriteColor
|
||||
{
|
||||
get { return backGround; }
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
|
||||
private LocationType(XElement element)
|
||||
{
|
||||
name = element.Name.ToString();
|
||||
|
||||
commonness = element.GetAttributeInt("commonness", 1);
|
||||
totalWeight += commonness;
|
||||
|
||||
Name = element.Name.ToString();
|
||||
DisplayName = element.GetAttributeString("name", "Name");
|
||||
|
||||
nameFormats = new List<string>();
|
||||
foreach (XAttribute nameFormat in element.Element("nameformats").Attributes())
|
||||
{
|
||||
nameFormats.Add(nameFormat.Value);
|
||||
}
|
||||
|
||||
string nameFile = element.GetAttributeString("namefile", "Content/Map/locationNames.txt");
|
||||
try
|
||||
{
|
||||
names = File.ReadAllLines(nameFile).ToList();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to read name file for location type \""+Name+"\"!", e);
|
||||
names = new List<string>() { "Name file not found" };
|
||||
}
|
||||
|
||||
string[] commonnessPerZoneStrs = element.GetAttributeStringArray("commonnessperzone", new string[] { "" });
|
||||
foreach (string commonnessPerZoneStr in commonnessPerZoneStrs)
|
||||
{
|
||||
string[] splitCommonnessPerZone = commonnessPerZoneStr.Split(':');
|
||||
if (splitCommonnessPerZone.Length != 2 ||
|
||||
!int.TryParse(splitCommonnessPerZone[0].Trim(), out int zoneIndex) ||
|
||||
!float.TryParse(splitCommonnessPerZone[1].Trim(), NumberStyles.Float, CultureInfo.InvariantCulture, out float zoneCommonness))
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to read commonness values for location type \"" + Name + "\" - commonness should be given in the format \"zone0index: zone0commonness, zone1index: zone1commonness\"");
|
||||
break;
|
||||
}
|
||||
CommonnessPerZone[zoneIndex] = zoneCommonness;
|
||||
}
|
||||
|
||||
hireableJobs = new List<Tuple<JobPrefab, float>>();
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLowerInvariant() != "hireable") continue;
|
||||
|
||||
string jobName = subElement.GetAttributeString("name", "");
|
||||
|
||||
JobPrefab jobPrefab = JobPrefab.List.Find(jp => jp.Name.ToLowerInvariant() == jobName.ToLowerInvariant());
|
||||
if (jobPrefab==null)
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
DebugConsole.ThrowError("Invalid job name ("+jobName+") in location type "+name);
|
||||
case "hireable":
|
||||
string jobIdentifier = subElement.GetAttributeString("identifier", "");
|
||||
JobPrefab jobPrefab = null;
|
||||
if (jobIdentifier == "")
|
||||
{
|
||||
DebugConsole.ThrowError("Error in location type \""+ Name + "\" - hireable jobs should be configured using identifiers instead of names.");
|
||||
jobIdentifier = subElement.GetAttributeString("name", "");
|
||||
jobPrefab = JobPrefab.List.Find(jp => jp.Name.ToLowerInvariant() == jobIdentifier.ToLowerInvariant());
|
||||
}
|
||||
else
|
||||
{
|
||||
jobPrefab = JobPrefab.List.Find(jp => jp.Identifier.ToLowerInvariant() == jobIdentifier.ToLowerInvariant());
|
||||
}
|
||||
if (jobPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in in location type " + Name + " - could not find a job with the identifier \"" + jobIdentifier + "\".");
|
||||
continue;
|
||||
}
|
||||
float jobCommonness = subElement.GetAttributeFloat("commonness", 1.0f);
|
||||
totalHireableWeight += jobCommonness;
|
||||
Tuple<JobPrefab, float> hireableJob = new Tuple<JobPrefab, float>(jobPrefab, jobCommonness);
|
||||
hireableJobs.Add(hireableJob);
|
||||
break;
|
||||
case "symbol":
|
||||
symbolSprite = new Sprite(subElement);
|
||||
SpriteColor = subElement.GetAttributeColor("color", Color.White);
|
||||
break;
|
||||
case "changeto":
|
||||
CanChangeTo.Add(new LocationTypeChange(subElement));
|
||||
break;
|
||||
case "portrait":
|
||||
var portrait = new Sprite(subElement);
|
||||
if (portrait != null)
|
||||
{
|
||||
portraits.Add(portrait);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
float jobCommonness = subElement.GetAttributeFloat("commonness", 1.0f);
|
||||
totalHireableWeight += jobCommonness;
|
||||
|
||||
Tuple<JobPrefab, float> hireableJob = new Tuple<JobPrefab, float>(jobPrefab, jobCommonness);
|
||||
|
||||
hireableJobs.Add(hireableJob);
|
||||
}
|
||||
|
||||
string spritePath = element.GetAttributeString("symbol", "Content/Map/beaconSymbol.png");
|
||||
symbolSprite = new Sprite(spritePath, new Vector2(0.5f, 0.5f));
|
||||
|
||||
string backgroundPath = element.GetAttributeString("background", "");
|
||||
backGround = new Sprite(backgroundPath, Vector2.Zero);
|
||||
}
|
||||
|
||||
public JobPrefab GetRandomHireable()
|
||||
{
|
||||
float randFloat = Rand.Range(0.0f, totalHireableWeight);
|
||||
float randFloat = Rand.Range(0.0f, totalHireableWeight, Rand.RandSync.Server);
|
||||
|
||||
foreach (Tuple<JobPrefab, float> hireable in hireableJobs)
|
||||
{
|
||||
@@ -106,46 +148,61 @@ namespace Barotrauma
|
||||
return null;
|
||||
}
|
||||
|
||||
public static LocationType Random(string seed = "")
|
||||
public Sprite GetPortrait(int portraitId)
|
||||
{
|
||||
Debug.Assert(list.Count > 0, "LocationType.list.Count == 0, you probably need to initialize LocationTypes");
|
||||
if (portraits.Count == 0) { return null; }
|
||||
return portraits[Math.Abs(portraitId) % portraits.Count];
|
||||
}
|
||||
|
||||
public string GetRandomName()
|
||||
{
|
||||
return names[Rand.Int(names.Count, Rand.RandSync.Server)];
|
||||
}
|
||||
|
||||
public static LocationType Random(string seed = "", int? zone = null)
|
||||
{
|
||||
Debug.Assert(List.Count > 0, "LocationType.list.Count == 0, you probably need to initialize LocationTypes");
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(seed))
|
||||
{
|
||||
Rand.SetSyncedSeed(ToolBox.StringToInt(seed));
|
||||
}
|
||||
|
||||
int randInt = Rand.Int(totalWeight, Rand.RandSync.Server);
|
||||
List<LocationType> allowedLocationTypes = zone.HasValue ? List.FindAll(lt => lt.CommonnessPerZone.ContainsKey(zone.Value)) : List;
|
||||
|
||||
foreach (LocationType type in list)
|
||||
if (allowedLocationTypes.Count == 0)
|
||||
{
|
||||
if (randInt < type.commonness) return type;
|
||||
randInt -= type.commonness;
|
||||
DebugConsole.ThrowError("Could not generate a random location type - no location types for the zone " + zone + " found!");
|
||||
}
|
||||
|
||||
return null;
|
||||
if (zone.HasValue)
|
||||
{
|
||||
return ToolBox.SelectWeightedRandom(
|
||||
allowedLocationTypes,
|
||||
allowedLocationTypes.Select(a => a.CommonnessPerZone[zone.Value]).ToList(),
|
||||
Rand.RandSync.Server);
|
||||
}
|
||||
else
|
||||
{
|
||||
return allowedLocationTypes[Rand.Int(allowedLocationTypes.Count, Rand.RandSync.Server)];
|
||||
}
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
var locationTypeFiles = GameMain.SelectedPackage.GetFilesOfType(ContentType.LocationTypes);
|
||||
|
||||
var locationTypeFiles = GameMain.Instance.GetFilesOfType(ContentType.LocationTypes);
|
||||
|
||||
foreach (string file in locationTypeFiles)
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(file);
|
||||
|
||||
if (doc==null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (doc?.Root == null) continue;
|
||||
|
||||
foreach (XElement element in doc.Root.Elements())
|
||||
{
|
||||
LocationType locationType = new LocationType(element);
|
||||
list.Add(locationType);
|
||||
List.Add(locationType);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class LocationTypeChange
|
||||
{
|
||||
public readonly string ChangeTo;
|
||||
public readonly float Probability;
|
||||
public readonly int RequiredDuration;
|
||||
|
||||
public List<string> Messages = new List<string>();
|
||||
|
||||
//the change can't happen if there's a location of the given type next to this one
|
||||
public readonly List<string> DisallowedAdjacentLocations;
|
||||
|
||||
//the change can only happen if there's at least one of the given types of locations next to this one
|
||||
public readonly List<string> RequiredAdjacentLocations;
|
||||
|
||||
public LocationTypeChange(XElement element)
|
||||
{
|
||||
ChangeTo = element.GetAttributeString("type", "");
|
||||
Probability = element.GetAttributeFloat("probability", 1.0f);
|
||||
RequiredDuration = element.GetAttributeInt("requiredduration", 0);
|
||||
|
||||
DisallowedAdjacentLocations = element.GetAttributeStringArray("disallowedadjacentlocations", new string[0]).ToList();
|
||||
RequiredAdjacentLocations = element.GetAttributeStringArray("requiredadjacentlocations", new string[0]).ToList();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLowerInvariant() == "message")
|
||||
{
|
||||
Messages.Add(subElement.GetAttributeString("text", ""));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,141 +8,207 @@ using Voronoi2;
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Map
|
||||
{
|
||||
Vector2 difficultyIncrease = new Vector2(5.0f, 10.0f);
|
||||
Vector2 difficultyCutoff = new Vector2(80.0f, 100.0f);
|
||||
|
||||
{
|
||||
private MapGenerationParams generationParams;
|
||||
|
||||
private List<Level> levels;
|
||||
|
||||
private List<Location> locations;
|
||||
|
||||
private List<LocationConnection> connections;
|
||||
|
||||
private string seed;
|
||||
private int size;
|
||||
|
||||
private Location currentLocation;
|
||||
private Location selectedLocation;
|
||||
|
||||
private LocationConnection selectedConnection;
|
||||
|
||||
public Action<Location, LocationConnection> OnLocationSelected;
|
||||
public Action<Location> OnLocationChanged;
|
||||
//from -> to
|
||||
public Action<Location, Location> OnLocationChanged;
|
||||
public Action<LocationConnection, Mission> OnMissionSelected;
|
||||
|
||||
public Location CurrentLocation
|
||||
{
|
||||
get { return currentLocation; }
|
||||
}
|
||||
public Location CurrentLocation { get; private set; }
|
||||
|
||||
public int CurrentLocationIndex
|
||||
{
|
||||
get { return locations.IndexOf(currentLocation); }
|
||||
get { return Locations.IndexOf(CurrentLocation); }
|
||||
}
|
||||
|
||||
public Location SelectedLocation
|
||||
{
|
||||
get { return selectedLocation; }
|
||||
}
|
||||
public Location SelectedLocation { get; private set; }
|
||||
|
||||
public int SelectedLocationIndex
|
||||
{
|
||||
get { return locations.IndexOf(selectedLocation); }
|
||||
get { return Locations.IndexOf(SelectedLocation); }
|
||||
}
|
||||
|
||||
public LocationConnection SelectedConnection
|
||||
public int SelectedMissionIndex
|
||||
{
|
||||
get { return selectedConnection; }
|
||||
get { return SelectedConnection == null ? -1 : CurrentLocation.SelectedMissionIndex; }
|
||||
}
|
||||
|
||||
public string Seed
|
||||
{
|
||||
get { return seed; }
|
||||
}
|
||||
public LocationConnection SelectedConnection { get; private set; }
|
||||
|
||||
public List<Location> Locations
|
||||
{
|
||||
get { return locations; }
|
||||
}
|
||||
public string Seed { get; private set; }
|
||||
|
||||
public Map(string seed, int size)
|
||||
{
|
||||
this.seed = seed;
|
||||
public List<Location> Locations { get; private set; }
|
||||
|
||||
this.size = size;
|
||||
public Map(string seed)
|
||||
{
|
||||
generationParams = MapGenerationParams.Instance;
|
||||
this.Seed = seed;
|
||||
this.size = generationParams.Size;
|
||||
|
||||
levels = new List<Level>();
|
||||
|
||||
locations = new List<Location>();
|
||||
Locations = new List<Location>();
|
||||
|
||||
connections = new List<LocationConnection>();
|
||||
|
||||
#if CLIENT
|
||||
if (iceTexture == null) iceTexture = new Sprite("Content/Map/iceSurface.png", Vector2.Zero);
|
||||
if (iceCraters == null) iceCraters = TextureLoader.FromFile("Content/Map/iceCraters.png");
|
||||
if (iceCrack == null) iceCrack = TextureLoader.FromFile("Content/Map/iceCrack.png");
|
||||
#endif
|
||||
Rand.SetSyncedSeed(ToolBox.StringToInt(this.Seed));
|
||||
|
||||
Rand.SetSyncedSeed(ToolBox.StringToInt(this.seed));
|
||||
Generate();
|
||||
|
||||
GenerateLocations();
|
||||
|
||||
currentLocation = locations[locations.Count / 2];
|
||||
currentLocation.Discovered = true;
|
||||
GenerateDifficulties(currentLocation, new List<LocationConnection>(connections), 10.0f);
|
||||
//start from the colony furthest away from the center
|
||||
float largestDist = 0.0f;
|
||||
Vector2 center = new Vector2(size, size) / 2;
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
if (location.Type.Name != "City") continue;
|
||||
float dist = Vector2.DistanceSquared(center, location.MapPosition);
|
||||
if (dist > largestDist)
|
||||
{
|
||||
largestDist = dist;
|
||||
CurrentLocation = location;
|
||||
}
|
||||
}
|
||||
|
||||
CurrentLocation.Discovered = true;
|
||||
|
||||
foreach (LocationConnection connection in connections)
|
||||
{
|
||||
connection.Level = Level.CreateRandom(connection);
|
||||
}
|
||||
|
||||
InitProjectSpecific();
|
||||
}
|
||||
|
||||
private void GenerateLocations()
|
||||
{
|
||||
Voronoi voronoi = new Voronoi(0.5f);
|
||||
partial void InitProjectSpecific();
|
||||
|
||||
public float[,] Noise;
|
||||
|
||||
List<Vector2> sites = new List<Vector2>();
|
||||
for (int i = 0; i < 100; i++)
|
||||
private void GenerateNoiseMap(int octaves, float persistence)
|
||||
{
|
||||
float z = Rand.Range(0.0f, 1.0f, Rand.RandSync.Server);
|
||||
Noise = new float[generationParams.NoiseResolution, generationParams.NoiseResolution];
|
||||
|
||||
float min = float.MaxValue, max = 0.0f;
|
||||
for (int x = 0; x < generationParams.NoiseResolution; x++)
|
||||
{
|
||||
sites.Add(new Vector2(Rand.Range(0.0f, size, Rand.RandSync.Server), Rand.Range(0.0f, size, Rand.RandSync.Server)));
|
||||
for (int y = 0; y < generationParams.NoiseResolution; y++)
|
||||
{
|
||||
Noise[x, y] = (float)PerlinNoise.OctavePerlin(
|
||||
(double)x / generationParams.NoiseResolution,
|
||||
(double)y / generationParams.NoiseResolution,
|
||||
z, generationParams.NoiseFrequency, octaves, persistence);
|
||||
min = Math.Min(Noise[x, y], min);
|
||||
max = Math.Max(Noise[x, y], max);
|
||||
}
|
||||
}
|
||||
|
||||
float radius = generationParams.NoiseResolution / 2;
|
||||
Vector2 center = Vector2.One * radius;
|
||||
float range = max - min;
|
||||
|
||||
float centerDarkenRadius = radius * generationParams.CenterDarkenRadius;
|
||||
float edgeDarkenRadius = radius * generationParams.EdgeDarkenRadius;
|
||||
for (int x = 0; x < generationParams.NoiseResolution; x++)
|
||||
{
|
||||
for (int y = 0; y < generationParams.NoiseResolution; y++)
|
||||
{
|
||||
//normalize the noise to 0-1 range
|
||||
Noise[x, y] = (Noise[x, y] - min) / range;
|
||||
|
||||
float dist = Vector2.Distance(center, new Vector2(x, y));
|
||||
if (dist < centerDarkenRadius)
|
||||
{
|
||||
float angle = (float)Math.Atan2(y - center.Y, x - center.X);
|
||||
float phase = angle * generationParams.CenterDarkenWaveFrequency + Noise[x, y] * generationParams.CenterDarkenWavePhaseNoise;
|
||||
float currDarkenRadius = centerDarkenRadius * (0.6f + (float)Math.Sin(phase) * 0.4f);
|
||||
if (dist < currDarkenRadius)
|
||||
{
|
||||
float darkenAmount = 1.0f - (dist / currDarkenRadius);
|
||||
Noise[x, y] = MathHelper.Lerp(Noise[x, y], Noise[x, y] * (1.0f - generationParams.CenterDarkenStrength), darkenAmount);
|
||||
}
|
||||
}
|
||||
if (dist > edgeDarkenRadius)
|
||||
{
|
||||
float darkenAmount = Math.Min((dist - edgeDarkenRadius) / (radius - edgeDarkenRadius), 1.0f);
|
||||
Noise[x, y] = MathHelper.Lerp(Noise[x, y], 1.0f - generationParams.EdgeDarkenStrength, darkenAmount);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void GenerateNoiseMapProjSpecific();
|
||||
|
||||
private void Generate()
|
||||
{
|
||||
connections.Clear();
|
||||
Locations.Clear();
|
||||
|
||||
GenerateNoiseMap(generationParams.NoiseOctaves, generationParams.NoisePersistence);
|
||||
|
||||
List<Vector2> sites = new List<Vector2>();
|
||||
float mapRadius = size / 2;
|
||||
Vector2 mapCenter = new Vector2(mapRadius, mapRadius);
|
||||
|
||||
float locationRadius = mapRadius * generationParams.LocationRadius;
|
||||
|
||||
for (float x = mapCenter.X - locationRadius; x < mapCenter.X + locationRadius; x += generationParams.VoronoiSiteInterval)
|
||||
{
|
||||
for (float y = mapCenter.Y - locationRadius; y < mapCenter.Y + locationRadius; y += generationParams.VoronoiSiteInterval)
|
||||
{
|
||||
float noiseVal = Noise[(int)(x / size * generationParams.NoiseResolution), (int)(y / size * generationParams.NoiseResolution)];
|
||||
if (Rand.Range(generationParams.VoronoiSitePlacementMinVal, 1.0f, Rand.RandSync.Server) <
|
||||
noiseVal * generationParams.VoronoiSitePlacementProbability)
|
||||
{
|
||||
sites.Add(new Vector2(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Voronoi voronoi = new Voronoi(0.5f);
|
||||
List<GraphEdge> edges = voronoi.MakeVoronoiGraph(sites, size, size);
|
||||
float zoneRadius = size / 2 / generationParams.DifficultyZones;
|
||||
|
||||
sites.Clear();
|
||||
foreach (GraphEdge edge in edges)
|
||||
{
|
||||
if (edge.point1 == edge.point2) continue;
|
||||
|
||||
//remove points from the edge of the map
|
||||
if (edge.point1.X == 0 || edge.point1.X == size) continue;
|
||||
if (edge.point1.Y == 0 || edge.point1.Y == size) continue;
|
||||
if (edge.point2.X == 0 || edge.point2.X == size) continue;
|
||||
if (edge.point2.Y == 0 || edge.point2.Y == size) continue;
|
||||
if (edge.Point1 == edge.Point2) continue;
|
||||
|
||||
if (Vector2.DistanceSquared(edge.Point1, mapCenter) >= locationRadius * locationRadius ||
|
||||
Vector2.DistanceSquared(edge.Point2, mapCenter) >= locationRadius * locationRadius) continue;
|
||||
|
||||
Location[] newLocations = new Location[2];
|
||||
newLocations[0] = locations.Find(l => l.MapPosition == edge.point1 || l.MapPosition == edge.point2);
|
||||
newLocations[1] = locations.Find(l => l != newLocations[0] && (l.MapPosition == edge.point1 || l.MapPosition == edge.point2));
|
||||
newLocations[0] = Locations.Find(l => l.MapPosition == edge.Point1 || l.MapPosition == edge.Point2);
|
||||
newLocations[1] = Locations.Find(l => l != newLocations[0] && (l.MapPosition == edge.Point1 || l.MapPosition == edge.Point2));
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
if (newLocations[i] != null) continue;
|
||||
|
||||
Vector2[] points = new Vector2[] { edge.point1, edge.point2 };
|
||||
Vector2[] points = new Vector2[] { edge.Point1, edge.Point2 };
|
||||
|
||||
int positionIndex = Rand.Int(1, Rand.RandSync.Server);
|
||||
|
||||
Vector2 position = points[positionIndex];
|
||||
if (newLocations[1 - i] != null && newLocations[1 - i].MapPosition == position) position = points[1 - positionIndex];
|
||||
|
||||
newLocations[i] = Location.CreateRandom(position);
|
||||
locations.Add(newLocations[i]);
|
||||
int zone = MathHelper.Clamp(generationParams.DifficultyZones - (int)Math.Floor(Vector2.Distance(position, mapCenter) / zoneRadius), 1, generationParams.DifficultyZones);
|
||||
newLocations[i] = Location.CreateRandom(position, zone);
|
||||
Locations.Add(newLocations[i]);
|
||||
}
|
||||
//int seed = (newLocations[0].GetHashCode() | newLocations[1].GetHashCode());
|
||||
connections.Add(new LocationConnection(newLocations[0], newLocations[1]));
|
||||
|
||||
var newConnection = new LocationConnection(newLocations[0], newLocations[1]);
|
||||
float centerDist = Vector2.Distance(newConnection.CenterPos, mapCenter);
|
||||
newConnection.Difficulty = MathHelper.Clamp(((1.0f - centerDist / mapRadius) * 100) + Rand.Range(-10.0f, 10.0f, Rand.RandSync.Server), 0, 100);
|
||||
|
||||
connections.Add(newConnection);
|
||||
}
|
||||
|
||||
//remove connections that are too short
|
||||
float minDistance = 50.0f;
|
||||
float minDistance = generationParams.MinConnectionDistance;
|
||||
for (int i = connections.Count - 1; i >= 0; i--)
|
||||
{
|
||||
LocationConnection connection = connections[i];
|
||||
@@ -152,7 +218,7 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
|
||||
locations.Remove(connection.Locations[0]);
|
||||
//locations.Remove(connection.Locations[0]);
|
||||
connections.Remove(connection);
|
||||
|
||||
foreach (LocationConnection connection2 in connections)
|
||||
@@ -162,12 +228,19 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
HashSet<Location> connectedLocations = new HashSet<Location>();
|
||||
foreach (LocationConnection connection in connections)
|
||||
{
|
||||
connection.Locations[0].Connections.Add(connection);
|
||||
connection.Locations[1].Connections.Add(connection);
|
||||
|
||||
connectedLocations.Add(connection.Locations[0]);
|
||||
connectedLocations.Add(connection.Locations[1]);
|
||||
}
|
||||
|
||||
//remove orphans
|
||||
Locations.RemoveAll(c => !connectedLocations.Contains(c));
|
||||
|
||||
for (int i = connections.Count - 1; i >= 0; i--)
|
||||
{
|
||||
i = Math.Min(i, connections.Count - 1);
|
||||
@@ -184,70 +257,39 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
foreach (LocationConnection connection in connections)
|
||||
{
|
||||
Vector2 start = connection.Locations[0].MapPosition;
|
||||
Vector2 end = connection.Locations[1].MapPosition;
|
||||
int generations = (int)(Math.Sqrt(Vector2.Distance(start, end) / 10.0f));
|
||||
connection.CrackSegments = MathUtils.GenerateJaggedLine(start, end, generations, 5.0f);
|
||||
float centerDist = Vector2.Distance(connection.CenterPos, mapCenter);
|
||||
connection.Difficulty = MathHelper.Clamp(((1.0f - centerDist / mapRadius) * 100) + Rand.Range(-10.0f, 10.0f, Rand.RandSync.Server), 0, 100);
|
||||
}
|
||||
|
||||
|
||||
AssignBiomes();
|
||||
|
||||
GenerateNoiseMapProjSpecific();
|
||||
}
|
||||
|
||||
private void AssignBiomes()
|
||||
{
|
||||
List<LocationConnection> biomeSeeds = new List<LocationConnection>();
|
||||
float locationRadius = size * 0.5f * generationParams.LocationRadius;
|
||||
|
||||
List<Biome> centerBiomes = LevelGenerationParams.GetBiomes().FindAll(b => b.Placement.HasFlag(Biome.MapPlacement.Center));
|
||||
if (centerBiomes.Count > 0)
|
||||
var biomes = LevelGenerationParams.GetBiomes();
|
||||
Vector2 centerPos = new Vector2(size, size) / 2;
|
||||
for (int i = 0; i < generationParams.DifficultyZones; i++)
|
||||
{
|
||||
Vector2 mapCenter = new Vector2(locations.Sum(l => l.MapPosition.X), locations.Sum(l => l.MapPosition.Y)) / locations.Count;
|
||||
foreach (Biome centerBiome in centerBiomes)
|
||||
List<Biome> allowedBiomes = biomes.FindAll(b => b.AllowedZones.Contains(generationParams.DifficultyZones - i));
|
||||
float zoneRadius = locationRadius * ((i + 1.0f) / generationParams.DifficultyZones);
|
||||
foreach (LocationConnection connection in connections)
|
||||
{
|
||||
LocationConnection closestConnection = null;
|
||||
float closestDist = float.PositiveInfinity;
|
||||
foreach (LocationConnection connection in connections)
|
||||
if (connection.Biome != null) continue;
|
||||
|
||||
if (i == generationParams.DifficultyZones - 1 ||
|
||||
Vector2.Distance(connection.Locations[0].MapPosition, centerPos) < zoneRadius ||
|
||||
Vector2.Distance(connection.Locations[1].MapPosition, centerPos) < zoneRadius)
|
||||
{
|
||||
if (connection.Biome != null) continue;
|
||||
|
||||
float dist = Vector2.Distance(connection.CenterPos, mapCenter);
|
||||
if (closestConnection == null || dist < closestDist)
|
||||
{
|
||||
closestConnection = connection;
|
||||
closestDist = dist;
|
||||
}
|
||||
connection.Biome = allowedBiomes[Rand.Range(0, allowedBiomes.Count, Rand.RandSync.Server)];
|
||||
}
|
||||
|
||||
closestConnection.Biome = centerBiome;
|
||||
biomeSeeds.Add(closestConnection);
|
||||
}
|
||||
}
|
||||
|
||||
List<Biome> edgeBiomes = LevelGenerationParams.GetBiomes().FindAll(b => b.Placement.HasFlag(Biome.MapPlacement.Edge));
|
||||
if (edgeBiomes.Count > 0)
|
||||
{
|
||||
List<LocationConnection> edges = GetMapEdges();
|
||||
foreach (LocationConnection edge in edges)
|
||||
{
|
||||
edge.Biome = edgeBiomes[Rand.Range(0, edgeBiomes.Count, Rand.RandSync.Server)];
|
||||
}
|
||||
}
|
||||
|
||||
List<Biome> randomBiomes = LevelGenerationParams.GetBiomes().FindAll(b => b.Placement.HasFlag(Biome.MapPlacement.Random));
|
||||
foreach (Biome biome in randomBiomes)
|
||||
{
|
||||
LocationConnection seed = connections[0];
|
||||
while (seed.Biome != null)
|
||||
{
|
||||
seed = connections[Rand.Range(0, connections.Count, Rand.RandSync.Server)];
|
||||
}
|
||||
seed.Biome = biome;
|
||||
biomeSeeds.Add(seed);
|
||||
}
|
||||
|
||||
ExpandBiomes(biomeSeeds);
|
||||
}
|
||||
|
||||
private void ExpandBiomes(List<LocationConnection> seeds)
|
||||
@@ -276,7 +318,7 @@ namespace Barotrauma
|
||||
|
||||
private List<LocationConnection> GetMapEdges()
|
||||
{
|
||||
List<Vector2> verts = locations.Select(l => l.MapPosition).ToList();
|
||||
List<Vector2> verts = Locations.Select(l => l.MapPosition).ToList();
|
||||
|
||||
List<Vector2> giftWrappedVerts = MathUtils.GiftWrap(verts);
|
||||
|
||||
@@ -293,95 +335,101 @@ namespace Barotrauma
|
||||
return edges;
|
||||
}
|
||||
|
||||
|
||||
private void GenerateDifficulties(Location start, List<LocationConnection> locations, float currDifficulty)
|
||||
{
|
||||
//start.Difficulty = currDifficulty;
|
||||
currDifficulty += Rand.Range(difficultyIncrease.X, difficultyIncrease.Y, Rand.RandSync.Server);
|
||||
if (currDifficulty > Rand.Range(difficultyCutoff.X, difficultyCutoff.Y, Rand.RandSync.Server)) currDifficulty = 10.0f;
|
||||
|
||||
foreach (LocationConnection connection in start.Connections)
|
||||
{
|
||||
if (!locations.Contains(connection)) continue;
|
||||
|
||||
Location nextLocation = connection.OtherLocation(start);
|
||||
locations.Remove(connection);
|
||||
|
||||
connection.Difficulty = currDifficulty;
|
||||
|
||||
GenerateDifficulties(nextLocation, locations, currDifficulty);
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveToNextLocation()
|
||||
{
|
||||
selectedConnection.Passed = true;
|
||||
Location prevLocation = CurrentLocation;
|
||||
SelectedConnection.Passed = true;
|
||||
|
||||
currentLocation = selectedLocation;
|
||||
currentLocation.Discovered = true;
|
||||
selectedLocation = null;
|
||||
CurrentLocation = SelectedLocation;
|
||||
CurrentLocation.Discovered = true;
|
||||
SelectedLocation = null;
|
||||
|
||||
OnLocationChanged?.Invoke(currentLocation);
|
||||
OnLocationChanged?.Invoke(prevLocation, CurrentLocation);
|
||||
}
|
||||
|
||||
public void SetLocation(int index)
|
||||
{
|
||||
if (index == -1)
|
||||
{
|
||||
currentLocation = null;
|
||||
CurrentLocation = null;
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < 0 || index >= locations.Count)
|
||||
if (index < 0 || index >= Locations.Count)
|
||||
{
|
||||
DebugConsole.ThrowError("Location index out of bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
currentLocation = locations[index];
|
||||
currentLocation.Discovered = true;
|
||||
Location prevLocation = CurrentLocation;
|
||||
CurrentLocation = Locations[index];
|
||||
CurrentLocation.Discovered = true;
|
||||
|
||||
OnLocationChanged?.Invoke(currentLocation);
|
||||
OnLocationChanged?.Invoke(prevLocation, CurrentLocation);
|
||||
}
|
||||
|
||||
public void SelectLocation(int index)
|
||||
{
|
||||
if (index == -1)
|
||||
{
|
||||
selectedLocation = null;
|
||||
selectedConnection = null;
|
||||
SelectedLocation = null;
|
||||
SelectedConnection = null;
|
||||
|
||||
OnLocationSelected?.Invoke(null, null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (index < 0 || index >= locations.Count)
|
||||
if (index < 0 || index >= Locations.Count)
|
||||
{
|
||||
DebugConsole.ThrowError("Location index out of bounds");
|
||||
return;
|
||||
}
|
||||
|
||||
selectedLocation = locations[index];
|
||||
selectedConnection = connections.Find(c => c.Locations.Contains(currentLocation) && c.Locations.Contains(selectedLocation));
|
||||
OnLocationSelected?.Invoke(selectedLocation, selectedConnection);
|
||||
SelectedLocation = Locations[index];
|
||||
SelectedConnection = connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation));
|
||||
OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection);
|
||||
}
|
||||
|
||||
public void SelectLocation(Location location)
|
||||
{
|
||||
if (!locations.Contains(location))
|
||||
if (!Locations.Contains(location))
|
||||
{
|
||||
DebugConsole.ThrowError("Failed to select a location. "+location.Name+" not found in the map.");
|
||||
string errorMsg = "Failed to select a location. " + (location?.Name ?? "null") + " not found in the map.";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Map.SelectLocation:LocationNotFound", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return;
|
||||
}
|
||||
|
||||
selectedLocation = location;
|
||||
selectedConnection = connections.Find(c => c.Locations.Contains(currentLocation) && c.Locations.Contains(selectedLocation));
|
||||
OnLocationSelected?.Invoke(selectedLocation, selectedConnection);
|
||||
SelectedLocation = location;
|
||||
SelectedConnection = connections.Find(c => c.Locations.Contains(CurrentLocation) && c.Locations.Contains(SelectedLocation));
|
||||
OnLocationSelected?.Invoke(SelectedLocation, SelectedConnection);
|
||||
}
|
||||
|
||||
public void SelectMission(int missionIndex)
|
||||
{
|
||||
if (SelectedConnection == null) { return; }
|
||||
if (CurrentLocation == null)
|
||||
{
|
||||
string errorMsg = "Failed to select a mission (current location not set).";
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("Map.SelectMission:CurrentLocationNotSet", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return;
|
||||
}
|
||||
CurrentLocation.SelectedMissionIndex = missionIndex;
|
||||
|
||||
//the destination must be the same as the destination of the mission
|
||||
if (CurrentLocation.SelectedMission != null &&
|
||||
CurrentLocation.SelectedMission.Locations[1] != SelectedLocation)
|
||||
{
|
||||
SelectLocation(CurrentLocation.SelectedMission.Locations[1]);
|
||||
}
|
||||
|
||||
OnMissionSelected?.Invoke(SelectedConnection, CurrentLocation.SelectedMission);
|
||||
}
|
||||
|
||||
public void SelectRandomLocation(bool preferUndiscovered)
|
||||
{
|
||||
List<Location> nextLocations = currentLocation.Connections.Select(c => c.OtherLocation(currentLocation)).ToList();
|
||||
List<Location> nextLocations = CurrentLocation.Connections.Select(c => c.OtherLocation(CurrentLocation)).ToList();
|
||||
List<Location> undiscoveredLocations = nextLocations.FindAll(l => !l.Discovered);
|
||||
|
||||
if (undiscoveredLocations.Count > 0 && preferUndiscovered)
|
||||
@@ -394,37 +442,127 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void ProgressWorld()
|
||||
{
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
if (!location.Discovered) continue;
|
||||
|
||||
//find which types of locations this one can change to
|
||||
List<LocationTypeChange> allowedTypeChanges = new List<LocationTypeChange>();
|
||||
List<LocationTypeChange> readyTypeChanges = new List<LocationTypeChange>();
|
||||
foreach (LocationTypeChange typeChange in location.Type.CanChangeTo)
|
||||
{
|
||||
//check if there are any adjacent locations that would prevent the change
|
||||
bool disallowedFound = false;
|
||||
foreach (string disallowedLocationName in typeChange.DisallowedAdjacentLocations)
|
||||
{
|
||||
if (location.Connections.Any(c => c.OtherLocation(location).Type.Name.ToLowerInvariant() == disallowedLocationName.ToLowerInvariant()))
|
||||
{
|
||||
disallowedFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (disallowedFound) continue;
|
||||
|
||||
//check that there's a required adjacent location present
|
||||
bool requiredFound = false;
|
||||
foreach (string requiredLocationName in typeChange.RequiredAdjacentLocations)
|
||||
{
|
||||
if (location.Connections.Any(c => c.OtherLocation(location).Type.Name.ToLowerInvariant() == requiredLocationName.ToLowerInvariant()))
|
||||
{
|
||||
requiredFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!requiredFound && typeChange.RequiredAdjacentLocations.Count > 0) continue;
|
||||
|
||||
allowedTypeChanges.Add(typeChange);
|
||||
|
||||
if (location.TypeChangeTimer >= typeChange.RequiredDuration)
|
||||
{
|
||||
readyTypeChanges.Add(typeChange);
|
||||
}
|
||||
}
|
||||
|
||||
//select a random type change
|
||||
if (Rand.Range(0.0f, 1.0f) < readyTypeChanges.Sum(t => t.Probability))
|
||||
{
|
||||
var selectedTypeChange =
|
||||
ToolBox.SelectWeightedRandom(readyTypeChanges, readyTypeChanges.Select(t => t.Probability).ToList(), Rand.RandSync.Unsynced);
|
||||
if (selectedTypeChange != null)
|
||||
{
|
||||
string prevName = location.Name;
|
||||
location.ChangeType(LocationType.List.Find(lt => lt.Name.ToLowerInvariant() == selectedTypeChange.ChangeTo.ToLowerInvariant()));
|
||||
ChangeLocationType(location, prevName, selectedTypeChange);
|
||||
location.TypeChangeTimer = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (allowedTypeChanges.Count > 0)
|
||||
{
|
||||
location.TypeChangeTimer++;
|
||||
}
|
||||
else
|
||||
{
|
||||
location.TypeChangeTimer = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partial void ChangeLocationType(Location location, string prevName, LocationTypeChange change);
|
||||
partial void ClearAnimQueue();
|
||||
|
||||
public static Map LoadNew(XElement element)
|
||||
{
|
||||
string mapSeed = element.GetAttributeString("seed", "a");
|
||||
|
||||
int size = element.GetAttributeInt("size", 1000);
|
||||
Map map = new Map(mapSeed, size);
|
||||
map.Load(element);
|
||||
string mapSeed = element.GetAttributeString("seed", "a");
|
||||
Map map = new Map(mapSeed);
|
||||
map.Load(element, false);
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
public void Load(XElement element)
|
||||
public void Load(XElement element, bool showNotifications)
|
||||
{
|
||||
ClearAnimQueue();
|
||||
SetLocation(element.GetAttributeInt("currentlocation", 0));
|
||||
|
||||
string discoveredStr = element.GetAttributeString("discovered", "");
|
||||
|
||||
string[] discoveredStrs = discoveredStr.Split(',');
|
||||
for (int i = 0; i < discoveredStrs.Length; i++)
|
||||
if (!Version.TryParse(element.GetAttributeString("version", ""), out _))
|
||||
{
|
||||
int index = -1;
|
||||
if (int.TryParse(discoveredStrs[i], out index)) locations[index].Discovered = true;
|
||||
DebugConsole.ThrowError("Incompatible map save file, loading the game failed.");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString() != "connection") continue;
|
||||
int connectionIndex = subElement.GetAttributeInt("i", -1);
|
||||
if (connectionIndex < 0 || connectionIndex >= connections.Count) continue;
|
||||
connections[connectionIndex].Passed = true;
|
||||
connections[connectionIndex].MissionsCompleted = subElement.GetAttributeInt("m", 0);
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "location":
|
||||
string locationType = subElement.GetAttributeString("type", "");
|
||||
Location location = Locations[subElement.GetAttributeInt("i", 0)];
|
||||
int typeChangeTimer = subElement.GetAttributeInt("changetimer", 0);
|
||||
int missionsCompleted = subElement.GetAttributeInt("missionscompleted", 0);
|
||||
|
||||
string prevLocationName = location.Name;
|
||||
LocationType prevLocationType = location.Type;
|
||||
location.Discovered = true;
|
||||
location.ChangeType(LocationType.List.Find(lt => lt.Name.ToLowerInvariant() == locationType.ToLowerInvariant()));
|
||||
location.TypeChangeTimer = typeChangeTimer;
|
||||
location.MissionsCompleted = missionsCompleted;
|
||||
if (showNotifications && prevLocationType != location.Type)
|
||||
{
|
||||
ChangeLocationType(
|
||||
location,
|
||||
prevLocationName,
|
||||
prevLocationType.CanChangeTo.Find(c => c.ChangeTo.ToLowerInvariant() == location.Type.Name.ToLowerInvariant()));
|
||||
}
|
||||
break;
|
||||
case "connection":
|
||||
int connectionIndex = subElement.GetAttributeInt("i", 0);
|
||||
connections[connectionIndex].Passed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -432,123 +570,55 @@ namespace Barotrauma
|
||||
{
|
||||
XElement mapElement = new XElement("map");
|
||||
|
||||
mapElement.Add(new XAttribute("version", GameMain.Version.ToString()));
|
||||
mapElement.Add(new XAttribute("currentlocation", CurrentLocationIndex));
|
||||
mapElement.Add(new XAttribute("seed", Seed));
|
||||
mapElement.Add(new XAttribute("size", size));
|
||||
|
||||
List<int> discoveredLocations = new List<int>();
|
||||
for (int i = 0; i < locations.Count; i++)
|
||||
for (int i = 0; i < Locations.Count; i++)
|
||||
{
|
||||
if (locations[i].Discovered) discoveredLocations.Add(i);
|
||||
var location = Locations[i];
|
||||
if (!location.Discovered) continue;
|
||||
|
||||
var locationElement = new XElement("location", new XAttribute("i", i));
|
||||
locationElement.Add(new XAttribute("type", location.Type.Name));
|
||||
if (location.TypeChangeTimer > 0)
|
||||
{
|
||||
locationElement.Add(new XAttribute("changetimer", location.TypeChangeTimer));
|
||||
}
|
||||
|
||||
location.CheckMissionCompleted();
|
||||
if (location.MissionsCompleted > 0)
|
||||
{
|
||||
locationElement.Add(new XAttribute("missionscompleted", location.MissionsCompleted));
|
||||
}
|
||||
|
||||
mapElement.Add(locationElement);
|
||||
}
|
||||
mapElement.Add(new XAttribute("discovered", string.Join(",", discoveredLocations)));
|
||||
|
||||
for (int i = 0; i < connections.Count; i++)
|
||||
{
|
||||
if (!connections[i].Passed) continue;
|
||||
connections[i].CheckMissionCompleted();
|
||||
var connection = connections[i];
|
||||
if (!connection.Passed) continue;
|
||||
|
||||
var connectionElement = new XElement("connection",
|
||||
new XAttribute("i", i),
|
||||
new XAttribute("passed", connection.Passed));
|
||||
|
||||
var connectionElement = new XElement("connection", new XAttribute("i", i));
|
||||
if (connections[i].MissionsCompleted > 0) connectionElement.Add(new XAttribute("m", connections[i].MissionsCompleted));
|
||||
mapElement.Add(connectionElement);
|
||||
|
||||
}
|
||||
|
||||
element.Add(mapElement);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LocationConnection
|
||||
{
|
||||
private Location[] locations;
|
||||
private Level level;
|
||||
|
||||
public Biome Biome;
|
||||
|
||||
public float Difficulty;
|
||||
|
||||
public List<Vector2[]> CrackSegments;
|
||||
|
||||
public bool Passed;
|
||||
|
||||
public int MissionsCompleted;
|
||||
|
||||
private Mission mission;
|
||||
public Mission Mission
|
||||
public void Remove()
|
||||
{
|
||||
get
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
if (mission == null || mission.Completed)
|
||||
{
|
||||
if (mission != null && mission.Completed) MissionsCompleted++;
|
||||
|
||||
long seed = (long)locations[0].MapPosition.X + (long)locations[0].MapPosition.Y * 100;
|
||||
seed += (long)locations[1].MapPosition.X * 10000 + (long)locations[1].MapPosition.Y * 1000000;
|
||||
|
||||
MTRandom rand = new MTRandom((int)((seed + MissionsCompleted) % int.MaxValue));
|
||||
|
||||
if (rand.NextDouble() < 0.3f) return null;
|
||||
|
||||
mission = Mission.LoadRandom(locations, rand, "", true);
|
||||
if (GameSettings.VerboseLogging && mission != null)
|
||||
{
|
||||
DebugConsole.NewMessage("Generated a new mission for a location connection (seed: " + seed + ", type: " + mission.Name + ")", Color.White);
|
||||
}
|
||||
}
|
||||
|
||||
return mission;
|
||||
location.Remove();
|
||||
}
|
||||
RemoveProjSpecific();
|
||||
}
|
||||
|
||||
public Location[] Locations
|
||||
{
|
||||
get { return locations; }
|
||||
}
|
||||
|
||||
public Level Level
|
||||
{
|
||||
get { return level; }
|
||||
set { level = value; }
|
||||
}
|
||||
|
||||
public Vector2 CenterPos
|
||||
{
|
||||
get
|
||||
{
|
||||
return (locations[0].MapPosition + locations[1].MapPosition) / 2.0f;
|
||||
}
|
||||
}
|
||||
|
||||
public LocationConnection(Location location1, Location location2)
|
||||
{
|
||||
locations = new Location[] { location1, location2 };
|
||||
MissionsCompleted = 0;
|
||||
}
|
||||
|
||||
public void CheckMissionCompleted()
|
||||
{
|
||||
if (mission != null && mission.Completed)
|
||||
{
|
||||
MissionsCompleted++;
|
||||
mission = null;
|
||||
}
|
||||
}
|
||||
|
||||
public Location OtherLocation(Location location)
|
||||
{
|
||||
if (locations[0] == location)
|
||||
{
|
||||
return locations[1];
|
||||
}
|
||||
else if (locations[1] == location)
|
||||
{
|
||||
return locations[0];
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
partial void RemoveProjSpecific();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,234 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class MapGenerationParams : ISerializableEntity
|
||||
{
|
||||
private static MapGenerationParams instance;
|
||||
public static MapGenerationParams Instance
|
||||
{
|
||||
get
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
[Serialize(false, true), Editable]
|
||||
public bool ShowNoiseMap { get; set; }
|
||||
|
||||
[Serialize(true, true), Editable]
|
||||
public bool ShowLocations { get; set; }
|
||||
|
||||
[Serialize(true, true), Editable]
|
||||
public bool ShowLevelTypeNames { get; set; }
|
||||
|
||||
[Serialize(true, true), Editable]
|
||||
public bool ShowOverlay { get; set; }
|
||||
#else
|
||||
public readonly bool ShowLocations = true;
|
||||
public readonly bool ShowLevelTypeNames = false;
|
||||
public readonly bool ShowOverlay = true;
|
||||
#endif
|
||||
|
||||
[Serialize(6, true)]
|
||||
public int DifficultyZones { get; set; } //Number of difficulty zones
|
||||
|
||||
[Serialize(2000, true)]
|
||||
public int Size { get; set; }
|
||||
|
||||
[Serialize(20.0f, true), Editable(0.0f, 5000.0f, ToolTip = "Connections with a length smaller or equal to this generate the smallest possible levels (using the MinWidth parameter in the level generation paramaters).")]
|
||||
public float SmallLevelConnectionLength { get; set; }
|
||||
|
||||
[Serialize(200.0f, true), Editable(0.0f, 5000.0f, ToolTip = "Connections with a length larger or equal to this generate the largest possible levels (using the MaxWidth parameter in the level generation paramaters).")]
|
||||
public float LargeLevelConnectionLength { get; set; }
|
||||
|
||||
[Serialize(1024, true)]
|
||||
public int NoiseResolution { get; set; } //Resolution of the noisemap overlay
|
||||
|
||||
[Serialize(10.0f, true), Editable(0.0f, 1000.0f)]
|
||||
public float NoiseFrequency { get; set; }
|
||||
|
||||
[Serialize(8, true), Editable(1, 100)]
|
||||
public int NoiseOctaves { get; set; }
|
||||
|
||||
[Serialize(0.5f, true), Editable(0.0f, 1.0f)]
|
||||
public float NoisePersistence { get; set; }
|
||||
|
||||
[Serialize("200,200", true), Editable]
|
||||
public Vector2 TileSpriteSize { get; set; }
|
||||
[Serialize("280,80", true), Editable]
|
||||
public Vector2 TileSpriteSpacing { get; set; }
|
||||
|
||||
[Serialize(1.0f, true), Editable(0.0f, 1.0f, ToolTip = "How dark the center of the map is (1.0f = black).")]
|
||||
public float CenterDarkenStrength { get; set; }
|
||||
|
||||
[Serialize(0.9f, true), Editable(0.0f, 1.0f, ToolTip = "How close to the center the darkening starts (0.8f = 20% from the edge).")]
|
||||
public float CenterDarkenRadius { get; set; }
|
||||
|
||||
[Serialize(5, true), Editable(0, 1000,
|
||||
ToolTip = "The edge of the dark center area is wave-shaped, and the frequency is determined by this value." +
|
||||
" I.e. how many points does the star-shaped dark area in the center have.")]
|
||||
public int CenterDarkenWaveFrequency { get; set; }
|
||||
|
||||
[Serialize(15.0f, true), Editable(0, 1000.0f,
|
||||
ToolTip = "How heavily the noise map affects the phase of the edge wave (higher value = more irregular shape).")]
|
||||
public float CenterDarkenWavePhaseNoise { get; set; }
|
||||
|
||||
[Serialize(0.8f, true), Editable(0.0f, 1.0f, ToolTip = "How dark the edges of the map are (1.0f = black).")]
|
||||
public float EdgeDarkenStrength { get; set; }
|
||||
|
||||
[Serialize(0.9f, true), Editable(0.0f, 1.0f, ToolTip = "How far from the center the darkening starts (0.95f = 5% from the edge).")]
|
||||
public float EdgeDarkenRadius { get; set; }
|
||||
|
||||
[Serialize(0.9f, true), Editable(0.0f, 1.0f, ToolTip = "How far from the center locations can be placed.")]
|
||||
public float LocationRadius { get; set; }
|
||||
|
||||
[Serialize(20.0f, true), Editable(1.0f, 100.0f,
|
||||
ToolTip = "How far from each other voronoi sites are placed. "+
|
||||
"Sites determine shape of the voronoi graph. Locations are placed at the vertices of the voronoi cells. "+
|
||||
"(Decreasing this value causes the number of sites, and the complexity of the map, to increase exponentially - be careful when adjusting)") ]
|
||||
public float VoronoiSiteInterval { get; set; }
|
||||
|
||||
[Serialize(0.3f, true), Editable(0.01f, 1.0f,
|
||||
ToolTip = "How likely it is for a site to be placed at a given spot (e.g. 20% probability for a site to be placed every 5 units of the map). "+
|
||||
"Multiplied with the noise value in the spot, meaning that sites are less likely to appear in dark spots.")]
|
||||
public float VoronoiSitePlacementProbability { get; set; }
|
||||
|
||||
[Serialize(0.1f, true), Editable(0.01f, 1.0f,
|
||||
ToolTip = "Probability * noise ^ 2 must be higher than this for a site to be placed. "+
|
||||
"= How bright the noise map must be at a given spot for a location to be placed there")]
|
||||
public float VoronoiSitePlacementMinVal { get; set; }
|
||||
|
||||
[Serialize(10.0f, true), Editable(0.0f, 500.0f, ToolTip = "Connections smaller than this are removed.")]
|
||||
public float MinConnectionDistance { get; set; }
|
||||
|
||||
[Serialize(0.2f, true), Editable(0.0f, 10.0f,
|
||||
ToolTip = "Affects how many iterations are done when generating the jagged shape of the connections (iterations = Sqrt(connectionLength * multiplier)).")]
|
||||
public float ConnectionIterationMultiplier { get; set; }
|
||||
|
||||
[Serialize(0.5f, true), Editable(0.0f, 10.0f, ToolTip = "How large the \"bends\" in the connections are (displacement = connectionLength * multiplier).")]
|
||||
public float ConnectionDisplacementMultiplier { get; set; }
|
||||
|
||||
[Serialize(0.1f, true), Editable(0.0f, 10.0f, ToolTip = "ConnectionIterationMultiplier for the UI indicator lines between locations.")]
|
||||
public float ConnectionIndicatorIterationMultiplier { get; set; }
|
||||
|
||||
[Serialize(0.1f, true), Editable(0.0f, 10.0f, ToolTip = "ConnectionDisplacementMultiplier for the UI indicator lines between locations.")]
|
||||
public float ConnectionIndicatorDisplacementMultiplier { get; set; }
|
||||
|
||||
public Sprite ConnectionSprite { get; private set; }
|
||||
|
||||
#if CLIENT
|
||||
|
||||
[Serialize(15.0f, true), Editable(1.0f, 1000.0f, ToolTip = "Size of the location icons in pixels when at 100% zoom.")]
|
||||
public float LocationIconSize { get; set; }
|
||||
|
||||
[Serialize("150,150,150,255", true), Editable(ToolTip = "The color used to display the low-difficulty connections on the map.")]
|
||||
public Color LowDifficultyColor { get; set; }
|
||||
[Serialize("210,143,83,255", true), Editable(ToolTip = "The color used to display the medium-difficulty connections on the map.")]
|
||||
public Color MediumDifficultyColor { get; set; }
|
||||
[Serialize("216,154,138", true), Editable(ToolTip = "The color used to display the high-difficulty connections on the map.")]
|
||||
public Color HighDifficultyColor { get; set; }
|
||||
|
||||
public SpriteSheet DecorativeMapSprite { get; private set; }
|
||||
public SpriteSheet DecorativeGraphSprite { get; private set; }
|
||||
public SpriteSheet DecorativeLineTop { get; private set; }
|
||||
public SpriteSheet DecorativeLineBottom { get; private set; }
|
||||
public SpriteSheet DecorativeLineCorner { get; private set; }
|
||||
|
||||
public SpriteSheet ReticleLarge { get; private set; }
|
||||
public SpriteSheet ReticleMedium { get; private set; }
|
||||
public SpriteSheet ReticleSmall { get; private set; }
|
||||
|
||||
public Sprite MapCircle { get; private set; }
|
||||
public Sprite LocationIndicator { get; private set; }
|
||||
#endif
|
||||
|
||||
public List<Sprite> BackgroundTileSprites { get; private set; }
|
||||
|
||||
public string Name
|
||||
{
|
||||
get { return GetType().ToString(); }
|
||||
}
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties
|
||||
{
|
||||
get; private set;
|
||||
}
|
||||
|
||||
public static void Init()
|
||||
{
|
||||
var files = ContentPackage.GetFilesOfType(GameMain.Config.SelectedContentPackages, ContentType.MapGenerationParameters);
|
||||
if (!files.Any())
|
||||
{
|
||||
DebugConsole.ThrowError("No map generation parameters found in the selected content packages!");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (string file in files)
|
||||
{
|
||||
XDocument doc = XMLExtensions.TryLoadXml(file);
|
||||
if (doc?.Root == null) return;
|
||||
|
||||
instance = new MapGenerationParams(doc.Root);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private MapGenerationParams(XElement element)
|
||||
{
|
||||
SerializableProperties = SerializableProperty.DeserializeProperties(this, element);
|
||||
BackgroundTileSprites = new List<Sprite>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "connectionsprite":
|
||||
ConnectionSprite = new Sprite(subElement);
|
||||
break;
|
||||
case "backgroundtile":
|
||||
BackgroundTileSprites.Add(new Sprite(subElement));
|
||||
break;
|
||||
#if CLIENT
|
||||
case "mapcircle":
|
||||
MapCircle = new Sprite(subElement);
|
||||
break;
|
||||
case "locationindicator":
|
||||
LocationIndicator = new Sprite(subElement);
|
||||
break;
|
||||
case "decorativemapsprite":
|
||||
DecorativeMapSprite = new SpriteSheet(subElement);
|
||||
break;
|
||||
case "decorativegraphsprite":
|
||||
DecorativeGraphSprite = new SpriteSheet(subElement);
|
||||
break;
|
||||
case "decorativelinetop":
|
||||
DecorativeLineTop = new SpriteSheet(subElement);
|
||||
break;
|
||||
case "decorativelinebottom":
|
||||
DecorativeLineBottom = new SpriteSheet(subElement);
|
||||
break;
|
||||
case "decorativelinecorner":
|
||||
DecorativeLineCorner = new SpriteSheet(subElement);
|
||||
break;
|
||||
case "reticlelarge":
|
||||
ReticleLarge = new SpriteSheet(subElement);
|
||||
break;
|
||||
case "reticlemedium":
|
||||
ReticleMedium = new SpriteSheet(subElement);
|
||||
break;
|
||||
case "reticlesmall":
|
||||
ReticleSmall = new SpriteSheet(subElement);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user