Track LocalMods as part of monolith

This commit is contained in:
2026-06-08 18:50:16 +03:00
parent 143f2fed7c
commit 1b214b44c2
1287 changed files with 139255 additions and 1 deletions

View File

@@ -0,0 +1,57 @@
using Barotrauma;
using MoreLevelContent.Shared.Data;
using MoreLevelContent.Shared.Generation;
using MoreLevelContent.Shared.XML;
namespace Barotrauma
{
/// <summary>
/// Changes what map feature the current level has.
/// </summary>
[InjectScriptedEvent]
internal class AlterMapFeatureAction : EventAction
{
[Serialize("", IsPropertySaveable.Yes, description: "The Identifier this levels map feature should change to.")]
public Identifier MapFeatureIdentifier { get; set; }
public AlterMapFeatureAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
if (MapFeatureIdentifier.IsEmpty)
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": MapFeatureIdentifier has not been configured.",
contentPackage: element.ContentPackage);
}
}
private bool isFinished = false;
public override bool IsFinished(ref string goToLabel)
{
return isFinished;
}
public override void Reset()
{
isFinished = false;
}
// This is going to break people mid-joining
public override void Update(float deltaTime)
{
if (isFinished) { return; }
if (MapFeatureModule.TryGetFeature(MapFeatureIdentifier, out var feature))
{
var data = Level.Loaded?.LevelData?.MLC();
if (data != null)
{
data.MapFeatureData.Name = feature.Name;
}
}
isFinished = true;
}
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(AlterMapFeatureAction)}";
}
}
}

View File

@@ -0,0 +1,91 @@
using Barotrauma;
using Microsoft.Xna.Framework;
using MoreLevelContent.Networking;
using MoreLevelContent.Shared;
using MoreLevelContent.Shared.Data;
using MoreLevelContent.Shared.Generation;
using MoreLevelContent.Shared.Utils;
using MoreLevelContent.Shared.XML;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Barotrauma
{
/// <summary>
/// Remove the fog of war from a nearby area
/// </summary>
[InjectScriptedEvent]
internal class RevealMapAreaAction : EventAction
{
private bool isFinished = false;
private readonly Random random;
public RevealMapAreaAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
//the action chooses the same mission if
// 1. event seed is the same (based on level seed, changes when events are completed)
// 2. event is the same (two different events shouldn't choose the same mission)
// 3. the MissionAction is the same (two different actions in the same event shouldn't choose the same mission)
// Taken from MissionAction
random = new MTRandom(
parentEvent.RandomSeed +
ToolBox.StringToInt(ParentEvent.Prefab.Identifier.Value) +
ParentEvent.Actions.Count);
}
public override void Update(float deltaTime)
{
isFinished = true;
if (GameMain.GameSession.GameMode is CampaignMode campaign)
{
Location loc = MLCUtils.FindUnlockLocation(new MLCUtils.FindLocationInfo()
{
MinDistance = 3,
MustBeFurtherOnMap = true,
MustBeHidden = true
});
if (loc == null)
{
Log.Error("Failed to find a location to unlock");
return;
}
campaign.Map.Discover(loc, false);
// Probably have to do some syncing here, maybe not
if (campaign is MultiPlayerCampaign mpCampaign)
{
mpCampaign.IncrementLastUpdateIdForFlag(MultiPlayerCampaign.NetFlags.MapAndMissions);
}
#if CLIENT
ShowNotification(loc);
#endif
}
}
#if CLIENT
public static void ShowNotification(Location loc)
{
if (GameMain.GameSession.GameMode is not CampaignMode campaign) return;
_ = new GUIMessageBox(TextManager.Get("mapupdate.generic.header"), TextManager.GetWithVariable("mapupdate.revealed.location", "[location1]", loc.DisplayName),
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, iconStyle: "", relativeSize: new Vector2(0.2f, 0.06f), minSize: new Point(64, 74));
}
#endif
public override bool IsFinished(ref string goToLabel) => isFinished;
public override void Reset() => isFinished = false;
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(RevealMapFeatureAction)}";
}
}
}

View File

@@ -0,0 +1,149 @@
using Barotrauma;
using Microsoft.Xna.Framework;
using MoreLevelContent.Networking;
using MoreLevelContent.Shared;
using MoreLevelContent.Shared.Data;
using MoreLevelContent.Shared.Generation;
using MoreLevelContent.Shared.XML;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Barotrauma
{
/// <summary>
/// Finds and reveals a hidden map feature.
/// </summary>
[InjectScriptedEvent]
internal class RevealMapFeatureAction : EventAction
{
[Serialize("", IsPropertySaveable.Yes, description: "The Identifier of the map feature to search for.")]
public Identifier MapFeatureIdentifier { get; set; }
private bool isFinished = false;
public RevealMapFeatureAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
if (MapFeatureIdentifier.IsEmpty)
{
DebugConsole.ThrowError($"Error in event \"{parentEvent.Prefab.Identifier}\": MapFeatureIdentifier has not been configured.",
contentPackage: element.ContentPackage);
}
}
public override void Update(float deltaTime)
{
if (GameMain.GameSession.GameMode is CampaignMode campaign)
{
LocationConnection featureConnection;
try
{
featureConnection = FindConnectionWithMapFeature(MapFeatureIdentifier);
} catch(Exception e)
{
isFinished = true;
Log.Error($"RevealMapFeatureAction crashed! {e.Message}");
return;
}
if (featureConnection != null)
{
MapFeatureModule.TryGetFeature(featureConnection.LevelData.MLC().MapFeatureData.Name, out MapFeature feature);
// Probably have to do some syncing here, maybe not
if (campaign is MultiPlayerCampaign mpCampaign)
{
mpCampaign.IncrementLastUpdateIdForFlag(MultiPlayerCampaign.NetFlags.MapAndMissions);
}
#if CLIENT
ShowNotification(feature, featureConnection);
#else
MapDirector.Instance.NotifyMapFeatureRevealed(featureConnection, featureConnection.LevelData.MLC().MapFeatureData);
featureConnection.LevelData.MLC().MapFeatureData.Revealed = true;
#endif
}
}
isFinished = true;
}
#if CLIENT
public static void ShowNotification(MapFeature feature, LocationConnection featureConnection)
{
if (GameMain.GameSession.GameMode is not CampaignMode campaign) return;
_ = new GUIMessageBox(TextManager.GetWithVariable("mapfeature.revealed.header", "[feature]", feature.Display.DisplayName), TextManager.Get("mapfeature.revealed.description"),
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, iconStyle: feature.Display.Icon, relativeSize: new Vector2(0.2f, 0.06f), minSize: new Point(64, 74));
featureConnection.LevelData.MLC().MapFeatureData.Revealed = true;
}
#endif
private LocationConnection FindConnectionWithMapFeature(Identifier name)
{
if (GameMain.GameSession.GameMode is not CampaignMode campaign) return null;
LocationConnection currentConnection;
if (Level.Loaded.Type == LevelData.LevelType.LocationConnection)
{
var start = Level.Loaded.StartLocation;
var end = Level.Loaded.EndLocation;
currentConnection = start.Connections.Where(c => c.OtherLocation(start) == end).FirstOrDefault();
} else
{
currentConnection = campaign.CurrentLocation.Connections.FirstOrDefault();
}
HashSet<LocationConnection> checkedConnections = new HashSet<LocationConnection>();
HashSet<LocationConnection> pendingConnections = new HashSet<LocationConnection>() { currentConnection };
do
{
List<LocationConnection> currentConnections = pendingConnections.ToList();
pendingConnections.Clear();
foreach (var connection in currentConnections)
{
checkedConnections.Add(connection);
var data = connection.LevelData.MLC();
if (data == null)
{
Log.Error("Missing data");
continue;
}
MapFeatureData feature = data.MapFeatureData;
if (feature.Name == name && !feature.Revealed)
{
return connection;
}
else
{
foreach (Location location in connection.Locations)
{
foreach (var item in location.Connections)
{
if (checkedConnections.Contains(item)) { continue; }
pendingConnections.Add(item);
}
}
}
}
} while (pendingConnections.Any());
return null;
}
public override bool IsFinished(ref string goToLabel) => isFinished;
public override void Reset() => isFinished = false;
public override string ToDebugString()
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(RevealMapFeatureAction)} -> ({(MapFeatureIdentifier)})";
}
}
}

View File

@@ -0,0 +1,133 @@
using Barotrauma;
using Microsoft.Xna.Framework;
using MoreLevelContent.Shared;
using MoreLevelContent.Shared.Data;
using MoreLevelContent.Shared.Generation;
using MoreLevelContent.Shared.Generation.Pirate;
using MoreLevelContent.Shared.XML;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
namespace Barotrauma
{
/// <summary>
/// Changes what map feature the current level has.
/// </summary>
[InjectScriptedEvent]
internal class RevealPirateBaseAction //: BinaryOptionAction
{
/*
public RevealPirateBaseAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element) { }
private LocationConnection FindPirateBase()
{
if (GameMain.GameSession.GameMode is not CampaignMode campaign) return null;
LocationConnection startSearchConnection;
if (Level.Loaded.Type == LevelData.LevelType.LocationConnection)
{
var start = Level.Loaded.StartLocation;
var end = Level.Loaded.EndLocation;
startSearchConnection = start.Connections.Where(c => c.OtherLocation(start) == end).FirstOrDefault();
}
else
{
startSearchConnection = campaign.CurrentLocation.Connections.FirstOrDefault();
}
HashSet<LocationConnection> checkedConnections = new HashSet<LocationConnection>();
HashSet<LocationConnection> pendingConnections = new HashSet<LocationConnection>() { startSearchConnection };
do
{
List<LocationConnection> currentConnections = pendingConnections.ToList();
pendingConnections.Clear();
foreach (var connection in currentConnections)
{
checkedConnections.Add(connection);
var data = connection.LevelData.MLC();
if (data == null)
{
Log.Error("Missing data");
continue;
}
var pirateData = data.PirateData;
// Don't use the current connection, only look for active and not revealed bases
if (startSearchConnection != connection && pirateData.Status == PirateOutpostStatus.Active && !pirateData.Revealed)
{
Log.Debug("Found connection with pirate base");
return connection;
}
else
{
foreach (Location location in connection.Locations)
{
foreach (var item in location.Connections)
{
if (checkedConnections.Contains(item)) { continue; }
pendingConnections.Add(item);
}
}
}
}
} while (pendingConnections.Any());
return null;
}
protected override bool? DetermineSuccess()
{
if (GameMain.GameSession.GameMode is not CampaignMode campaign) return false;
try
{
LocationConnection connection = FindPirateBase();
if (connection != null)
{
#if CLIENT
ShowNotification(connection);
#endif
if (!Main.IsClient)
{
var data = connection.LevelData.MLC().PirateData;
data.Revealed = true;
PirateOutpostDirector.UpdateStatus(data, connection);
if (campaign is MultiPlayerCampaign mpCampaign)
{
mpCampaign.IncrementLastUpdateIdForFlag(MultiPlayerCampaign.NetFlags.MapAndMissions);
}
Log.Debug("Updated status of pirate base to revealed");
}
return true;
}
}
catch(Exception e)
{
Log.Error($"RevealMapFeatureAction crashed! {e.Message}");
}
return false;
}
#if CLIENT
public static void ShowNotification(LocationConnection connection)
{
if (GameMain.GameSession.GameMode is not CampaignMode) return;
_ = new GUIMessageBox
(TextManager.Get("mapupdate.generic.header"),
RichString.Rich(TextManager.GetWithVariables("mapupdate.piratebase.revealed",
("[location1]", $"‖color:gui.orange‖{connection.Locations[0].DisplayName}‖end‖"),
("[location2]", $"‖color:gui.orange‖{connection.Locations[1].DisplayName}‖end‖"))),
Array.Empty<LocalizedString>(), type: GUIMessageBox.Type.InGame, iconStyle: "PirateBase", relativeSize: new Vector2(0.2f, 0.06f), minSize: new Point(64, 74));
}
#endif
*/
}
}

View File

@@ -0,0 +1,16 @@
using Barotrauma;
using MoreLevelContent.Shared.XML;
namespace MoreLevelContent
{
[InjectScriptedEvent]
internal class SpawnCreatureNearbyAction : EventAction
{
public SpawnCreatureNearbyAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
}
public override bool IsFinished(ref string goToLabel) => throw new System.NotImplementedException();
public override void Reset() => throw new System.NotImplementedException();
}
}

View File

@@ -0,0 +1,104 @@
using Barotrauma;
using Barotrauma.Extensions;
using Barotrauma.Items.Components;
using Microsoft.Xna.Framework;
using MoreLevelContent.Shared;
using MoreLevelContent.Shared.XML;
using System.Collections.Generic;
using System.Linq;
using static Barotrauma.Level;
namespace Barotrauma
{
[InjectScriptedEvent]
internal class TeleportCharacterAction : EventAction
{
[Serialize("", IsPropertySaveable.Yes, description: "Tag of the target(s) to teleport.")]
public Identifier TargetTag { get; set; }
public TeleportCharacterAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
}
private bool isFinished;
public override void Update(float deltaTime)
{
if (isFinished) { return; }
// Try to find a ruin to tp to
Submarine wreck = null;
Submarine ruin = null;
foreach (var sub in Submarine.Loaded)
{
if (sub.Info.Type == SubmarineType.Ruin)
{
ruin = sub;
break;
}
if (sub.Info.Type == SubmarineType.Wreck)
{
wreck = sub;
}
}
try
{
Tp();
} catch { Teleport(Vector2.Zero); }
isFinished = true;
void Tp()
{
if (ruin != null)
{
Teleport(ruin);
return;
}
// If we can't find a ruin, try to find a wreck
if (wreck != null)
{
Teleport(wreck);
return;
}
// If both those fail, try to find an abyss cave
if (Loaded != null && Loaded.TryGetInterestingPosition(false, PositionType.AbyssCave, Sonar.DefaultSonarRange, out InterestingPosition pos, suppressWarning: true))
{
Teleport(pos.Position.ToVector2());
return;
}
// Try to find a cave, main path or side path position
if (Loaded != null && Loaded.TryGetInterestingPosition(false, PositionType.Cave | PositionType.MainPath | PositionType.SidePath, Sonar.DefaultSonarRange, out InterestingPosition pos2, suppressWarning: true))
{
Teleport(pos2.Position.ToVector2());
return;
}
// If all else fails, teleport to a random waypoint or 0,0
Teleport(WayPoint.GetRandom(SpawnType.Path)?.WorldPosition ?? Vector2.Zero);
}
}
void Teleport(Submarine target)
{
var wp = WayPoint.GetRandom(sub: target);
Teleport(wp.WorldPosition);
}
void Teleport(Vector2 worldPos)
{
foreach (var target in ParentEvent.GetTargets(TargetTag))
{
if (target is Character c)
{
c.TeleportTo(worldPos);
}
}
}
public override bool IsFinished(ref string goToLabel)
{
return isFinished;
}
public override void Reset() { isFinished = false; }
}
}