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;
}
}