using Barotrauma; using Microsoft.Xna.Framework; using MoreLevelContent.Shared.Utils; using System; using System.Linq; using System.Xml.Linq; namespace MoreLevelContent.Shared.Generation { internal abstract class MapModule { public MapModule() => InitProjSpecific(); protected MapDirector Instance => MapDirector.Instance; protected abstract void InitProjSpecific(); protected static bool TryGetMissionByTag(string tag, LevelData data, out MissionPrefab missionPrefab, string forceMission = "") { var orderedMissions = MissionPrefab.Prefabs.Where(m => m.Tags.Contains(tag)).OrderBy(m => m.UintIdentifier); if (!string.IsNullOrEmpty(forceMission)) { orderedMissions = orderedMissions.Where(m => m.Identifier == forceMission).OrderBy(m => m.UintIdentifier); } Random rand = new MTRandom(ToolBox.StringToInt(data.Seed)); missionPrefab = ToolBox.SelectWeightedRandom(orderedMissions, p => p.Commonness, rand); return missionPrefab != null; } protected LocationConnection WalkConnection(Location start, Random rand, int preferedWalkDistance) { // Since we do a connection step at the end of the process, there's one step implict in every walk // so we subtract a step here int actualWalkDist = preferedWalkDistance - 1; if (actualWalkDist <= 0) { return GetConnectionWeighted(start, rand); } Location location = WalkLocation(start, rand, actualWalkDist); return GetConnectionWeighted(location, rand); } protected Location WalkLocation(Location start, Random rand, int preferedWalkDistance, LocationConnection from = null) { var filteredConnections = start.Connections.Where(c => c != from); if (!filteredConnections.Any()) { return start; } LocationConnection connectionToTravel = ToolBox.SelectWeightedRandom( filteredConnections.ToList(), filteredConnections.Select(c => GetConnectionWeight(start, c)).ToList(), rand); Location walkedLocation = connectionToTravel.OtherLocation(start); preferedWalkDistance--; // if we haven't walked our wanted dist or if (preferedWalkDistance > 0) walkedLocation = WalkLocation(walkedLocation, rand, preferedWalkDistance); return walkedLocation; } static LocationConnection GetConnectionWeighted(Location location, Random rand) { LocationConnection connectionToTravel = ToolBox.SelectWeightedRandom( location.Connections, location.Connections.Select(c => GetConnectionWeight(location, c)).ToList(), rand); return connectionToTravel; } static float GetConnectionWeight(Location location, LocationConnection c) { // get the destination of this connection Location destination = c.OtherLocation(location); if (destination == null) { return 0; } float minWeight = 0.0001f; float lowWeight = 0.2f; float normalWeight = 1.0f; float maxWeight = 2.0f; // prefer connections we haven't passed through float weight = c.Passed ? lowWeight : normalWeight; if (location.Biome.AllowedZones.Contains(1)) { // In the first biome, give a stronger preference for locations that are farther to the right) float diff = destination.MapPosition.X - location.MapPosition.X; if (diff < 0) { weight *= 0.1f; } else { float maxRelevantDiff = 300; weight = MathHelper.Lerp(weight, maxWeight, MathUtils.InverseLerp(0, maxRelevantDiff, diff)); } } else if (destination.MapPosition.X > location.MapPosition.X) { weight *= 2.0f; } if (destination.IsRadiated()) { weight *= 0.001f; } // Prefer locations that have been revealed if (!destination.Discovered) { weight *= 0.5f; } return MathHelper.Clamp(weight, minWeight, maxWeight); } protected void SendChatUpdate(string msg) { #if CLIENT if (GameMain.Client != null) { GameMain.Client.AddChatMessage(msg, Barotrauma.Networking.ChatMessageType.Default, TextManager.Get("mlc.navigationannouce").Value); } else { GameMain.GameSession?.GameMode.CrewManager.AddSinglePlayerChatMessage( TextManager.Get("mlc.navigationannouce").Value, msg, Barotrauma.Networking.ChatMessageType.Default, sender: null); } #endif } protected void AddNewsStory(string tag, LocationConnection connection) { #if CLIENT string randomTag = MLCUtils.GetRandomTag(tag, connection.LevelData); string msg = TextManager.GetWithVariables(randomTag, ("[location1]", $"‖color:gui.orange‖{connection.Locations[0].DisplayName}‖end‖"), ("[location2]", $"‖color:gui.orange‖{connection.Locations[1].DisplayName}‖end‖")).Value; Log.Debug($"Added text tag {randomTag} : {msg} to news ticket"); MapDirector.Instance.AddNewsStory(msg); #endif } public virtual void OnAddExtraMissions(CampaignMode __instance, LevelData levelData) { } public virtual void OnPreRoundStart(LevelData levelData) { } public virtual void OnPostRoundStart(LevelData levelData) { } public virtual void OnLevelDataGenerate(LevelData __instance, LocationConnection locationConnection) { } public virtual void OnProgressWorld(Map __instance) { } public virtual void OnLevelDataLoad(LevelData __instance, XElement element) { } public virtual void OnLevelDataSave(LevelData __instance, XElement parentElement) { } public virtual void OnMapLoad(Map __instance) { } public virtual void OnLevelGenerate(LevelData levelData, bool mirror) { } } }