Files
BarotraumaModServer/LocalMods/More Level Content/CSharp/Shared/XML/Injection.cs
2026-06-09 00:42:10 +03:00

137 lines
6.2 KiBLFS
C#
Executable File

using Barotrauma;
using Barotrauma.MoreLevelContent.Shared.Utils;
using HarmonyLib;
using MoreLevelContent.Custom.Missions;
using MoreLevelContent.Shared.Content;
using MoreLevelContent.Shared.Data;
using MoreLevelContent.Shared.Generation;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Reflection;
namespace MoreLevelContent.Shared.XML
{
[AttributeUsage(AttributeTargets.Class)]
public class InjectScriptedEvent : Attribute { }
public class InjectionManager : Singleton<InjectionManager>
{
FieldInfo missionPrefab_constructor;
static ImmutableDictionary<string, Type> _CustomScriptedEvents;
private static Assembly _BaroAsm;
public override void Setup()
{
missionPrefab_constructor = AccessTools.Field(typeof(MissionPrefab), "constructor");
var npcConversation_GetCurrentFlags = AccessTools.Method(typeof(NPCConversation), "GetCurrentFlags");
var eventAction_Instantiate = AccessTools.Method(typeof(EventAction), "Instantiate");
_BaroAsm = Assembly.GetAssembly(typeof(EventAction));
_ = Main.Harmony.Patch(npcConversation_GetCurrentFlags, postfix: new HarmonyMethod(AccessTools.Method(typeof(InjectionManager), nameof(AddNPCConcersationFlags))));
_ = Main.Harmony.Patch(eventAction_Instantiate, prefix: new HarmonyMethod(AccessTools.Method(typeof(InjectionManager), nameof(InjectEventActions))));
// Collect scripted events
Dictionary<string, Type> customEventDict = new()
{
{ nameof(AlterMapFeatureAction), typeof(AlterMapFeatureAction) },
{ nameof(RevealMapAreaAction), typeof(RevealMapAreaAction) },
{ nameof(RevealMapFeatureAction), typeof(RevealMapFeatureAction) },
{ nameof(RevealPirateBaseAction), typeof(RevealPirateBaseAction) },
{ nameof(TeleportCharacterAction), typeof(TeleportCharacterAction) }
};
_CustomScriptedEvents = customEventDict.ToImmutableDictionary();
InjectMissions();
}
// This should be a transpiler but I don't want to spend the time doing that and I don't think any other mod is ever going to add custom scripted events
// We'll fix this incompatability when it happens!
private static bool InjectEventActions(ScriptedEvent scriptedEvent, ContentXElement element, ref EventAction __result)
{
Type actionType;
__result = null;
try
{
Identifier typeName = element.Name.ToString().ToIdentifier();
if (typeName == "TutorialSegmentAction")
{
typeName = nameof(EventObjectiveAction).ToIdentifier();
}
else if (typeName == "TutorialHighlightAction")
{
typeName = nameof(HighlightAction).ToIdentifier();
}
if (!_CustomScriptedEvents.TryGetValue(typeName.ToString(), out actionType))
{
actionType = _BaroAsm.GetType("Barotrauma." + typeName, throwOnError: true, ignoreCase: true);
//actionType = _BaroAsm.GetType("Barotrauma." + typeName.ToString(), throwOnError: true, ignoreCase: true);
}
if (actionType == null) { throw new NullReferenceException(); }
}
catch(Exception e)
{
Log.Error(e.Message);
DebugConsole.ThrowError($"Could not find an {nameof(EventAction)} class of the type \"{element.Name}\".",
contentPackage: element.ContentPackage);
return false;
}
ConstructorInfo constructor = actionType.GetConstructor(new[] { typeof(ScriptedEvent), typeof(ContentXElement) });
try
{
if (constructor == null)
{
throw new Exception($"Error in scripted event \"{scriptedEvent.Prefab.Identifier}\" - could not find a constructor for the EventAction \"{actionType}\".");
}
__result = constructor.Invoke(new object[] { scriptedEvent, element }) as EventAction;
return false;
}
catch (Exception ex)
{
DebugConsole.ThrowError(ex.InnerException != null ? ex.InnerException.ToString() : ex.ToString(),
contentPackage: element.ContentPackage);
return false;
}
}
private static void AddNPCConcersationFlags(ref List<Identifier> __result, Character speaker)
{
if (speaker == null) return;
if (speaker.MLC().IsDistressShuttle) __result.Add("DistressShuttle");
if (speaker.MLC().IsDistressDiver) __result.Add("DistressDiver");
if (speaker.TeamID == CharacterTeamType.FriendlyNPC)
{
if (speaker.Submarine == MapFeatureModule.MapFeatureSub)
{
__result.Add(MapFeatureModule.CurrentMapFeature);
}
if (Submarine.MainSub != null && speaker.Submarine == Submarine.MainSub)
{
__result.Add("MainSub");
}
}
}
private void InjectMissions()
{
Log.Debug("Injecting custom missions");
foreach (MissionPrefab prefab in MissionPrefab.Prefabs)
{
Identifier customType = prefab.ConfigElement.GetAttributeIdentifier("customType", Identifier.Empty);
if(!Enum.TryParse(customType.Value, true, out CustomMissionType type)) continue;
missionPrefab_constructor.SetValue(prefab, CustomMissions.MissionDefs[type].GetConstructor(new[] { typeof(MissionPrefab), typeof(Location[]), typeof(Submarine) }));
Log.Verbose($"Updated consturctor for mission {prefab.Name} to type {type}");
}
}
}
}