Track LocalMods as part of monolith
This commit is contained in:
@@ -0,0 +1,82 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MoreLevelContent.Shared.Utils;
|
||||
using System.Reflection.Metadata;
|
||||
using MoreLevelContent.Shared.Generation;
|
||||
|
||||
namespace MoreLevelContent.Shared.AI
|
||||
{
|
||||
partial class CaveAI
|
||||
{
|
||||
private CoroutineHandle fadeOutRoutine;
|
||||
partial void FadeOutColors()
|
||||
{
|
||||
if (fadeOutRoutine != null)
|
||||
{
|
||||
CoroutineManager.StopCoroutines(fadeOutRoutine);
|
||||
}
|
||||
fadeOutRoutine = CoroutineManager.StartCoroutine(FadeOutColors(Config.DeadEntityColorFadeOutTime));
|
||||
}
|
||||
|
||||
private IEnumerable<CoroutineStatus> FadeOutColors(float time)
|
||||
{
|
||||
float timer = 0;
|
||||
while (timer < time)
|
||||
{
|
||||
timer += CoroutineManager.DeltaTime;
|
||||
float m = MathHelper.Lerp(1, Config.DeadEntityColorMultiplier, MathUtils.InverseLerp(0, time, timer));
|
||||
foreach (var item in ThalamusItems)
|
||||
{
|
||||
if (item.Prefab.BrokenSprites.None())
|
||||
{
|
||||
Color c = item.Prefab.SpriteColor;
|
||||
item.SpriteColor = new Color(c.R / 255f * m, c.G / 255f * m, c.B / 255f * m, c.A / 255f);
|
||||
}
|
||||
}
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
public void DebugDraw(Microsoft.Xna.Framework.Graphics.SpriteBatch sb, Camera cam)
|
||||
{
|
||||
float lineThickness = 1f / Screen.Selected.Cam.Zoom;
|
||||
sb.DrawPoint(new Vector2(Cave.StartPos.X, -Cave.StartPos.Y), Color.Pink, 10 / Screen.Selected.Cam.Zoom);
|
||||
foreach (var turret in turrets)
|
||||
{
|
||||
const float coneRadius = 300.0f;
|
||||
float radians = turret.GetMaxRotation() - turret.GetMinRotation();
|
||||
float circleRadius = coneRadius / Screen.Selected.Cam.Zoom * GUI.Scale;
|
||||
|
||||
sb.DrawSector(turret.GetDrawPos(), circleRadius, radians, (int)Math.Abs(90 * radians), GUIStyle.Green, offset: turret.GetMinRotation(), thickness: lineThickness);
|
||||
|
||||
Dictionary<ActionType, List<StatusEffect>> dic = (Dictionary<ActionType, List<StatusEffect>>)CaveGenerationDirector.item_statusEffectList.GetValue(turret.Item);
|
||||
if (dic?.TryGetValue(ActionType.OnUse, out List<StatusEffect> effects) ?? false)
|
||||
{
|
||||
foreach (var effect in effects)
|
||||
{
|
||||
var pos = turret.Item.Position + new Vector2(effect.Offset.X, effect.Offset.Y);
|
||||
pos = new Vector2(pos.X, -pos.Y);
|
||||
GUI.DrawRectangle(sb, pos, 100, 100, 0, Color.Aqua, thickness: lineThickness);
|
||||
foreach (var spawnEffect in effect.SpawnCharacters)
|
||||
{
|
||||
var pos2 = turret.Item.Position + new Vector2(spawnEffect.Offset.X, spawnEffect.Offset.Y);
|
||||
pos2 = new Vector2(pos2.X, -pos2.Y);
|
||||
GUI.DrawRectangle(sb, pos2, 50, 50, 0, Color.Orange, thickness: lineThickness);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void ClientEventRead(IReadMessage msg, float sendingTime)
|
||||
{
|
||||
IsAlive = msg.ReadBoolean();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
using Barotrauma.MoreLevelContent.Client.UI;
|
||||
using Barotrauma.MoreLevelContent.Shared.Utils;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MoreLevelContent.Shared;
|
||||
using Barotrauma.MoreLevelContent.Shared.Config;
|
||||
using MoreLevelContent;
|
||||
using Barotrauma.Networking;
|
||||
using MoreLevelContent.Networking;
|
||||
|
||||
namespace Barotrauma.MoreLevelContent.Config
|
||||
{
|
||||
/// <summary>
|
||||
/// Client
|
||||
/// </summary>
|
||||
partial class ConfigManager : Singleton<ConfigManager>
|
||||
{
|
||||
public static bool ShouldDisplayPatchNotes = false;
|
||||
private void SetupClient()
|
||||
{
|
||||
CommandUtils.AddCommand(
|
||||
"mlc_config",
|
||||
"Toggle the display of the config editor",
|
||||
ToggleGUI);
|
||||
// Exit if we're in an editor
|
||||
if (Screen.Selected.IsEditor) return;
|
||||
if (GameMain.IsSingleplayer) return; // We don't need to do any of this if we're in singleplayer
|
||||
NetUtil.Register(NetEvent.CONFIG_WRITE_CLIENT, ClientRead);
|
||||
if (!GameMain.Client.IsServerOwner) RequestConfig();
|
||||
else ClientWrite();
|
||||
}
|
||||
|
||||
public void SetConfig(MLCConfig config)
|
||||
{
|
||||
this.Config = config;
|
||||
Log.Debug("[CLIENT] Config Updated");
|
||||
Log.Verbose(Config.ToString());
|
||||
|
||||
if (!GameMain.IsSingleplayer) UpdateConfig();
|
||||
SaveConfig();
|
||||
}
|
||||
|
||||
private void RequestConfig()
|
||||
{
|
||||
IWriteMessage outMsg = NetUtil.CreateNetMsg(NetEvent.CONFIG_REQUEST);
|
||||
outMsg.WriteString(Main.Version);
|
||||
GameMain.LuaCs.Networking.Send(outMsg);
|
||||
Log.Verbose("Requested config from server...");
|
||||
}
|
||||
|
||||
private void ClientWrite()
|
||||
{
|
||||
// Always allow the server owner to write
|
||||
if (!GameMain.Client.HasPermission(ClientPermissions.ManageSettings) && !GameMain.Client.IsServerOwner)
|
||||
{
|
||||
Log.Error("No Perms!");
|
||||
return;
|
||||
}
|
||||
IWriteMessage outMsg = NetUtil.CreateNetMsg(NetEvent.CONFIG_WRITE_SERVER);
|
||||
outMsg.WriteString(Main.Version);
|
||||
WriteConfig(ref outMsg);
|
||||
GameMain.LuaCs.Networking.Send(outMsg);
|
||||
Log.Debug("Sent config packet to server!");
|
||||
}
|
||||
|
||||
private void ClientRead(object[] args)
|
||||
{
|
||||
Log.Debug("Got config packet!");
|
||||
IReadMessage inMsg = (IReadMessage)args[0];
|
||||
ReadNetConfig(ref inMsg);
|
||||
}
|
||||
|
||||
private void UpdateConfig()
|
||||
{
|
||||
if (!GameMain.Client.HasPermission(ClientPermissions.ManageSettings)) return;
|
||||
ClientWrite();
|
||||
}
|
||||
|
||||
private void DisplayPatchNotes(bool force = false)
|
||||
{
|
||||
// REMEMBER TO CHANGE THIS BACK
|
||||
if (Config.Version != Main.Version || force || Main.IsNightly)
|
||||
{
|
||||
ShouldDisplayPatchNotes = true;
|
||||
}
|
||||
}
|
||||
|
||||
public bool SettingsOpen
|
||||
{
|
||||
get => _settingsOpen;
|
||||
set
|
||||
{
|
||||
if (value == _settingsOpen) { return; }
|
||||
|
||||
if (value)
|
||||
{
|
||||
|
||||
_settingsMenu = new GUIFrame(new RectTransform(Vector2.One, Screen.Selected.Frame.RectTransform, Anchor.Center), style: null);
|
||||
_ = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, _settingsMenu.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
|
||||
|
||||
var settingsMenuInner = new GUIFrame(new RectTransform(new Vector2(1.0f, 0.8f), _settingsMenu.RectTransform, Anchor.Center, scaleBasis: ScaleBasis.Smallest) { MinSize = new Point(640, 480) });
|
||||
_ = ConfigMenu.Create(settingsMenuInner.RectTransform);
|
||||
Log.Verbose("Opened Settings");
|
||||
}
|
||||
else
|
||||
{
|
||||
ConfigMenu.Instance?.Close();
|
||||
_settingsMenu.Parent.RemoveChild(_settingsMenu);
|
||||
_settingsMenu = null;
|
||||
Log.Verbose("Closed Settings");
|
||||
}
|
||||
_settingsOpen = value;
|
||||
}
|
||||
}
|
||||
private static bool _settingsOpen;
|
||||
private static GUIFrame _settingsMenu;
|
||||
private void ToggleGUI(object[] args) => SettingsOpen = !SettingsOpen;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,123 @@
|
||||
using Barotrauma.MoreLevelContent.Shared.Utils;
|
||||
using MoreLevelContent.Networking;
|
||||
using HarmonyLib;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Reflection;
|
||||
using Barotrauma;
|
||||
using Barotrauma.Networking;
|
||||
using MoreLevelContent.Shared.Data;
|
||||
using System.Reflection.Emit;
|
||||
using System.Xml.Linq;
|
||||
using static Barotrauma.Networking.MessageFragment;
|
||||
|
||||
namespace MoreLevelContent.Shared.Generation
|
||||
{
|
||||
// Client
|
||||
public partial class MapDirector : Singleton<MapDirector>
|
||||
{
|
||||
private FieldInfo _notificationList;
|
||||
private Type _notification;
|
||||
private MethodInfo _addMethod;
|
||||
private ConstructorInfo _notifConstructor;
|
||||
private FieldInfo _mapAnimField;
|
||||
private ConstructorInfo _mapAnimConstructor;
|
||||
private MapSyncState SyncState = MapSyncState.Syncing;
|
||||
partial void SetupProjSpecific()
|
||||
{
|
||||
NetUtil.Register(NetEvent.MAP_CONNECTION_EQUALITYCHECK_SENDCLIENT, ConnectionEqualityCheck);
|
||||
NetUtil.Register(NetEvent.EVENT_REVEALMAPFEATURE, NotifyRevealMapFeature);
|
||||
_notificationList = AccessTools.Field(typeof(Map), "mapNotifications");
|
||||
_notification = typeof(Map).GetNestedType("MapNotification", BindingFlags.NonPublic);
|
||||
|
||||
_addMethod = AccessTools.Method(_notificationList.FieldType, "Add");
|
||||
_notifConstructor = AccessTools.Constructor(_notification, new Type[] { typeof(string), typeof(GUIFont), _notificationList.FieldType, typeof(Location) });
|
||||
|
||||
_mapAnimField = AccessTools.Field(typeof(Map), "mapAnimQueue");
|
||||
|
||||
var mapAnimType = typeof(Map).GetNestedType("MapAnim", BindingFlags.NonPublic);
|
||||
_mapAnimConstructor = AccessTools.Constructor(mapAnimType);
|
||||
|
||||
var singlePlayerCampaign_Start = typeof(SinglePlayerCampaign).GetMethod(nameof(SinglePlayerCampaign.Start));
|
||||
_ = Main.Harmony.Patch(singlePlayerCampaign_Start, postfix: new HarmonyMethod(GetType().GetMethod(nameof(OnSinglePlayerMapLoad), BindingFlags.Static | BindingFlags.NonPublic)));
|
||||
|
||||
NetUtil.Register(NetEvent.MAP_SEND_STATE, ReceiveMapState);
|
||||
}
|
||||
|
||||
private static void OnSinglePlayerMapLoad(SinglePlayerCampaign __instance) => OnMapLoad(__instance.Map);
|
||||
|
||||
private void ReceiveMapState(object[] args)
|
||||
{
|
||||
Log.Debug("Got map state packet");
|
||||
IReadMessage inMsg = (IReadMessage)args[0];
|
||||
MapSyncState mapState = (MapSyncState)inMsg.ReadByte();
|
||||
if (mapState != MapSyncState.MapSynced)
|
||||
{
|
||||
Log.Debug($"Map did not sync, state: {mapState}");
|
||||
SyncState = mapState;
|
||||
return;
|
||||
}
|
||||
|
||||
byte activeBeacons = inMsg.ReadByte();
|
||||
for (int i = 0; i < activeBeacons; i++)
|
||||
{
|
||||
int connectionID = inMsg.ReadUInt16();
|
||||
int stepsLeft = inMsg.ReadByte();
|
||||
if (!IdConnectionLookup.TryGetValue(connectionID, out var connection))
|
||||
{
|
||||
DebugConsole.ThrowError($"More Level Content tried to add an active distress beacon on connection with ID '{connectionID}' but found no connection on the clients id connection lookup! Do we have connections in the lookup? {IdConnectionLookup.Count > 0}s");
|
||||
continue;
|
||||
}
|
||||
connection.LevelData.MLC().HasDistress = true;
|
||||
connection.LevelData.MLC().DistressStepsLeft = stepsLeft;
|
||||
}
|
||||
Log.Debug("Synced map state!");
|
||||
}
|
||||
|
||||
internal partial void RoundEnd(CampaignMode.TransitionType transitionType)
|
||||
{
|
||||
if (transitionType == CampaignMode.TransitionType.None)
|
||||
{
|
||||
Log.Debug($"Cleaned connection lookup");
|
||||
// Reset connection lookup
|
||||
if (_validatedConnectionLookup)
|
||||
{
|
||||
_validatedConnectionLookup = false;
|
||||
IdConnectionLookup.Clear();
|
||||
ConnectionIdLookup.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void AddNewsStory(string message)
|
||||
{
|
||||
object list = _notificationList.GetValue(GameMain.GameSession.Map);
|
||||
|
||||
var notification = _notifConstructor.Invoke(new object[] {
|
||||
message,
|
||||
GUIStyle.SubHeadingFont,
|
||||
list,
|
||||
null
|
||||
});
|
||||
|
||||
_ = _addMethod.Invoke(list, new object[] { notification });
|
||||
|
||||
_notificationList.SetValue(GameMain.GameSession.Map, list);
|
||||
}
|
||||
|
||||
public void AddMapAnimation() => throw new NotImplementedException();
|
||||
|
||||
void NotifyRevealMapFeature(object[] args)
|
||||
{
|
||||
IReadMessage inMsg = (IReadMessage)args[0];
|
||||
Identifier featureName = inMsg.ReadIdentifier();
|
||||
Int32 conId = inMsg.ReadInt32();
|
||||
LocationConnection con = IdConnectionLookup[conId];
|
||||
MapFeatureModule.TryGetFeature(featureName, out MapFeature feature);
|
||||
RevealMapFeatureAction.ShowNotification(feature, con);
|
||||
}
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma;
|
||||
using MoreLevelContent.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MoreLevelContent.Shared.Generation
|
||||
{
|
||||
// Client
|
||||
internal partial class OldDistressMapModule
|
||||
{
|
||||
protected override void InitProjSpecific()
|
||||
{
|
||||
if (GameMain.IsMultiplayer) NetUtil.Register(NetEvent.MAP_SEND_NEWDISTRESS, CreateDistress);
|
||||
}
|
||||
|
||||
internal void CreateDistress(object[] args)
|
||||
{
|
||||
IReadMessage inMsg = (IReadMessage)args[0];
|
||||
int id = (int)inMsg.ReadUInt32();
|
||||
byte steps = inMsg.ReadByte();
|
||||
LocationConnection connection = MapDirector.IdConnectionLookup[id];
|
||||
CreateDistress(connection, steps);
|
||||
}
|
||||
}
|
||||
|
||||
internal partial class DistressMapModule : TimedEventMapModule
|
||||
{
|
||||
}
|
||||
}
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MoreLevelContent.Shared.Generation
|
||||
{
|
||||
// Client
|
||||
internal partial class LostCargoMapModule : TimedEventMapModule
|
||||
{
|
||||
}
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
using Barotrauma;
|
||||
using Barotrauma.Networking;
|
||||
using MoreLevelContent.Networking;
|
||||
|
||||
namespace MoreLevelContent.Shared.Generation
|
||||
{
|
||||
// Client
|
||||
abstract partial class TimedEventMapModule
|
||||
{
|
||||
protected override void InitProjSpecific()
|
||||
{
|
||||
if (GameMain.IsMultiplayer) NetUtil.Register(EventCreated, CreateEvent);
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal void CreateEvent(object[] args)
|
||||
{
|
||||
IReadMessage inMsg = (IReadMessage)args[0];
|
||||
int id = (int)inMsg.ReadUInt32();
|
||||
byte steps = inMsg.ReadByte();
|
||||
LocationConnection connection = MapDirector.IdConnectionLookup[id];
|
||||
CreateEvent(connection, steps);
|
||||
}
|
||||
}
|
||||
}
|
||||
+1173
File diff suppressed because it is too large
Load Diff
+58
@@ -0,0 +1,58 @@
|
||||
using Barotrauma;
|
||||
using Barotrauma.MoreLevelContent.Client.UI;
|
||||
using Barotrauma.MoreLevelContent.Config;
|
||||
using HarmonyLib;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MoreLevelContent.Shared.Utils;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace MoreLevelContent
|
||||
{
|
||||
/// <summary>
|
||||
/// Client
|
||||
/// </summary>
|
||||
partial class Main
|
||||
{
|
||||
public void InitClient()
|
||||
{
|
||||
MapUI.Instance.Setup();
|
||||
Hooks.Instance.OnDebugDraw += ClientDebugDraw.Draw;
|
||||
SonarExtensions.Instance.Setup();
|
||||
|
||||
GameMain.LuaCs.Hook.Add("roundStart", OpenPatchNotes);
|
||||
|
||||
// Exit if we're in an editor
|
||||
if (Screen.Selected.IsEditor) return;
|
||||
MethodInfo info = typeof(GUI).GetMethod("TogglePauseMenu", BindingFlags.Static | BindingFlags.Public);
|
||||
Patch(info, postfix: new HarmonyMethod(AccessTools.Method(typeof(Main), "AddSettingsButton")));
|
||||
}
|
||||
|
||||
private static void AddSettingsButton()
|
||||
{
|
||||
if (!GUI.PauseMenuOpen) return; // don't try to add the button when the pause menu doesn't exist
|
||||
var target = GUI.PauseMenu.Children.ToList()[1].Children.First();
|
||||
var button = new GUIButton(new RectTransform(new Vector2(1, 0.1f), target.RectTransform), TextManager.Get("mlc.configshort"), style: "GUIButtonSmall")
|
||||
{
|
||||
OnClicked = (GUIButton obj, object o) =>
|
||||
{
|
||||
GUI.TogglePauseMenu();
|
||||
return Instance.OpenConfig(obj, o);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
object OpenPatchNotes(object[] args)
|
||||
{
|
||||
if (!ConfigManager.ShouldDisplayPatchNotes) return null;
|
||||
CoroutineManager.Invoke(() => PatchNotes.Open(), delay: 5.0f);
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool OpenConfig(GUIButton button, object obj)
|
||||
{
|
||||
ConfigManager.Instance.SettingsOpen = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using Barotrauma;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace MoreLevelContent.Missions
|
||||
{
|
||||
partial class BeaconConstMission : Mission
|
||||
{
|
||||
public override bool DisplayAsCompleted => State > 0;
|
||||
|
||||
public override bool DisplayAsFailed => false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Barotrauma;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MoreLevelContent.Missions
|
||||
{
|
||||
// Client
|
||||
internal partial class CablePuzzleMission : Mission
|
||||
{
|
||||
public override bool DisplayAsCompleted => State == 2;
|
||||
|
||||
public override bool DisplayAsFailed => false;
|
||||
|
||||
}
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
using Barotrauma;
|
||||
using Barotrauma.Networking;
|
||||
using MoreLevelContent.Shared;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MoreLevelContent.Missions
|
||||
{
|
||||
// Client
|
||||
partial class DistressEscortMission : DistressMission
|
||||
{
|
||||
public override bool DisplayAsFailed => State == 1;
|
||||
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
base.ClientReadInitial(msg);
|
||||
missionNPCs.Read(msg);
|
||||
InitCharacters();
|
||||
}
|
||||
}
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
using Barotrauma;
|
||||
using Barotrauma.Networking;
|
||||
using MoreLevelContent.Shared;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MoreLevelContent.Missions
|
||||
{
|
||||
partial class DistressGhostshipMission : DistressMission
|
||||
{
|
||||
private readonly Identifier EXPLORE_SUB = "MLCDISTRESS_GHOSTSHIP_EXPLORE_SUB";
|
||||
private readonly Identifier SALVAGE_SUB = "MLCDISTRESS_GHOSTSHIP_SALVAGE";
|
||||
|
||||
public override bool DisplayAsFailed => false;
|
||||
public override bool DisplayAsCompleted => State >= 2;
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
base.ClientReadInitial(msg);
|
||||
Log.Debug("message read init");
|
||||
missionNPCs.Read(msg);
|
||||
}
|
||||
|
||||
public override RichString GetMissionRewardText(Submarine sub) => !SubSalvaged ? base.GetMissionRewardText(sub) : GetBaseMissionRewardText(sub);
|
||||
private GhostshipState CurrentState = GhostshipState.WaitForBoardSub;
|
||||
private ObjectiveManager.Segment ExploreSegment;
|
||||
private List<Hull> HullToExplore = new();
|
||||
private List<Hull> ExploredHulls = new();
|
||||
|
||||
private enum GhostshipState
|
||||
{
|
||||
WaitForBoardSub,
|
||||
WaitForExplore,
|
||||
WaitForSalvage,
|
||||
Salvage
|
||||
}
|
||||
|
||||
private bool _salvaged = false;
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime)
|
||||
{
|
||||
if (SubSalvaged && !_salvaged)
|
||||
{
|
||||
ObjectiveManager.CompleteSegment(SALVAGE_SUB);
|
||||
_salvaged = true;
|
||||
CoroutineManager.StartCoroutine(_showMessageBox(TextManager.Get("missionheader0.distress_ghostship"), TextManager.Get("dgs.inrageforsalvage")));
|
||||
}
|
||||
IEnumerable<CoroutineStatus> _showMessageBox(LocalizedString header, LocalizedString message)
|
||||
{
|
||||
while (GUIMessageBox.VisibleBox?.UserData is RoundSummary)
|
||||
{
|
||||
yield return new WaitForSeconds(1.0f);
|
||||
}
|
||||
CreateMessageBox(header, message);
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
switch (State)
|
||||
{
|
||||
case 2:
|
||||
if (CurrentState == GhostshipState.WaitForSalvage)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (CurrentState == GhostshipState.WaitForExplore)
|
||||
{
|
||||
foreach (var crewMember in GameSession.GetSessionCrewCharacters(CharacterType.Player))
|
||||
{
|
||||
if (HullToExplore.Contains(crewMember.CurrentHull))
|
||||
{
|
||||
if (!ExploredHulls.Contains(crewMember.CurrentHull))
|
||||
{
|
||||
ExploredHulls.Add(crewMember.CurrentHull);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ExploredHulls.Count >= HullToExplore.Count / 2)
|
||||
{
|
||||
CurrentState = GhostshipState.WaitForSalvage;
|
||||
ObjectiveManager.CompleteSegment(EXPLORE_SUB);
|
||||
ObjectiveManager.TriggerSegment(ObjectiveManager.Segment.CreateObjectiveSegment(SALVAGE_SUB, "dgs.obj.optionalsalvage"));
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
HullToExplore = ghostship.GetHulls(false).Where(h => h.AvoidStaying == false).ToList();
|
||||
CurrentState = GhostshipState.WaitForExplore;
|
||||
ExploreSegment = ObjectiveManager.Segment.CreateObjectiveSegment(EXPLORE_SUB, "dgs.obj.exploreship");
|
||||
ExploreSegment.CanBeCompleted = true;
|
||||
ObjectiveManager.TriggerSegment(ExploreSegment);
|
||||
Log.Debug("Triggered state");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Barotrauma;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MoreLevelContent.Missions
|
||||
{
|
||||
// Client
|
||||
abstract partial class DistressMission : Mission
|
||||
{
|
||||
public override bool DisplayAsCompleted => false;
|
||||
|
||||
// Hide reward until the end of the round
|
||||
public override RichString GetMissionRewardText(Submarine sub) => RichString.Rich(TextManager.GetWithVariable("missionreward", "[reward]", $"‖color:gui.orange‖{(GameMain.GameSession.RoundEnding || DisplayReward ? Reward : "???")}‖end‖"));
|
||||
protected RichString GetBaseMissionRewardText(Submarine sub) => base.GetMissionRewardText(sub);
|
||||
}
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
using Barotrauma;
|
||||
using Barotrauma.Networking;
|
||||
using HarmonyLib;
|
||||
using MoreLevelContent.Shared;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MoreLevelContent.Missions
|
||||
{
|
||||
// Client
|
||||
partial class DistressSubmarineMission : DistressMission
|
||||
{
|
||||
public override bool DisplayAsFailed => false;
|
||||
|
||||
public override RichString GetMissionRewardText(Submarine sub) => State == 0 ? base.GetMissionRewardText(sub) : GetBaseMissionRewardText(sub);
|
||||
|
||||
public override void ClientReadInitial(IReadMessage msg)
|
||||
{
|
||||
base.ClientReadInitial(msg);
|
||||
missionNPCs.Read(msg);
|
||||
|
||||
foreach (var character in missionNPCs.characters)
|
||||
{
|
||||
int reward = msg.ReadUInt16();
|
||||
rewardLookup.Add(character, reward);
|
||||
character.Info.Title = TextManager.GetWithVariable("currencyformat", "[credits]", string.Format(CultureInfo.InvariantCulture, "{0:N0}", reward));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
using Barotrauma.Networking;
|
||||
using Barotrauma;
|
||||
using MoreLevelContent.Shared;
|
||||
|
||||
namespace MoreLevelContent.Missions
|
||||
{
|
||||
// Client
|
||||
partial class MissionNPCCollection
|
||||
{
|
||||
internal void Read(IReadMessage msg)
|
||||
{
|
||||
bool hasCharacters = msg.ReadBoolean();
|
||||
if (!hasCharacters)
|
||||
{
|
||||
Log.Debug("Mission has no characters");
|
||||
return;
|
||||
}
|
||||
byte characterCount = msg.ReadByte();
|
||||
for (int i = 0; i < characterCount; i++)
|
||||
{
|
||||
Character character = Character.ReadSpawnData(msg);
|
||||
bool allowOrdering = msg.ReadBoolean();
|
||||
characters.Add(character);
|
||||
if (allowOrdering)
|
||||
{
|
||||
_ = GameMain.GameSession.CrewManager.AddCharacterToCrewList(character);
|
||||
Log.InternalDebug($"Added character {character.Name} to crew list");
|
||||
}
|
||||
ushort itemCount = msg.ReadUInt16();
|
||||
for (int j = 0; j < itemCount; j++)
|
||||
{
|
||||
Item.ReadSpawnData(msg);
|
||||
}
|
||||
}
|
||||
if (characters.Contains(null))
|
||||
{
|
||||
throw new System.Exception("Error in EscortMission.ClientReadInitial: character list contains null (mission: " + mission.Prefab.Identifier + ")");
|
||||
}
|
||||
|
||||
if (characters.Count != characterCount)
|
||||
{
|
||||
throw new System.Exception("Error in EscortMission.ClientReadInitial: character count does not match the server count (" + characterCount + " != " + characters.Count + "mission: " + mission.Prefab.Identifier + ")");
|
||||
}
|
||||
|
||||
InitCharacters();
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
using Barotrauma;
|
||||
|
||||
namespace MoreLevelContent.Missions
|
||||
{
|
||||
// Client
|
||||
internal partial class TriangulationMission : Mission
|
||||
{
|
||||
public override bool DisplayAsCompleted => false;
|
||||
public override bool DisplayAsFailed => false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Barotrauma;
|
||||
using Barotrauma.Networking;
|
||||
|
||||
namespace MoreLevelContent.Networking
|
||||
{
|
||||
/// <summary>
|
||||
/// Client
|
||||
/// </summary>
|
||||
public static partial class NetUtil
|
||||
{
|
||||
internal static void SendServer(IWriteMessage outMsg, DeliveryMethod deliveryMethod = DeliveryMethod.Reliable)
|
||||
{
|
||||
if (GameMain.IsSingleplayer) return;
|
||||
GameMain.LuaCs.Networking.Send(outMsg, deliveryMethod);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
using Barotrauma;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MoreLevelContent.Shared.Generation;
|
||||
using Microsoft.Xna.Framework;
|
||||
using MoreLevelContent.Shared.Utils;
|
||||
using Barotrauma.Items.Components;
|
||||
using MoreLevelContent.Shared;
|
||||
using System;
|
||||
|
||||
public static class ClientDebugDraw
|
||||
{
|
||||
internal static void Draw(SpriteBatch spriteBatch, Camera cam)
|
||||
{
|
||||
if (Level.Loaded != null)
|
||||
{
|
||||
// spriteBatch.DrawCircle(new Vector2(Level.Loaded.StartPosition.X, -Level.Loaded.StartPosition.Y), CaveGenerationDirector.MIN_DIST_FROM_START, 16, Color.Red, thickness: 100);
|
||||
// spriteBatch.DrawLine(new Vector2(0, -Level.Loaded.StartPosition.Y + (Sonar.DefaultSonarRange / 2)), new Vector2(int.MaxValue, -Level.Loaded.StartPosition.Y + (Sonar.DefaultSonarRange / 2)), Color.Yellow, thickness: 2 / Screen.Selected.Cam.Zoom * GUI.Scale);
|
||||
}
|
||||
|
||||
foreach (var item in CaveGenerationDirector.Instance._InitialCaveCheckDebug)
|
||||
{
|
||||
GUI.DrawString(spriteBatch, new Vector2(item.Cell.Center.X, -item.Cell.Center.Y), "Cell", Color.Azure);
|
||||
}
|
||||
|
||||
foreach (var (from, to) in MissionGenerationDirector.DebugPoints)
|
||||
{
|
||||
var from1 = new Vector2(from.X, -from.Y);
|
||||
var to1 = new Vector2(to.X, -to.Y);
|
||||
spriteBatch.DrawCircle(from1, 20, 8, Color.Aqua);
|
||||
spriteBatch.DrawCircle(to1, 10, 8, Color.Yellow);
|
||||
GUI.DrawLine(spriteBatch, from1, to1, Color.Red, width: 5);
|
||||
}
|
||||
|
||||
if (CaveGenerationDirector.Instance.ActiveThalaCave != null) CaveGenerationDirector.Instance.ActiveThalaCave.DebugDraw(spriteBatch, cam);
|
||||
}
|
||||
}
|
||||
+354
@@ -0,0 +1,354 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Barotrauma.MoreLevelContent.Shared.Config;
|
||||
using Barotrauma.MoreLevelContent.Config;
|
||||
using MoreLevelContent.Shared;
|
||||
using OpenAL;
|
||||
|
||||
namespace Barotrauma.MoreLevelContent.Client.UI
|
||||
{
|
||||
public class ConfigMenu
|
||||
{
|
||||
public static ConfigMenu Instance { get; private set; }
|
||||
|
||||
private MLCConfig unsavedConfig;
|
||||
|
||||
private readonly GUIFrame mainFrame;
|
||||
private readonly GUILayoutGroup tabber;
|
||||
private readonly GUIFrame contentFrame;
|
||||
private readonly GUILayoutGroup bottom;
|
||||
private readonly Dictionary<Tab, (GUIButton Button, GUIFrame Content)> tabContents;
|
||||
|
||||
public ConfigMenu(RectTransform mainParent)
|
||||
{
|
||||
unsavedConfig = ConfigManager.Instance.Config;
|
||||
mainFrame = new GUIFrame(new RectTransform(Vector2.One, mainParent));
|
||||
var mainLayout = new GUILayoutGroup(new RectTransform(Vector2.One * 0.95f, mainFrame.RectTransform, Anchor.Center, Pivot.Center),
|
||||
isHorizontal: false, childAnchor: Anchor.TopRight);
|
||||
|
||||
_ = new GUITextBlock(new RectTransform((1.0f, 0.07f), mainLayout.RectTransform), TextManager.Get("mlc.config"),
|
||||
font: GUIStyle.LargeFont);
|
||||
|
||||
var tabberAndContentLayout = new GUILayoutGroup(new RectTransform((1.0f, 0.86f), mainLayout.RectTransform),
|
||||
isHorizontal: true);
|
||||
|
||||
void tabberPadding()
|
||||
=> new GUIFrame(new RectTransform((0.01f, 1.0f), tabberAndContentLayout.RectTransform), style: null);
|
||||
|
||||
|
||||
tabberPadding();
|
||||
tabber = new GUILayoutGroup(new RectTransform((0.06f, 1.0f), tabberAndContentLayout.RectTransform), isHorizontal: false) { AbsoluteSpacing = GUI.IntScale(5f) };
|
||||
tabberPadding();
|
||||
tabContents = new Dictionary<Tab, (GUIButton Button, GUIFrame Content)>();
|
||||
|
||||
contentFrame = new GUIFrame(new RectTransform((0.92f, 1.0f), tabberAndContentLayout.RectTransform),
|
||||
style: "InnerFrame");
|
||||
bottom = new GUILayoutGroup(new RectTransform((contentFrame.RectTransform.RelativeSize.X, 0.04f), mainLayout.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.01f };
|
||||
|
||||
var tabToSelect = Tab.Debug;
|
||||
tabToSelect = MakePermissionLockedTabs(tabToSelect);
|
||||
CreateDebugTab();
|
||||
|
||||
CreateBottomButtons();
|
||||
|
||||
SelectTab(tabToSelect);
|
||||
}
|
||||
|
||||
private Tab MakePermissionLockedTabs(Tab defaultTab)
|
||||
{
|
||||
// If we're not in single player
|
||||
if (!GameMain.IsSingleplayer)
|
||||
{
|
||||
// and we don't have perms
|
||||
if (!GameMain.Client.HasPermission(Networking.ClientPermissions.ManageSettings))
|
||||
return defaultTab; // don't create perm locked tabs
|
||||
}
|
||||
CreateGeneralTab();
|
||||
CreatePirateOutpostTab();
|
||||
return Tab.General;
|
||||
}
|
||||
|
||||
GUITextBlock moveRuinsChanceDisplay;
|
||||
GUITextBlock maxDistressCountDisplay;
|
||||
GUITextBlock distressSpawnChanceDisplay;
|
||||
private void CreateGeneralTab()
|
||||
{
|
||||
GUIFrame content = CreateNewContentFrame(Tab.General);
|
||||
var (left, right) = CreateSidebars(content);
|
||||
|
||||
Tickbox(left,
|
||||
TextManager.Get("mlc.settings.enablethalamuscave"),
|
||||
TextManager.Get("mlc.settings.enablethalamuscavetooltip"),
|
||||
unsavedConfig.NetworkedConfig.GeneralConfig.EnableThalamusCaves,
|
||||
(v) => unsavedConfig.NetworkedConfig.GeneralConfig.EnableThalamusCaves = v);
|
||||
|
||||
Tickbox(left,
|
||||
TextManager.Get("mlc.settings.enabledistressmissions"),
|
||||
TextManager.Get("mlc.settings.enabledistressmissionstooltip"),
|
||||
unsavedConfig.NetworkedConfig.GeneralConfig.EnableDistressMissions,
|
||||
(v) => unsavedConfig.NetworkedConfig.GeneralConfig.EnableDistressMissions = v);
|
||||
|
||||
Tickbox(left,
|
||||
TextManager.Get("mlc.settings.enablemapfeature"),
|
||||
TextManager.Get("mlc.settings.enablemapfeaturetooltip"),
|
||||
unsavedConfig.NetworkedConfig.GeneralConfig.EnableMapFeatures,
|
||||
(v) => unsavedConfig.NetworkedConfig.GeneralConfig.EnableMapFeatures = v);
|
||||
|
||||
Tickbox(left,
|
||||
TextManager.Get("mlc.settings.enablerelaystation"),
|
||||
TextManager.Get("mlc.settings.enablerelaystationtooltip"),
|
||||
unsavedConfig.NetworkedConfig.GeneralConfig.EnableRelayStations,
|
||||
(v) => unsavedConfig.NetworkedConfig.GeneralConfig.EnableRelayStations = v);
|
||||
|
||||
Tickbox(left,
|
||||
TextManager.Get("mlc.settings.enableconstructionsites"),
|
||||
TextManager.Get("mlc.settings.enableconstructionsitestooltip"),
|
||||
unsavedConfig.NetworkedConfig.GeneralConfig.EnableConstructionSites,
|
||||
(v) => unsavedConfig.NetworkedConfig.GeneralConfig.EnableConstructionSites = v);
|
||||
|
||||
|
||||
|
||||
|
||||
var moveRuinsChance = Label(left, TextManager.Get("mlc.settings.moveruins"), GUIStyle.SubHeadingFont);
|
||||
moveRuinsChanceDisplay = TextBlock(moveRuinsChance, TextManager.Get("mlc.settings.moveruinstooltip"));
|
||||
Slider(left, (0, 100), 100, (v) => $"{Round(v)}%",
|
||||
unsavedConfig.NetworkedConfig.GeneralConfig.RuinMoveChance,
|
||||
(v) => UpdateRuinMoveChance(v));
|
||||
|
||||
var maxActiveDistress = Label(left, TextManager.Get("mlc.settings.maxdistresscount"), GUIStyle.SubHeadingFont);
|
||||
maxDistressCountDisplay = TextBlock(maxActiveDistress, TextManager.Get("mlc.settings.maxdistresscounttooltip"));
|
||||
Slider(left, (0, 100), 100, (v) => $"{Round(v)}",
|
||||
unsavedConfig.NetworkedConfig.GeneralConfig.MaxActiveDistressBeacons,
|
||||
(v) => UpdateMaxDistress(v));
|
||||
|
||||
|
||||
var distressSpawnChance = Label(left, TextManager.Get("mlc.settings.spawndistresschance"), GUIStyle.SubHeadingFont);
|
||||
distressSpawnChanceDisplay = TextBlock(distressSpawnChance, TextManager.Get("mlc.settings.spawndistresschancetooltip"));
|
||||
Slider(left, (0, 100), 100, (v) => $"{Round(v)}",
|
||||
unsavedConfig.NetworkedConfig.GeneralConfig.DistressSpawnChance,
|
||||
(v) => UpdateDistressSpawnChance(v));
|
||||
|
||||
|
||||
void UpdateRuinMoveChance(float v)
|
||||
{
|
||||
unsavedConfig.NetworkedConfig.GeneralConfig.RuinMoveChance = Round(v);
|
||||
moveRuinsChanceDisplay.Text = TextManager.GetWithVariable("mlc.settings.spawnchance", "[chance]", Round(v).ToString()); ;
|
||||
}
|
||||
|
||||
void UpdateMaxDistress(float v)
|
||||
{
|
||||
unsavedConfig.NetworkedConfig.GeneralConfig.MaxActiveDistressBeacons = Round(v);
|
||||
maxDistressCountDisplay.Text = TextManager.GetWithVariable("mlc.settings.maxactive", "[max]", Round(v).ToString()); ;
|
||||
}
|
||||
|
||||
void UpdateDistressSpawnChance(float v)
|
||||
{
|
||||
unsavedConfig.NetworkedConfig.GeneralConfig.DistressSpawnChance = Round(v);
|
||||
distressSpawnChanceDisplay.Text = TextManager.GetWithVariable("mlc.settings.spawnchance", "[chance]", Round(v).ToString());
|
||||
}
|
||||
|
||||
|
||||
UpdateRuinMoveChance(unsavedConfig.NetworkedConfig.GeneralConfig.RuinMoveChance);
|
||||
UpdateMaxDistress(unsavedConfig.NetworkedConfig.GeneralConfig.MaxActiveDistressBeacons);
|
||||
UpdateDistressSpawnChance(unsavedConfig.NetworkedConfig.GeneralConfig.DistressSpawnChance);
|
||||
|
||||
GUITextBlock TextBlock(GUITextBlock container, RichString tooltip)
|
||||
{
|
||||
return new GUITextBlock(new RectTransform(new Vector2(1.0f, 1.0f), container.RectTransform), "", textAlignment: Alignment.CenterRight)
|
||||
{
|
||||
ToolTip = tooltip
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
GUITextBlock pirateSpawnChanceDisplay;
|
||||
|
||||
private void CreatePirateOutpostTab()
|
||||
{
|
||||
GUIFrame content = CreateNewContentFrame(Tab.PirateOutpost);
|
||||
var (left, right) = CreateSidebars(content);
|
||||
|
||||
Tickbox(left,
|
||||
TextManager.Get("mlc.settings.enablepiratebase"),
|
||||
TextManager.Get("mlc.settings.enablepiratebasetooltip"),
|
||||
unsavedConfig.NetworkedConfig.PirateConfig.EnablePirateBases,
|
||||
(v) => unsavedConfig.NetworkedConfig.PirateConfig.EnablePirateBases = v);
|
||||
|
||||
|
||||
// If the pirate outpost is displayed on sonar
|
||||
Tickbox(left,
|
||||
TextManager.Get("mlc.config.piratedisplaysonar"),
|
||||
TextManager.Get("mlc.config.piratedisplaysonartooltip"),
|
||||
unsavedConfig.NetworkedConfig.PirateConfig.DisplaySonarMarker,
|
||||
(v) => unsavedConfig.NetworkedConfig.PirateConfig.DisplaySonarMarker = v);
|
||||
|
||||
// If the pirate difficulty should scale with server memebers
|
||||
Tickbox(left,
|
||||
TextManager.Get("mlc.config.piratescalediff"),
|
||||
TextManager.Get("mlc.config.piratescaledifftooltip"),
|
||||
unsavedConfig.NetworkedConfig.PirateConfig.AddDiffPerPlayer,
|
||||
(v) => unsavedConfig.NetworkedConfig.PirateConfig.AddDiffPerPlayer = v);
|
||||
}
|
||||
|
||||
private void CreateDebugTab()
|
||||
{
|
||||
GUIFrame content = CreateNewContentFrame(Tab.Debug);
|
||||
var (left, right) = CreateSidebars(content);
|
||||
|
||||
Tickbox(left, TextManager.Get("mlc.config.debugverbose"), TextManager.Get("mlc.config.debugverbosetooltip"), unsavedConfig.Client.Verbose, (v) => unsavedConfig.Client.Verbose = v);
|
||||
Tickbox(left, TextManager.Get("mlc.config.debuginternal"), TextManager.Get("mlc.config.debuginternaltooltip"), unsavedConfig.Client.Internal, (v) => unsavedConfig.Client.Internal = v);
|
||||
|
||||
GUIButton showPatchNotes = new GUIButton(NewItemRectT(left), text: "Patch Notes")
|
||||
{
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
MoreLevelContent.Client.UI.PatchNotes.Open();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void CreateBottomButtons()
|
||||
{
|
||||
GUIButton cancelButton =
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), bottom.RectTransform), text: "Cancel")
|
||||
{
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
GUIButton applyButton =
|
||||
new GUIButton(new RectTransform(new Vector2(1.0f, 1.0f), bottom.RectTransform), text: "Apply")
|
||||
{
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
ConfigManager.Instance.SetConfig(unsavedConfig);
|
||||
mainFrame.Flash(color: GUIStyle.Green);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private void Tickbox(GUILayoutGroup parent, LocalizedString label, LocalizedString tooltip, bool currentValue, Action<bool> setter)
|
||||
{
|
||||
var tickbox = new GUITickBox(NewItemRectT(parent), label)
|
||||
{
|
||||
Selected = currentValue,
|
||||
ToolTip = tooltip,
|
||||
OnSelected = (tb) =>
|
||||
{
|
||||
setter(tb.Selected);
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private int Round(float v) => (int)MathF.Round(v);
|
||||
|
||||
private void Slider(GUILayoutGroup parent, Vector2 range, int steps, Func<float, string> labelFunc, float currentValue, Action<float> setter, LocalizedString tooltip = null)
|
||||
{
|
||||
var layout = new GUILayoutGroup(NewItemRectT(parent), isHorizontal: true);
|
||||
var slider = new GUIScrollBar(new RectTransform((0.82f, 1.0f), layout.RectTransform), style: "GUISlider")
|
||||
{
|
||||
Range = range,
|
||||
BarScrollValue = currentValue,
|
||||
Step = 1.0f / (steps - 1),
|
||||
BarSize = 1.0f / steps
|
||||
};
|
||||
if (tooltip != null)
|
||||
{
|
||||
slider.ToolTip = tooltip;
|
||||
}
|
||||
var label = new GUITextBlock(new RectTransform((0.18f, 1.0f), layout.RectTransform),
|
||||
labelFunc(currentValue), wrap: false, textAlignment: Alignment.Center);
|
||||
slider.OnMoved = (sb, val) =>
|
||||
{
|
||||
label.Text = labelFunc(sb.BarScrollValue);
|
||||
setter(sb.BarScrollValue);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
private static GUITextBlock Label(GUILayoutGroup parent, LocalizedString str, GUIFont font) => new GUITextBlock(NewItemRectT(parent), str, font: font);
|
||||
private static RectTransform NewItemRectT(GUILayoutGroup parent)
|
||||
=> new RectTransform((1.0f, 0.06f), parent.RectTransform, Anchor.CenterLeft);
|
||||
|
||||
private static (GUILayoutGroup Left, GUILayoutGroup Right) CreateSidebars(GUIFrame parent, bool split = false)
|
||||
{
|
||||
GUILayoutGroup layout = new GUILayoutGroup(new RectTransform(Vector2.One, parent.RectTransform), isHorizontal: true);
|
||||
GUILayoutGroup left = new GUILayoutGroup(new RectTransform((0.4875f, 1.0f), layout.RectTransform), isHorizontal: false);
|
||||
var centerFrame = new GUIFrame(new RectTransform((0.025f, 1.0f), layout.RectTransform), style: null);
|
||||
if (split)
|
||||
{
|
||||
_ = new GUICustomComponent(new RectTransform(Vector2.One, centerFrame.RectTransform),
|
||||
onDraw: (sb, c) => sb.DrawLine((c.Rect.Center.X, c.Rect.Top), (c.Rect.Center.X, c.Rect.Bottom), GUIStyle.TextColorDim, 2f));
|
||||
}
|
||||
GUILayoutGroup right = new GUILayoutGroup(new RectTransform((0.4875f, 1.0f), layout.RectTransform), isHorizontal: false);
|
||||
return (left, right);
|
||||
}
|
||||
|
||||
private GUIFrame CreateNewContentFrame(Tab tab)
|
||||
{
|
||||
var content = new GUIFrame(new RectTransform(Vector2.One * 0.95f, contentFrame.RectTransform, Anchor.Center, Pivot.Center), style: null);
|
||||
AddButtonToTabber(tab, content);
|
||||
return content;
|
||||
}
|
||||
|
||||
private void AddButtonToTabber(Tab tab, GUIFrame content)
|
||||
{
|
||||
var button = new GUIButton(new RectTransform(Vector2.One, tabber.RectTransform, Anchor.TopLeft, Pivot.TopLeft, scaleBasis: ScaleBasis.Smallest), "", style: $"SettingsMenuTab.{tab}")
|
||||
{
|
||||
ToolTip = TextManager.Get($"SettingsTab.{tab}"),
|
||||
OnClicked = (b, _) =>
|
||||
{
|
||||
SelectTab(tab);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
button.RectTransform.MaxSize = RectTransform.MaxPoint;
|
||||
button.Children.ForEach(c => c.RectTransform.MaxSize = RectTransform.MaxPoint);
|
||||
|
||||
tabContents.Add(tab, (button, content));
|
||||
}
|
||||
|
||||
public void SelectTab(Tab tab)
|
||||
{
|
||||
SwitchContent(tabContents[tab].Content);
|
||||
tabber.Children.ForEach(c =>
|
||||
{
|
||||
if (c is GUIButton btn) { btn.Selected = btn == tabContents[tab].Button; }
|
||||
});
|
||||
}
|
||||
|
||||
private void SwitchContent(GUIFrame newContent)
|
||||
{
|
||||
contentFrame.Children.ForEach(c => c.Visible = false);
|
||||
newContent.Visible = true;
|
||||
}
|
||||
|
||||
public static ConfigMenu Create(RectTransform mainParent)
|
||||
{
|
||||
Instance?.Close();
|
||||
Instance = new ConfigMenu(mainParent);
|
||||
return Instance;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
mainFrame.Parent.RemoveChild(mainFrame);
|
||||
Instance = null;
|
||||
ConfigManager.Instance.SettingsOpen = false;
|
||||
}
|
||||
|
||||
public enum Tab
|
||||
{
|
||||
General,
|
||||
PirateOutpost,
|
||||
Debug
|
||||
}
|
||||
}
|
||||
}
|
||||
+273
@@ -0,0 +1,273 @@
|
||||
using Barotrauma.MoreLevelContent.Shared.Utils;
|
||||
using System.Reflection;
|
||||
using MoreLevelContent;
|
||||
using HarmonyLib;
|
||||
using MoreLevelContent.Shared.Data;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework.Graphics;
|
||||
using MoreLevelContent.Shared;
|
||||
using MoreLevelContent.Shared.Generation;
|
||||
using System.Threading;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection.Emit;
|
||||
using Barotrauma.MoreLevelContent.Config;
|
||||
|
||||
namespace Barotrauma.MoreLevelContent.Client.UI
|
||||
{
|
||||
public class MapUI : Singleton<MapUI>
|
||||
{
|
||||
static FieldInfo zoomLevel;
|
||||
static FieldInfo tooltipField;
|
||||
static FieldInfo pendingSubInfoField;
|
||||
static MethodInfo isInFogOfWar;
|
||||
static bool _DrawingConnections = false;
|
||||
|
||||
|
||||
public override void Setup()
|
||||
{
|
||||
var drawConnection = typeof(Map).GetMethod("DrawConnection", BindingFlags.NonPublic | BindingFlags.Instance);
|
||||
var draw = AccessTools.Method(typeof(Map), nameof(Map.Draw));
|
||||
|
||||
zoomLevel = typeof(Map).GetField("zoom", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
tooltipField = typeof(Map).GetField("tooltip", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
pendingSubInfoField = AccessTools.Field(typeof(Map), "pendingSubInfo");
|
||||
isInFogOfWar = AccessTools.Method(typeof(Map), "IsInFogOfWar");
|
||||
|
||||
_ = Main.Harmony.Patch(draw, transpiler: new HarmonyMethod(AccessTools.Method(typeof(MapUI), nameof(TranspileMapDraw))));
|
||||
_ = Main.Harmony.Patch(drawConnection, postfix: new HarmonyMethod(GetType().GetMethod(nameof(OnDrawConnection), BindingFlags.NonPublic | BindingFlags.Static)));
|
||||
}
|
||||
|
||||
private static SubmarineInfo.PendingSubInfo pendingSubInfo;
|
||||
|
||||
private static IEnumerable<CodeInstruction> TranspileMapDraw(IEnumerable<CodeInstruction> instructions, ILGenerator il)
|
||||
{
|
||||
Log.Debug("Transpiling map draw...");
|
||||
bool finished = false;
|
||||
var code = new List<CodeInstruction>(instructions);
|
||||
for (int i = 0; i < code.Count; i++)
|
||||
{
|
||||
if (finished == false && code[i].opcode == OpCodes.Stloc_S && code[i].operand.ToString() == "Barotrauma.LocationConnection (32)")
|
||||
{
|
||||
finished = true;
|
||||
yield return code[i];
|
||||
yield return new CodeInstruction(OpCodes.Ldloc_S, code[i].operand); // Location connection
|
||||
yield return new CodeInstruction(OpCodes.Ldarg_0); // Map
|
||||
yield return new CodeInstruction(OpCodes.Ldarg_2); // Sprite batch
|
||||
yield return new CodeInstruction(OpCodes.Ldloc_1); // View area
|
||||
yield return new CodeInstruction(OpCodes.Ldloc_S, (byte)4); // View Offset
|
||||
yield return new CodeInstruction(OpCodes.Call, AccessTools.Method(typeof(MapUI), nameof(DrawRevealedFeatures)));
|
||||
yield return new CodeInstruction(OpCodes.Ldloc_S, code[i].operand);
|
||||
}
|
||||
yield return code[i];
|
||||
}
|
||||
if (!finished) Log.Error("Failed to find map transpile injection point!");
|
||||
}
|
||||
|
||||
private static void DrawRevealedFeatures(LocationConnection connection, Map map, SpriteBatch spriteBatch, Rectangle viewArea, Vector2 viewOffset)
|
||||
{
|
||||
// Skip if we don't have a feature or pirate base
|
||||
if (!CheckValid()) return;
|
||||
|
||||
// Both sides are in fog of war
|
||||
bool inFow = (bool)isInFogOfWar.Invoke(map, new object[] { connection.Locations[0] }) && (bool)isInFogOfWar.Invoke(map, new object[] { connection.Locations[1] });
|
||||
if (inFow)
|
||||
{
|
||||
DrawCustomConnections(spriteBatch, connection, viewArea, viewOffset, map, true);
|
||||
//Log.Debug("Drew custom connection");
|
||||
}
|
||||
|
||||
bool CheckValid()
|
||||
{
|
||||
var feature = connection.LevelData.MLC().MapFeatureData;
|
||||
var pirateBase = connection.LevelData.MLC().PirateData;
|
||||
|
||||
|
||||
if (pirateBase.HasPirateBase && pirateBase.Revealed) return true;
|
||||
// Not valid if we don't have a feature
|
||||
if (!feature.HasFeature) return false;
|
||||
// Not valid if the feature isn't revealed
|
||||
if (!feature.Revealed) return false;
|
||||
// Not valid if the feature starts revealed
|
||||
if (!feature.Feature.Display.HideUntilRevealed) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private static void OnDrawConnection(SpriteBatch spriteBatch, LocationConnection connection, Rectangle viewArea, Vector2 viewOffset, Map __instance, bool __state)
|
||||
{
|
||||
if (__state) return;
|
||||
DrawCustomConnections(spriteBatch, connection, viewArea, viewOffset, __instance, false);
|
||||
}
|
||||
|
||||
private static void DrawCustomConnections(SpriteBatch spriteBatch, LocationConnection connection, Rectangle viewArea, Vector2 viewOffset, Map __instance, bool drawInFOW)
|
||||
{
|
||||
_DrawingConnections = false;
|
||||
if (connection == null || spriteBatch == null) return;
|
||||
LevelData_MLCData data = connection.LevelData.MLC();
|
||||
Vector2? connectionStart = null;
|
||||
Vector2? connectionEnd = null;
|
||||
Vector2 rectCenter = viewArea.Center.ToVector2();
|
||||
int startIndex = connection.CrackSegments.Count > 2 ? 1 : 0;
|
||||
int endIndex = connection.CrackSegments.Count > 2 ? connection.CrackSegments.Count - 1 : connection.CrackSegments.Count;
|
||||
float zoom = (float)zoomLevel.GetValue(__instance);
|
||||
int iconCount, iconIndex = GetIconIndex(connection);
|
||||
for (int i = startIndex; i < endIndex; i++)
|
||||
{
|
||||
var segment = connection.CrackSegments[i];
|
||||
Vector2 start = rectCenter + (segment[0] + viewOffset) * zoom;
|
||||
Vector2 end = rectCenter + (segment[1] + viewOffset) * zoom;
|
||||
connectionEnd = end;
|
||||
if (!connectionStart.HasValue) { connectionStart = start; }
|
||||
}
|
||||
iconCount = GetIconCount(__instance, connection);
|
||||
|
||||
if (drawInFOW)
|
||||
{
|
||||
iconCount = 0;
|
||||
DrawMapFeature(data);
|
||||
DrawPirateBase();
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.HasBeaconConstruction && ConfigManager.Instance.Config.NetworkedConfig.GeneralConfig.EnableConstructionSites)
|
||||
{
|
||||
LocalizedString localizedString = TextManager.GetWithVariable("mlc.beaconconsttooltip", "[requestedsupplies]", data.GetRequestedSupplies());
|
||||
DrawIcon("BeaconConst", (int)(28 * zoom), RichString.Rich(localizedString));
|
||||
}
|
||||
|
||||
if (data.HasDistress && ConfigManager.Instance.Config.NetworkedConfig.GeneralConfig.EnableDistressMissions)
|
||||
{
|
||||
string tooltip = "mlc.distresstooltip";
|
||||
string iconStyle = "DistressBeacon";
|
||||
if (data.DistressStepsLeft <= 3)
|
||||
{
|
||||
tooltip = "mlc.distresstooltipfaint";
|
||||
iconStyle = "DistressBeaconFaint";
|
||||
}
|
||||
|
||||
LocalizedString localizedString = TextManager.Get(tooltip);
|
||||
DrawIcon(iconStyle, (int)(28 * zoom), RichString.Rich(localizedString));
|
||||
}
|
||||
|
||||
if (data.HasLostCargo)
|
||||
{
|
||||
DrawIcon("LostCargo", (int)(28 * zoom), RichString.Rich(TextManager.Get("mlc.lostcargotooltip")));
|
||||
}
|
||||
|
||||
if (data.HasRelayStation && ConfigManager.Instance.Config.NetworkedConfig.GeneralConfig.EnableRelayStations)
|
||||
{
|
||||
var iconName = data.RelayStationStatus == RelayStationStatus.Active ? "RelayStationActive" : "RelayStationInactive";
|
||||
var locString = data.RelayStationStatus == RelayStationStatus.Active ? "mlc.relaystationtooltip.active" : "mlc.relaystationtooltip.inactive";
|
||||
LocalizedString localizedString = TextManager.Get(locString);
|
||||
DrawIcon(iconName, (int)(28 * zoom), RichString.Rich(localizedString));
|
||||
}
|
||||
|
||||
DrawPirateBase();
|
||||
DrawMapFeature(data);
|
||||
|
||||
void DrawMapFeature(LevelData_MLCData data)
|
||||
{
|
||||
if (!ConfigManager.Instance.Config.NetworkedConfig.GeneralConfig.EnableMapFeatures) return;
|
||||
if (data.MapFeatureData.Name.IsEmpty) return;
|
||||
if (!data.MapFeatureData.Revealed && !GameMain.DebugDraw && !Commands.DisplayAllMapLocations) return;
|
||||
if (!MapFeatureModule.TryGetFeature(data.MapFeatureData.Name, out MapFeature feature))
|
||||
{
|
||||
Log.Error($"Failed to find map feature with identifier {data.MapFeatureData.Name}!!");
|
||||
return;
|
||||
}
|
||||
var tooltip = TextManager.Get(feature.Display.Tooltip);
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
tooltip = $"{tooltip.Value} + {data.MapFeatureData.Revealed}";
|
||||
}
|
||||
DrawIcon(feature.Display.Icon, (int)(28 * zoom), RichString.Rich(tooltip));
|
||||
}
|
||||
|
||||
void DrawPirateBase()
|
||||
{
|
||||
if (ConfigManager.Instance.Config.NetworkedConfig.PirateConfig.EnablePirateBases &&
|
||||
data.PirateData.HasPirateBase &&
|
||||
(GameMain.DebugDraw || Commands.DisplayAllMapLocations || data.PirateData.Revealed))
|
||||
{ } else { return; }
|
||||
LocalizedString text = "";
|
||||
switch (data.PirateData.Status)
|
||||
{
|
||||
case PirateOutpostStatus.Active:
|
||||
text = TextManager.Get("piratebase.active");
|
||||
break;
|
||||
case PirateOutpostStatus.Destroyed:
|
||||
text = TextManager.Get("piratebase.destroyed");
|
||||
break;
|
||||
case PirateOutpostStatus.Husked:
|
||||
text = TextManager.Get("piratebase.husked");
|
||||
break;
|
||||
}
|
||||
|
||||
if (GameMain.DebugDraw)
|
||||
{
|
||||
text += $" Revealed: {data.PirateData.Revealed}";
|
||||
}
|
||||
|
||||
|
||||
DrawIcon(data.PirateData.Status == PirateOutpostStatus.Active ? "PirateBase" : "PirateBaseDestroyed", (int)(28 * zoom), RichString.Rich(text));
|
||||
}
|
||||
|
||||
void DrawIcon(string iconStyle, int iconSize, RichString tooltipText)
|
||||
{
|
||||
Vector2 iconPos = (connectionStart.Value + connectionEnd.Value) / 2;
|
||||
Vector2 iconDiff = Vector2.Normalize(connectionEnd.Value - connectionStart.Value) * iconSize;
|
||||
|
||||
iconPos += (iconDiff * -(iconCount - 1) / 2.0f) + iconDiff * iconIndex;
|
||||
|
||||
var style = GUIStyle.GetComponentStyle(iconStyle);
|
||||
if (style == null)
|
||||
{
|
||||
Log.Error($"Unable to find icon style {style}");
|
||||
return;
|
||||
}
|
||||
bool mouseOn = Vector2.DistanceSquared(iconPos, PlayerInput.MousePosition) < iconSize * iconSize && IsPreferredTooltip(iconPos, __instance);
|
||||
Sprite iconSprite = style.GetDefaultSprite();
|
||||
iconSprite.Draw(spriteBatch, iconPos, (mouseOn ? style.HoverColor : style.Color) * 0.7f,
|
||||
scale: iconSize / iconSprite.size.X);
|
||||
if (mouseOn)
|
||||
{
|
||||
tooltipField.SetValue(__instance, (new Rectangle((iconPos - Vector2.One * iconSize / 2).ToPoint(), new Point(iconSize)), tooltipText));
|
||||
}
|
||||
iconIndex++;
|
||||
}
|
||||
|
||||
bool IsPreferredTooltip(Vector2 tooltipPos, Map map) => tooltipField.GetValue(map) == null || Vector2.DistanceSquared(tooltipPos, PlayerInput.MousePosition) < Vector2.DistanceSquared((tooltipField.GetValue(map) as (Rectangle targetArea, RichString tip)?).Value.targetArea.Center.ToVector2(), PlayerInput.MousePosition);
|
||||
|
||||
int GetIconCount(Map __instance, LocationConnection connection)
|
||||
{
|
||||
int iconCount = 0;
|
||||
float subCrushDepth = SubmarineInfo.GetSubCrushDepth(SubmarineSelection.CurrentOrPendingSubmarine(), ref pendingSubInfo);
|
||||
if (connection.LevelData.InitialDepth * Physics.DisplayToRealWorldRatio > subCrushDepth)
|
||||
{
|
||||
iconIndex++;
|
||||
iconCount++;
|
||||
}
|
||||
else if ((connection.LevelData.InitialDepth + connection.LevelData.Size.Y) * Physics.DisplayToRealWorldRatio > subCrushDepth)
|
||||
{
|
||||
iconIndex++;
|
||||
iconCount++;
|
||||
}
|
||||
|
||||
return iconCount;
|
||||
}
|
||||
|
||||
int GetIconIndex(LocationConnection connection)
|
||||
{
|
||||
int index = 0;
|
||||
if (connection.LevelData.HasBeaconStation) index++;
|
||||
if (connection.Locked) index++;
|
||||
if (connection.LevelData.HasHuntingGrounds) index++;
|
||||
|
||||
return index;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Barotrauma.MoreLevelContent.Config;
|
||||
using MoreLevelContent;
|
||||
|
||||
namespace Barotrauma.MoreLevelContent.Client.UI
|
||||
{
|
||||
public class PatchNotes
|
||||
{
|
||||
private readonly GUIFrame mainFrame;
|
||||
private readonly GUIFrame backgroundBlocker;
|
||||
private readonly GUIFrame contentFrame;
|
||||
private readonly GUILayoutGroup bottom;
|
||||
public static PatchNotes Instance { get; private set; }
|
||||
|
||||
public PatchNotes()
|
||||
{
|
||||
backgroundBlocker = new GUIFrame(new RectTransform(Vector2.One, Screen.Selected.Frame.RectTransform, Anchor.Center), style: null);
|
||||
_ = new GUIFrame(new RectTransform(GUI.Canvas.RelativeSize, backgroundBlocker.RectTransform, Anchor.Center), style: "GUIBackgroundBlocker");
|
||||
|
||||
var mainParent = new GUIFrame(new RectTransform(new Vector2(0.5f, 0.55f), backgroundBlocker.RectTransform, Anchor.Center, scaleBasis: ScaleBasis.Smallest) { MinSize = new Point(640, 480) }).RectTransform;
|
||||
|
||||
mainFrame = new GUIFrame(new RectTransform(Vector2.One, mainParent));
|
||||
var mainLayout = new GUILayoutGroup(new RectTransform(Vector2.One * 0.95f, mainFrame.RectTransform, Anchor.Center, Pivot.Center),
|
||||
isHorizontal: false, childAnchor: Anchor.TopRight);
|
||||
|
||||
_ = new GUITextBlock(new RectTransform((1.0f, 0.07f), mainLayout.RectTransform), TextManager.GetWithVariable("mlc.patchnote", "[version]", Main.Version),
|
||||
font: GUIStyle.LargeFont);
|
||||
|
||||
// Padding
|
||||
_ = new GUIFrame(new RectTransform((0.01f, 0.01f), mainLayout.RectTransform), style: null);
|
||||
|
||||
contentFrame = new GUIFrame(new RectTransform((1.0f, 0.8f), mainLayout.RectTransform),
|
||||
style: "InnerFrame");
|
||||
|
||||
_ = new GUITextBlock(new RectTransform((1.0f, 1.0f), contentFrame.RectTransform), TextManager.Get("mlc.patchnotes").Value ?? "hot spicy meme action", textAlignment: Alignment.TopLeft);
|
||||
|
||||
// Padding
|
||||
_ = new GUIFrame(new RectTransform((0.01f, 0.01f), mainLayout.RectTransform), style: null);
|
||||
|
||||
bottom = new GUILayoutGroup(new RectTransform((1.0f, 0.04f), mainLayout.RectTransform), isHorizontal: true) { Stretch = true, RelativeSpacing = 0.01f };
|
||||
|
||||
CreateBottomButton();
|
||||
}
|
||||
|
||||
private void CreateBottomButton()
|
||||
{
|
||||
GUIButton cancelButton =
|
||||
new GUIButton(new RectTransform(new Vector2(0.5f, 0.5f), bottom.RectTransform), text: "close")
|
||||
{
|
||||
OnClicked = (btn, obj) =>
|
||||
{
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static void Open()
|
||||
{
|
||||
Instance?.Close();
|
||||
Instance = new PatchNotes();
|
||||
ConfigManager.ShouldDisplayPatchNotes = false;
|
||||
}
|
||||
|
||||
public void Close()
|
||||
{
|
||||
mainFrame.Parent.RemoveChild(mainFrame);
|
||||
backgroundBlocker.Parent.RemoveChild(backgroundBlocker);
|
||||
if (Instance == this) { Instance = null; }
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user