Decompiled source of FusionTeams v0.1.5

FusionTeams.dll

Decompiled a day ago
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.CompilerServices;
using BoneLib.BoneMenu;
using FusionTeams;
using HarmonyLib;
using LabFusion.Entities;
using LabFusion.Network;
using LabFusion.Player;
using LabFusion.UI.Popups;
using MelonLoader;
using UnityEngine;

[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: MelonInfo(typeof(TeamMod), "Fusion Teams", "0.1.5", "Codex", null)]
[assembly: MelonGame("Stress Level Zero", "BONELAB")]
[assembly: CompilationRelaxations(8)]
[assembly: AssemblyVersion("0.0.0.0")]
namespace FusionTeams;

internal static class TeamState
{
	internal const string MetadataKey = "FusionTeams.Team";

	internal const string Red = "Red";

	internal const string Blue = "Blue";

	internal const string NoTeam = "None";

	internal static bool TryGetLocalTeam(out string team)
	{
		team = null;
		PlayerMetadata metadata = LocalPlayer.Metadata;
		if (metadata == null || !metadata.IsValid || metadata.Metadata == null)
		{
			return false;
		}
		return metadata.Metadata.TryGetMetadata("FusionTeams.Team", ref team) && IsPlayableTeam(team);
	}

	internal static bool TryGetTeam(NetworkPlayer player, out string team)
	{
		team = null;
		if (player == null || player.PlayerID == null || !player.PlayerID.IsValid)
		{
			return false;
		}
		return TryGetTeam(player.PlayerID, out team);
	}

	internal static bool TryGetTeam(PlayerID playerID, out string team)
	{
		team = null;
		if (playerID == null || !playerID.IsValid)
		{
			return false;
		}
		PlayerMetadata metadata = playerID.Metadata;
		if (metadata == null || !metadata.IsValid || metadata.Metadata == null)
		{
			return false;
		}
		return metadata.Metadata.TryGetMetadata("FusionTeams.Team", ref team) && IsPlayableTeam(team);
	}

	internal static bool IsPlayableTeam(string team)
	{
		return string.Equals(team, "Red", (StringComparison)4) || string.Equals(team, "Blue", (StringComparison)4);
	}

	internal static Color ColorFor(string team)
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_005f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0064: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		if (string.Equals(team, "Red", (StringComparison)4))
		{
			return new Color(1f, 0.22f, 0.22f, 1f);
		}
		if (string.Equals(team, "Blue", (StringComparison)4))
		{
			return new Color(0.22f, 0.52f, 1f, 1f);
		}
		return Color.white;
	}
}
public sealed class TeamMod : MelonMod
{
	private const float TeamChangeLockDuration = 40f;

	private static readonly FieldInfo NameTagField = typeof(NetworkPlayer).GetField("_nametag", (BindingFlags)36);

	private static readonly PropertyInfo NameTagColorProperty = ((NameTagField == (FieldInfo)null) ? null : NameTagField.FieldType.GetProperty("Color", (BindingFlags)20));

	private string _pendingTeam;

	private bool _pendingLeave;

	private bool _menuCreated;

	private float _teamChangeLockedUntil;

	private int _lastDisplayedLockSeconds = -1;

	private FunctionElement _joinRedButton;

	private FunctionElement _joinBlueButton;

	private FunctionElement _leaveTeamButton;

	private float _nextVisualRefresh;

	private readonly Dictionary<byte, Color> _savedNametagColors = new Dictionary<byte, Color>();

	public override void OnInitializeMelon()
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		new Harmony("FusionTeams").PatchAll(Assembly.GetExecutingAssembly());
	}

	public override void OnUpdate()
	{
		if (!_menuCreated && Page.Root != null)
		{
			CreateMenu();
		}
		PublishPendingChoice();
		if (!(Time.unscaledTime < _nextVisualRefresh))
		{
			_nextVisualRefresh = Time.unscaledTime + 0.25f;
			RefreshTeamMenuLabels();
			RefreshTeamColors();
		}
	}

	private void CreateMenu()
	{
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_001f: Unknown result type (might be due to invalid IL or missing references)
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Expected O, but got Unknown
		//IL_0041: Unknown result type (might be due to invalid IL or missing references)
		//IL_004d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Expected O, but got Unknown
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Expected O, but got Unknown
		Page val = Page.Root.CreatePage("Fusion Teams", Color.cyan, 0, true);
		_joinRedButton = val.CreateFunction("Join Red", Color.red, (Action)([CompilerGenerated] () =>
		{
			SelectTeam("Red");
		}));
		_joinBlueButton = val.CreateFunction("Join Blue", Color.blue, (Action)([CompilerGenerated] () =>
		{
			SelectTeam("Blue");
		}));
		_leaveTeamButton = val.CreateFunction("Leave Team", Color.white, new Action(LeaveTeam));
		RefreshTeamMenuLabels();
		_menuCreated = true;
		((MelonBase)this).LoggerInstance.Msg("Team choices added to BoneMenu.");
	}

	private void SelectTeam(string team)
	{
		if (IsTeamChangeLocked())
		{
			ShowTeamChangeLock();
			return;
		}
		if (TryGetCurrentTeamValue(out var team2) && string.Equals(team2, team, (StringComparison)4))
		{
			ShowInfo("You are already on " + team + " team.");
			return;
		}
		_pendingTeam = team;
		_pendingLeave = false;
		PublishPendingChoice();
	}

	private void LeaveTeam()
	{
		if (IsTeamChangeLocked())
		{
			ShowTeamChangeLock();
			return;
		}
		if (!TryGetCurrentTeamValue(out var team) || !TeamState.IsPlayableTeam(team))
		{
			ShowInfo("You are already unassigned.");
			return;
		}
		_pendingTeam = null;
		_pendingLeave = true;
		PublishPendingChoice();
	}

	private void PublishPendingChoice()
	{
		if (_pendingTeam != null || _pendingLeave)
		{
			PlayerMetadata metadata = LocalPlayer.Metadata;
			if (metadata != null && metadata.IsValid && metadata.Metadata != null && (_pendingLeave ? metadata.Metadata.TrySetMetadata("FusionTeams.Team", "None") : metadata.Metadata.TrySetMetadata("FusionTeams.Team", _pendingTeam)))
			{
				((MelonBase)this).LoggerInstance.Msg(_pendingLeave ? "Left the team." : ("Joined team " + _pendingTeam + "."));
				_teamChangeLockedUntil = Time.unscaledTime + 40f;
				_lastDisplayedLockSeconds = -1;
				SetLeaveButtonLabel(_pendingLeave ? "No Team — free-for-all" : ("Leave " + _pendingTeam + " Team"));
				_pendingTeam = null;
				_pendingLeave = false;
			}
		}
	}

	private void SetLeaveButtonLabel(string label)
	{
		if (_leaveTeamButton != null)
		{
			((Element)_leaveTeamButton).ElementName = label;
		}
	}

	private bool IsTeamChangeLocked()
	{
		return Time.unscaledTime < _teamChangeLockedUntil;
	}

	private bool TryGetCurrentTeamValue(out string team)
	{
		team = null;
		PlayerMetadata metadata = LocalPlayer.Metadata;
		return metadata != null && metadata.IsValid && metadata.Metadata != null && metadata.Metadata.TryGetMetadata("FusionTeams.Team", ref team);
	}

	private void RefreshTeamMenuLabels()
	{
		if (_joinRedButton == null || _joinBlueButton == null || _leaveTeamButton == null)
		{
			return;
		}
		int num = Mathf.CeilToInt(_teamChangeLockedUntil - Time.unscaledTime);
		if (num > 0)
		{
			if (num != _lastDisplayedLockSeconds)
			{
				((Element)_joinRedButton).ElementName = string.Concat((object)"Join Red (locked ", (object)num, (object)"s)");
				((Element)_joinBlueButton).ElementName = string.Concat((object)"Join Blue (locked ", (object)num, (object)"s)");
				((Element)_leaveTeamButton).ElementName = string.Concat((object)"Change Team (locked ", (object)num, (object)"s)");
				_lastDisplayedLockSeconds = num;
			}
		}
		else
		{
			_lastDisplayedLockSeconds = -1;
			((Element)_joinRedButton).ElementName = "Join Red";
			((Element)_joinBlueButton).ElementName = "Join Blue";
			((Element)_leaveTeamButton).ElementName = ((TryGetCurrentTeamValue(out var team) && TeamState.IsPlayableTeam(team)) ? ("Leave " + team + " Team") : "No Team — free-for-all");
		}
	}

	private void ShowTeamChangeLock()
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Expected O, but got Unknown
		//IL_0038: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		int num = Mathf.CeilToInt(_teamChangeLockedUntil - Time.unscaledTime);
		if (num < 1)
		{
			num = 1;
		}
		Notification val = new Notification();
		val.Tag = "FusionTeams.TeamChangeLock";
		val.Title = new NotificationText("Team change locked");
		val.Message = new NotificationText(string.Concat((object)"You can change teams in ", (object)num, (object)" seconds."));
		val.ShowPopup = true;
		val.PopupLength = 3f;
		val.Type = (NotificationType)1;
		val.SaveToMenu = false;
		Notifier.Send(val);
	}

	private void ShowInfo(string message)
	{
		//IL_0001: Unknown result type (might be due to invalid IL or missing references)
		//IL_0007: Expected O, but got Unknown
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0026: Unknown result type (might be due to invalid IL or missing references)
		Notification val = new Notification();
		val.Tag = "FusionTeams.TeamInfo";
		val.Title = new NotificationText("Fusion Teams");
		val.Message = new NotificationText(message);
		val.ShowPopup = true;
		val.PopupLength = 2f;
		val.Type = (NotificationType)0;
		val.SaveToMenu = false;
		Notifier.Send(val);
	}

	private void RefreshTeamColors()
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_0141: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
		//IL_0120: Unknown result type (might be due to invalid IL or missing references)
		//IL_0125: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_0182: Unknown result type (might be due to invalid IL or missing references)
		//IL_0184: Unknown result type (might be due to invalid IL or missing references)
		//IL_019a: Unknown result type (might be due to invalid IL or missing references)
		if (NameTagField == (FieldInfo)null || NameTagColorProperty == (PropertyInfo)null)
		{
			return;
		}
		Enumerator<NetworkPlayer> enumerator = NetworkPlayer.Players.GetEnumerator();
		try
		{
			Color val2 = default(Color);
			while (enumerator.MoveNext())
			{
				NetworkPlayer current = enumerator.Current;
				try
				{
					if (current == null || current.PlayerID == null || !current.PlayerID.IsValid)
					{
						continue;
					}
					object value = NameTagField.GetValue((object)current);
					if (value == null)
					{
						continue;
					}
					object value2 = NameTagColorProperty.GetValue(value, (object[])null);
					if (!(value2 is Color))
					{
						continue;
					}
					byte smallID = current.PlayerID.SmallID;
					Color val = (Color)value2;
					if (TeamState.TryGetTeam(current, out var team))
					{
						if (current.playerSettings != null)
						{
							_savedNametagColors[smallID] = current.playerSettings.NametagColor;
						}
						else if (!_savedNametagColors.ContainsKey(smallID))
						{
							_savedNametagColors[smallID] = val;
						}
						val2 = TeamState.ColorFor(team);
						goto IL_0182;
					}
					if (current.playerSettings != null)
					{
						val2 = current.playerSettings.NametagColor;
						_savedNametagColors.Remove(smallID);
						goto IL_0182;
					}
					if (!_savedNametagColors.TryGetValue(smallID, ref val2))
					{
						continue;
					}
					_savedNametagColors.Remove(smallID);
					goto IL_0182;
					IL_0182:
					if (val != val2)
					{
						NameTagColorProperty.SetValue(value, (object)val2, (object[])null);
					}
				}
				catch (global::System.Exception ex)
				{
					((MelonBase)this).LoggerInstance.Warning("Could not update a Fusion nametag color: " + ex.Message);
				}
			}
		}
		finally
		{
			((global::System.IDisposable)enumerator/*cast due to .constrained prefix*/).Dispose();
		}
	}
}
[HarmonyPatch(typeof(NetworkCombatManager), "CanAttack")]
internal static class TeamFriendlyFirePatch
{
	[HarmonyPrefix]
	private static bool Prefix(NetworkPlayer player, ref bool __result)
	{
		string team;
		bool flag = TeamState.TryGetLocalTeam(out team);
		string team2;
		bool flag2 = TeamState.TryGetTeam(player, out team2);
		bool flag3 = flag && flag2 && string.Equals(team, team2, (StringComparison)4);
		__result = !flag3;
		return false;
	}
}
[HarmonyPatch(typeof(PlayerRepDamageMessage), "OnHandleMessage")]
internal static class IncomingTeamDamagePatch
{
	private static bool _loggedBlockedHit;

	[HarmonyPrefix]
	private static bool Prefix(ReceivedMessage received)
	{
		byte? sender = ((ReceivedMessage)(ref received)).Sender;
		if (!sender.HasValue)
		{
			return true;
		}
		PlayerID playerID = PlayerIDManager.GetPlayerID(sender.Value);
		if (!TeamState.TryGetTeam(playerID, out var team) || !TeamState.TryGetLocalTeam(out var team2))
		{
			return true;
		}
		if (!string.Equals(team, team2, (StringComparison)4))
		{
			return true;
		}
		if (!_loggedBlockedHit)
		{
			_loggedBlockedHit = true;
			MelonLogger.Msg("Blocked a same-team Fusion damage message.");
		}
		return false;
	}
}