using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using HarmonyLib;
using Jotunn;
using Jotunn.Entities;
using Jotunn.Extensions;
using Jotunn.Managers;
using Jotunn.Utils;
using PvpOverhaul.API;
using PvpOverhaul.Compatibility;
using Splatform;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: AssemblyTitle("PvpOverhaul")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("PvpOverhaul")]
[assembly: AssemblyCopyright("Copyright © 2021")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("e3243d22-4307-4008-ba36-9f326008cde5")]
[assembly: AssemblyFileVersion("1.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.0.0")]
namespace PvPOverhaul
{
[HarmonyPatch(typeof(ItemData), "GetTooltip", new Type[]
{
typeof(ItemData),
typeof(int),
typeof(bool),
typeof(float),
typeof(int)
})]
public static class PvPResilienceTooltipPatch
{
private static void Postfix(ItemData item, ref string __result)
{
if (item?.m_customData != null)
{
float resilience = PvpResilienceAPI.GetResilience(item);
if (!(resilience <= 0f))
{
__result += $"\nRésilience : {resilience:0}";
}
}
}
}
}
namespace PvpOverhaul
{
internal class BountySystem
{
private class KillRecord
{
public string KillerId;
public string VictimId;
public DateTime TimestampUtc;
}
public class BountyState
{
public string PlayerId;
public DateTime StartUtc;
public DateTime EndUtc;
public int Tier;
public Vector3 LastKnownPosition;
}
public class LastHit
{
public string VictimName;
public string AttackerName;
public float Damage;
public float Time;
}
private readonly List _kills = new List();
private readonly Dictionary _activeBounties = new Dictionary();
private readonly Dictionary _lastHits = new Dictionary();
private readonly Dictionary _lastHitsByName = new Dictionary();
private readonly Dictionary _reportedDeaths = new Dictionary();
private readonly Dictionary _ignoredDeaths = new Dictionary();
private bool _loadedPersistence;
private float _nextTimerTick;
private float _nextPositionSync;
private float _nextFullSync;
public static BountySystem Instance { get; } = new BountySystem();
private string SaveDir => Path.Combine(Paths.ConfigPath, "PvPOverhaul");
private string SaveFile => Path.Combine(SaveDir, "bounties.txt");
private string LogDir => Path.Combine(Paths.ConfigPath, "PvPOverhaul", "Logs");
private TimeSpan KillWindow => TimeSpan.FromMinutes(PvPOverhaul.BountyKillWindowMinutes.Value);
private TimeSpan BountyDuration => TimeSpan.FromMinutes(PvPOverhaul.BountyDurationMinutes.Value);
public void Update()
{
if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
{
if (!_loadedPersistence)
{
_loadedPersistence = true;
LoadBounties();
}
if (Time.time >= _nextTimerTick)
{
_nextTimerTick = Time.time + 1f;
UpdateTimers();
}
if (Time.time >= _nextPositionSync)
{
_nextPositionSync = Time.time + 10f;
SyncBountyPositions();
}
if (Time.time >= _nextFullSync)
{
_nextFullSync = Time.time + 30f;
BroadcastAllBounties();
}
}
}
private void WriteServerLog(string message)
{
if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
{
Directory.CreateDirectory(LogDir);
string path = Path.Combine(LogDir, $"{DateTime.UtcNow:yyyy-MM-dd}.log");
string text = $"[{DateTime.UtcNow:HH:mm:ss} UTC] {message}";
File.AppendAllText(path, text + Environment.NewLine);
}
}
private bool IsPlayerOnline(string playerName)
{
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0030: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)ZNet.instance == (Object)null)
{
return false;
}
foreach (PlayerInfo player in ZNet.instance.GetPlayerList())
{
if (player.m_name == playerName)
{
return true;
}
}
return false;
}
private static string FormatMessage(string template, string killer = "", string victim = "", string player = "", int tier = 0, int duration = 0, int amount = 0, string item = "")
{
return template.Replace("{killer}", killer).Replace("{victim}", victim).Replace("{player}", player)
.Replace("{tier}", tier.ToString())
.Replace("{duration}", duration.ToString())
.Replace("{amount}", amount.ToString())
.Replace("{item}", item);
}
public void RegisterKill(string killerName, string victimName)
{
if (string.IsNullOrWhiteSpace(killerName) || string.IsNullOrWhiteSpace(victimName) || killerName == victimName)
{
return;
}
DateTime now = DateTime.UtcNow;
BountyState bounty = GetBounty(victimName);
ClearBounty(victimName, killed: true);
_kills.RemoveAll((KillRecord x) => x.KillerId == victimName);
SaveBounties();
if (bounty != null)
{
int num = Math.Max(1, bounty.Tier) * PvPOverhaul.BountyRewardAmountPerTier.Value;
string value = PvPOverhaul.BountyRewardItem.Value;
BountyRpc.SendRewardToPlayer(killerName, value, num);
_kills.RemoveAll((KillRecord x) => x.KillerId == killerName);
string message = FormatMessage(PvPOverhaul.MsgBountyReward.Value, killerName, victimName, "", 0, 0, num, value);
BountyRpc.BroadcastCenterMessage(message);
WriteServerLog($"REWARD | {killerName} claimed bounty on {victimName} | tier={bounty.Tier} | {num}x {value}");
}
_kills.Add(new KillRecord
{
KillerId = killerName,
VictimId = victimName,
TimestampUtc = now
});
CleanupOldKills(now);
int num2 = _kills.Count((KillRecord x) => x.KillerId == killerName && now - x.TimestampUtc <= KillWindow);
Logger.LogInfo((object)$"[PvPOverhaul SERVER] Kill registered: {killerName} -> {victimName} | kills={num2}");
WriteServerLog($"KILL | {killerName} killed {victimName} | kills={num2}");
BountyRpc.BroadcastCenterMessage(FormatMessage(PvPOverhaul.MsgKillBroadcast.Value, killerName, victimName));
if (bounty == null && num2 >= PvPOverhaul.BountyTriggerKillCount.Value)
{
ActivateOrRefreshBounty(killerName, num2, now);
}
}
private int GetTierFromKills(int kills)
{
return (int)Math.Floor(Math.Sqrt(kills));
}
private void ActivateOrRefreshBounty(string playerName, int killCount, DateTime now)
{
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_00dd: Unknown result type (might be due to invalid IL or missing references)
//IL_00de: 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_005d: 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_00f0: Unknown result type (might be due to invalid IL or missing references)
//IL_016d: Unknown result type (might be due to invalid IL or missing references)
int tierFromKills = GetTierFromKills(killCount);
DateTime dateTime = now.Add(BountyDuration);
Vector3 playerPosition = GetPlayerPosition(playerName);
if (!_activeBounties.TryGetValue(playerName, out var value))
{
value = new BountyState
{
PlayerId = playerName,
StartUtc = now,
EndUtc = dateTime,
Tier = tierFromKills,
LastKnownPosition = playerPosition
};
_activeBounties[playerName] = value;
Logger.LogInfo((object)("[PvPOverhaul SERVER] BOUNTY ACTIVATED: " + playerName));
WriteServerLog($"BOUNTY_ACTIVATED | {playerName} | tier={tierFromKills} | end={dateTime:o}");
BountyRpc.BroadcastCenterMessage(FormatMessage(PvPOverhaul.MsgBountyActivated.Value, "", "", playerName, tierFromKills));
}
else
{
value.EndUtc = dateTime;
value.Tier = tierFromKills;
if (playerPosition != Vector3.zero)
{
value.LastKnownPosition = playerPosition;
}
Logger.LogInfo((object)("[PvPOverhaul SERVER] BOUNTY REFRESHED: " + playerName));
WriteServerLog($"BOUNTY_REFRESHED | {playerName} | tier={tierFromKills} | end={dateTime:o}");
BountyRpc.BroadcastCenterMessage(FormatMessage(PvPOverhaul.MsgBountyRefreshed.Value, "", "", playerName, tierFromKills, PvPOverhaul.BountyDurationMinutes.Value));
}
SaveBounties();
if (IsPlayerOnline(playerName))
{
BountyRpc.BroadcastBountyCircle(playerName, active: true, value.LastKnownPosition, tierFromKills, dateTime);
}
}
public void UpdateBountyPositionFromClient(string playerName, Vector3 position)
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_002a: 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_0039: Unknown result type (might be due to invalid IL or missing references)
if (_activeBounties.TryGetValue(playerName, out var value) && !(position == Vector3.zero))
{
value.LastKnownPosition = position;
SaveBounties();
BountyRpc.BroadcastBountyCircle(playerName, active: true, position, value.Tier, value.EndUtc);
}
}
public void ClearBounty(string playerName, bool killed)
{
//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
if (_activeBounties.Remove(playerName))
{
Logger.LogInfo((object)("[PvPOverhaul SERVER] Bounty cleared: " + playerName));
WriteServerLog($"BOUNTY_CLEARED | {playerName} | killed={killed}");
if (killed)
{
BountyRpc.BroadcastCenterMessage(FormatMessage(PvPOverhaul.MsgBountyCleared.Value, "", "", playerName));
}
else
{
BountyRpc.BroadcastCenterMessage(FormatMessage(PvPOverhaul.MsgBountyExpired.Value, "", "", playerName));
}
SaveBounties();
BountyRpc.BroadcastBountyCircle(playerName, active: false, Vector3.zero, 0, DateTime.UtcNow);
}
}
public void ResetPlayer(string playerName)
{
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
_activeBounties.Remove(playerName);
_kills.RemoveAll((KillRecord x) => x.KillerId == playerName || x.VictimId == playerName);
Logger.LogInfo((object)("[PvPOverhaul SERVER] Full reset: " + playerName));
WriteServerLog("ADMIN_RESET | " + playerName);
SaveBounties();
BountyRpc.BroadcastBountyCircle(playerName, active: false, Vector3.zero, 0, DateTime.UtcNow);
}
public BountyState GetBounty(string playerName)
{
if (!_activeBounties.TryGetValue(playerName, out var value))
{
return null;
}
if (DateTime.UtcNow >= value.EndUtc)
{
ClearBounty(playerName, killed: false);
return null;
}
return value;
}
private void UpdateTimers()
{
DateTime now = DateTime.UtcNow;
foreach (string item in (from x in _activeBounties
where now >= x.Value.EndUtc
select x.Key).ToList())
{
ClearBounty(item, killed: false);
}
}
private void SyncBountyPositions()
{
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
DateTime utcNow = DateTime.UtcNow;
foreach (BountyState item in _activeBounties.Values.ToList())
{
if (!(utcNow >= item.EndUtc))
{
if (!IsPlayerOnline(item.PlayerId))
{
BountyRpc.BroadcastBountyCircle(item.PlayerId, active: false, Vector3.zero, item.Tier, item.EndUtc);
}
else
{
BountyRpc.BroadcastBountyCircle(item.PlayerId, active: true, item.LastKnownPosition, item.Tier, item.EndUtc);
}
}
}
}
private void BroadcastAllBounties()
{
//IL_0079: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
DateTime utcNow = DateTime.UtcNow;
foreach (BountyState item in _activeBounties.Values.ToList())
{
if (!(utcNow >= item.EndUtc))
{
if (!IsPlayerOnline(item.PlayerId))
{
BountyRpc.BroadcastBountyCircle(item.PlayerId, active: false, Vector3.zero, item.Tier, item.EndUtc);
}
else
{
BountyRpc.BroadcastBountyCircle(item.PlayerId, active: true, item.LastKnownPosition, item.Tier, item.EndUtc);
}
}
}
}
private Vector3 GetPlayerPosition(string playerName)
{
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: 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)
foreach (Player allPlayer in Player.GetAllPlayers())
{
if (allPlayer.GetPlayerName() == playerName)
{
return ((Component)allPlayer).transform.position;
}
}
return Vector3.zero;
}
private void CleanupOldKills(DateTime now)
{
_kills.RemoveAll((KillRecord x) => now - x.TimestampUtc > KillWindow);
}
public void SetLastHit(long victimId, string victimName, string attackerName, float damage)
{
LastHit value = new LastHit
{
VictimName = victimName,
AttackerName = attackerName,
Damage = damage,
Time = Time.time
};
_lastHits[victimId] = value;
if (!string.IsNullOrWhiteSpace(victimName))
{
_lastHitsByName[victimName] = value;
}
Logger.LogInfo((object)$"[PvPOverhaul BOUNTY] LastHit set: victim={victimName} id={victimId} attacker={attackerName} dmg={damage}");
}
public LastHit ResolveLastHit(long victimId)
{
if (!_lastHits.TryGetValue(victimId, out var value))
{
return null;
}
if (Time.time - value.Time > 10f)
{
return null;
}
return value;
}
private LastHit ResolveLastHit(Player victim)
{
if ((Object)(object)victim == (Object)null)
{
return null;
}
long playerID = victim.GetPlayerID();
LastHit value = ResolveLastHit(playerID);
if (value != null)
{
return value;
}
string playerName = victim.GetPlayerName();
if (string.IsNullOrWhiteSpace(playerName))
{
return null;
}
if (!_lastHitsByName.TryGetValue(playerName, out value))
{
return null;
}
if (Time.time - value.Time > 10f)
{
return null;
}
return value;
}
public void TryReportDeath(Player victim)
{
if ((Object)(object)victim == (Object)null)
{
return;
}
if (victim.m_godMode)
{
Logger.LogInfo((object)("[PvPOverhaul BOUNTY] Ignore death: godmode victim=" + victim.GetPlayerName()));
return;
}
if (((Character)victim).GetHealth() > 0f && !((Character)victim).IsDead())
{
Logger.LogInfo((object)$"[PvPOverhaul BOUNTY] Ignore death report: victim not dead yet {victim.GetPlayerName()} health={((Character)victim).GetHealth()} dead={((Character)victim).IsDead()}");
return;
}
if (RecentlyIgnoredDeath(victim))
{
Logger.LogInfo((object)("[PvPOverhaul BOUNTY] Ignore death: recently ignored victim=" + victim.GetPlayerName()));
return;
}
long playerID = victim.GetPlayerID();
if (!_reportedDeaths.TryGetValue(playerID, out var value) || !(Time.time - value < 10f))
{
LastHit lastHit = ResolveLastHit(victim);
Logger.LogInfo((object)($"[PvPOverhaul BOUNTY] TryReportDeath victim={victim.GetPlayerName()} id={playerID} " + "lastHit=" + ((lastHit != null) ? lastHit.AttackerName : "NULL") + " " + $"health={((Character)victim).GetHealth()} dead={((Character)victim).IsDead()}"));
if (lastHit != null && !string.IsNullOrWhiteSpace(lastHit.AttackerName) && !(lastHit.AttackerName == victim.GetPlayerName()))
{
_reportedDeaths[playerID] = Time.time;
Logger.LogInfo((object)("[PvPOverhaul CLIENT] Reporting REAL kill to server: " + lastHit.AttackerName + " -> " + victim.GetPlayerName()));
BountyRpc.SendKillToServer(lastHit.AttackerName, victim.GetPlayerName());
}
}
}
public void MarkIgnoredDeath(Player victim)
{
if (!((Object)(object)victim == (Object)null))
{
_ignoredDeaths[victim.GetPlayerID()] = Time.time;
}
}
private bool RecentlyIgnoredDeath(Player victim)
{
if ((Object)(object)victim == (Object)null)
{
return false;
}
if (!_ignoredDeaths.TryGetValue(victim.GetPlayerID(), out var value))
{
return false;
}
return Time.time - value < 1f;
}
private void SaveBounties()
{
Directory.CreateDirectory(SaveDir);
List list = new List();
foreach (BountyState value in _activeBounties.Values)
{
list.Add(string.Join("|", value.PlayerId, value.StartUtc.Ticks, value.EndUtc.Ticks, value.Tier, value.LastKnownPosition.x.ToString(CultureInfo.InvariantCulture), value.LastKnownPosition.y.ToString(CultureInfo.InvariantCulture), value.LastKnownPosition.z.ToString(CultureInfo.InvariantCulture)));
}
File.WriteAllLines(SaveFile, list);
}
private void LoadBounties()
{
//IL_0167: Unknown result type (might be due to invalid IL or missing references)
//IL_016c: Unknown result type (might be due to invalid IL or missing references)
//IL_01c9: Unknown result type (might be due to invalid IL or missing references)
if (!File.Exists(SaveFile))
{
return;
}
DateTime utcNow = DateTime.UtcNow;
string[] array = File.ReadAllLines(SaveFile);
foreach (string text in array)
{
string[] array2 = text.Split(new char[1] { '|' });
if (array2.Length < 7)
{
continue;
}
string text2 = array2[0];
if (!long.TryParse(array2[1], out var result) || !long.TryParse(array2[2], out var result2) || !int.TryParse(array2[3], out var result3) || !float.TryParse(array2[4], NumberStyles.Float, CultureInfo.InvariantCulture, out var result4) || !float.TryParse(array2[5], NumberStyles.Float, CultureInfo.InvariantCulture, out var result5) || !float.TryParse(array2[6], NumberStyles.Float, CultureInfo.InvariantCulture, out var result6))
{
continue;
}
DateTime dateTime = new DateTime(result2, DateTimeKind.Utc);
if (!(utcNow >= dateTime))
{
BountyState bountyState = new BountyState
{
PlayerId = text2,
StartUtc = new DateTime(result, DateTimeKind.Utc),
EndUtc = dateTime,
Tier = result3,
LastKnownPosition = new Vector3(result4, result5, result6)
};
_activeBounties[text2] = bountyState;
Logger.LogInfo((object)("[PvPOverhaul SERVER] Loaded persistent bounty: " + text2));
WriteServerLog($"BOUNTY_LOADED | {text2} | tier={result3} | end={dateTime:o}");
if (IsPlayerOnline(text2))
{
BountyRpc.BroadcastBountyCircle(text2, active: true, bountyState.LastKnownPosition, result3, dateTime);
}
}
}
SaveBounties();
}
public void ScheduleDeathCheck(Player victim)
{
if (!((Object)(object)victim == (Object)null) && !((Object)(object)PvPOverhaul.Instance == (Object)null))
{
((MonoBehaviour)PvPOverhaul.Instance).StartCoroutine(DelayedDeathCheck(victim));
}
}
private IEnumerator DelayedDeathCheck(Player victim)
{
if ((Object)(object)victim == (Object)null)
{
yield break;
}
string victimName = victim.GetPlayerName();
long victimId = victim.GetPlayerID();
for (int i = 0; i < 30; i++)
{
yield return (object)new WaitForSeconds(0.1f);
if ((Object)(object)victim == (Object)null)
{
break;
}
LastHit lastHit = ResolveLastHit(victimId);
if (i == 0 || ((Character)victim).IsDead() || ((Character)victim).GetHealth() <= 0f)
{
Logger.LogInfo((object)($"[PvPOverhaul BOUNTY] DelayedDeathCheck {i} victim={victimName} " + $"health={((Character)victim).GetHealth()} dead={((Character)victim).IsDead()} " + "lastHit=" + ((lastHit != null) ? lastHit.AttackerName : "NULL")));
}
if (((Character)victim).GetHealth() <= 0f || ((Character)victim).IsDead())
{
TryReportDeath(victim);
break;
}
}
}
}
internal static class BountyRpc
{
private const string RpcRegisterKill = "PvpModes_RegisterKill";
private const string RpcBroadcastMessage = "PvpModes_BroadcastMessage";
private const string RpcBountyCircle = "PvpModes_BountyCircle";
private const string RpcBountyPosition = "PvpModes_BountyPosition";
private const string RpcBountyStatusRequest = "PvpModes_BountyStatusRequest";
private const string RpcBountyStatusResponse = "PvpModes_BountyStatusResponse";
private const string RpcBountyResetRequest = "PvpModes_BountyResetRequest";
private const string RpcBountyResetResponse = "PvpModes_BountyResetResponse";
private const string RpcBountyReward = "PvpModes_BountyReward";
private static bool _registered;
private static ZRoutedRpc _registeredInstance;
private static string _pendingRewardItem;
private static int _pendingRewardAmount;
public static void Update()
{
EnsureRegistered();
}
public static bool EnsureRegistered()
{
if (ZRoutedRpc.instance == null)
{
_registered = false;
_registeredInstance = null;
return false;
}
if (_registered && _registeredInstance == ZRoutedRpc.instance)
{
return true;
}
_registered = false;
_registeredInstance = ZRoutedRpc.instance;
ZRoutedRpc.instance.Register("PvpModes_RegisterKill", (Action)RPC_RegisterKill);
ZRoutedRpc.instance.Register("PvpModes_BroadcastMessage", (Action)RPC_BroadcastMessage);
ZRoutedRpc.instance.Register("PvpModes_BountyCircle", (Action)RPC_BountyCircle);
ZRoutedRpc.instance.Register("PvpModes_BountyPosition", (Action)RPC_BountyPosition);
ZRoutedRpc.instance.Register("PvpModes_BountyStatusRequest", (Action)RPC_BountyStatusRequest);
ZRoutedRpc.instance.Register("PvpModes_BountyStatusResponse", (Action)RPC_BountyStatusResponse);
ZRoutedRpc.instance.Register("PvpModes_BountyResetRequest", (Action)RPC_BountyResetRequest);
ZRoutedRpc.instance.Register("PvpModes_BountyResetResponse", (Action)RPC_BountyResetResponse);
ZRoutedRpc.instance.Register("PvpModes_BountyReward", (Action)RPC_BountyReward);
_registered = true;
Logger.LogInfo((object)"[PvPOverhaul] RPC registered successfully");
return true;
}
public static void RequestBountyStatus(string playerName)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Expected O, but got Unknown
if (!EnsureRegistered())
{
Console.instance.Print("[PvPOverhaul] RPC not ready");
return;
}
ZPackage val = new ZPackage();
val.Write(playerName);
if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
{
SendBountyStatusResponse(ZRoutedRpc.instance.GetServerPeerID(), playerName);
return;
}
ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "PvpModes_BountyStatusRequest", new object[1] { val });
}
public static void RequestBountyReset(string playerName)
{
//IL_0020: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Expected O, but got Unknown
if (!EnsureRegistered())
{
Console.instance.Print("[PvPOverhaul] RPC not ready");
return;
}
ZPackage val = new ZPackage();
val.Write(playerName);
if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
{
BountySystem.Instance.ResetPlayer(playerName);
Console.instance.Print("Reset bounty for " + playerName);
}
else
{
ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "PvpModes_BountyResetRequest", new object[1] { val });
}
}
private static void RPC_BountyStatusRequest(long sender, ZPackage pkg)
{
if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
{
string text = pkg.ReadString();
ZPackage val = BuildBountyStatusResponse(text);
Logger.LogInfo((object)$"[PvPOverhaul SERVER] Bounty status request: {text} sender={sender}");
ZRoutedRpc.instance.InvokeRoutedRPC(sender, "PvpModes_BountyStatusResponse", new object[1] { val });
}
}
private static void RPC_BountyStatusResponse(long sender, ZPackage pkg)
{
string text = pkg.ReadString();
string text2;
if (!pkg.ReadBool())
{
text2 = "No bounty for " + text;
}
else
{
int num = pkg.ReadInt();
long ticks = pkg.ReadLong();
TimeSpan timeSpan = new DateTime(ticks, DateTimeKind.Utc) - DateTime.UtcNow;
if (timeSpan < TimeSpan.Zero)
{
timeSpan = TimeSpan.Zero;
}
text2 = $"{text} → Tier {num} | Time left: {timeSpan:hh\\:mm\\:ss}";
}
Console.instance.Print(text2);
if ((Object)(object)MessageHud.instance != (Object)null)
{
MessageHud.instance.ShowMessage((MessageType)2, text2, 0, (Sprite)null, false);
}
}
private static void RPC_BountyResetRequest(long sender, ZPackage pkg)
{
//IL_0037: Unknown result type (might be due to invalid IL or missing references)
//IL_003d: Expected O, but got Unknown
if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
{
string text = pkg.ReadString();
BountySystem.Instance.ResetPlayer(text);
ZPackage val = new ZPackage();
val.Write(text);
ZRoutedRpc.instance.InvokeRoutedRPC(sender, "PvpModes_BountyResetResponse", new object[1] { val });
}
}
private static void RPC_BountyResetResponse(long sender, ZPackage pkg)
{
string text = pkg.ReadString();
Console.instance.Print("Reset bounty for " + text);
}
private static ZPackage BuildBountyStatusResponse(string playerName)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Expected O, but got Unknown
ZPackage val = new ZPackage();
BountySystem.BountyState bounty = BountySystem.Instance.GetBounty(playerName);
val.Write(playerName);
val.Write(bounty != null);
if (bounty != null)
{
val.Write(bounty.Tier);
val.Write(bounty.EndUtc.Ticks);
}
return val;
}
private static void SendBountyStatusResponse(long targetPeerId, string playerName)
{
ZPackage val = BuildBountyStatusResponse(playerName);
ZRoutedRpc.instance.InvokeRoutedRPC(targetPeerId, "PvpModes_BountyStatusResponse", new object[1] { val });
}
public static void SendKillToServer(string killerName, string victimName)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Expected O, but got Unknown
if (EnsureRegistered())
{
ZPackage val = new ZPackage();
val.Write(killerName);
val.Write(victimName);
if ((Object)(object)ZNet.instance != (Object)null && ZNet.instance.IsServer())
{
BountySystem.Instance.RegisterKill(killerName, victimName);
return;
}
ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "PvpModes_RegisterKill", new object[1] { val });
}
}
public static void SendBountyPositionToServer(string playerName, Vector3 position)
{
//IL_002f: Unknown result type (might be due to invalid IL or missing references)
//IL_0035: Expected O, but got Unknown
//IL_003e: Unknown result type (might be due to invalid IL or missing references)
if (EnsureRegistered() && (!((Object)(object)ZNet.instance != (Object)null) || !ZNet.instance.IsServer()))
{
ZPackage val = new ZPackage();
val.Write(playerName);
val.Write(position);
ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.instance.GetServerPeerID(), "PvpModes_BountyPosition", new object[1] { val });
}
}
public static void BroadcastCenterMessage(string message)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Expected O, but got Unknown
if (EnsureRegistered())
{
ZPackage val = new ZPackage();
val.Write(message);
Logger.LogInfo((object)("[PvPOverhaul SERVER] Broadcast message: " + message));
ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "PvpModes_BroadcastMessage", new object[1] { val });
}
}
public static void BroadcastBountyCircle(string playerName, bool active, Vector3 position, int tier, DateTime endUtc)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Expected O, but got Unknown
//IL_0026: Unknown result type (might be due to invalid IL or missing references)
if (EnsureRegistered())
{
ZPackage val = new ZPackage();
val.Write(playerName);
val.Write(active);
val.Write(position);
val.Write(tier);
val.Write(endUtc.Ticks);
ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "PvpModes_BountyCircle", new object[1] { val });
}
}
private static void RPC_RegisterKill(long sender, ZPackage pkg)
{
if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
{
string text = pkg.ReadString();
string text2 = pkg.ReadString();
Logger.LogInfo((object)("[PvPOverhaul SERVER] Kill RPC received: " + text + " -> " + text2));
BountySystem.Instance.RegisterKill(text, text2);
}
}
private static void RPC_BountyPosition(long sender, ZPackage pkg)
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)ZNet.instance == (Object)null) && ZNet.instance.IsServer())
{
string playerName = pkg.ReadString();
Vector3 position = pkg.ReadVector3();
BountySystem.Instance.UpdateBountyPositionFromClient(playerName, position);
}
}
private static void RPC_BroadcastMessage(long sender, ZPackage pkg)
{
string text = pkg.ReadString();
if ((Object)(object)MessageHud.instance != (Object)null)
{
MessageHud.instance.ShowMessage((MessageType)2, text, 0, (Sprite)null, false);
}
}
private static void RPC_BountyCircle(long sender, ZPackage pkg)
{
//IL_0010: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Unknown result type (might be due to invalid IL or missing references)
string playerName = pkg.ReadString();
bool active = pkg.ReadBool();
Vector3 position = pkg.ReadVector3();
int tier = pkg.ReadInt();
long ticks = pkg.ReadLong();
DateTime endUtc = new DateTime(ticks, DateTimeKind.Utc);
BountyStatusEffectController.SetLocalBountyStatus(playerName, active, endUtc);
BountyMapCircle.Set(playerName, active, position, tier, endUtc);
}
public static void SendRewardToPlayer(string playerName, string itemPrefabName, int amount)
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Expected O, but got Unknown
if (EnsureRegistered())
{
ZPackage val = new ZPackage();
val.Write(playerName);
val.Write(itemPrefabName);
val.Write(amount);
Logger.LogInfo((object)$"[PvPOverhaul SERVER] Sending reward RPC to {playerName}: {amount}x {itemPrefabName}");
ZRoutedRpc.instance.InvokeRoutedRPC(ZRoutedRpc.Everybody, "PvpModes_BountyReward", new object[1] { val });
}
}
private static void RPC_BountyReward(long sender, ZPackage pkg)
{
string text = pkg.ReadString();
string text2 = pkg.ReadString();
int num = pkg.ReadInt();
Logger.LogInfo((object)$"[PvPOverhaul CLIENT] Reward RPC received for {text}: {num}x {text2}");
if (!((Object)(object)Player.m_localPlayer == (Object)null) && !(Player.m_localPlayer.GetPlayerName() != text))
{
Logger.LogInfo((object)("[PvPOverhaul CLIENT] Reward accepted by local player " + Player.m_localPlayer.GetPlayerName()));
_pendingRewardItem = text2;
_pendingRewardAmount += num;
TryGivePendingReward();
}
}
public static void UpdateReward()
{
TryGivePendingReward();
}
private static void TryGivePendingReward()
{
if (string.IsNullOrWhiteSpace(_pendingRewardItem) || (Object)(object)Player.m_localPlayer == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
{
return;
}
GameObject itemPrefab = ObjectDB.instance.GetItemPrefab(_pendingRewardItem);
if ((Object)(object)itemPrefab == (Object)null)
{
Logger.LogWarning((object)("[PvPOverhaul] Reward prefab not found yet: " + _pendingRewardItem));
return;
}
((Humanoid)Player.m_localPlayer).GetInventory().AddItem(itemPrefab, _pendingRewardAmount);
if ((Object)(object)MessageHud.instance != (Object)null)
{
MessageHud.instance.ShowMessage((MessageType)2, $"Récompense de prime : {_pendingRewardAmount}x {_pendingRewardItem}", 0, (Sprite)null, false);
}
_pendingRewardItem = null;
_pendingRewardAmount = 0;
}
}
internal static class BountyMapCircle
{
private class CircleState
{
public PinData Pin;
public bool Active;
public int Tier;
public DateTime EndUtc;
public float NextPositionSend;
}
private static readonly Dictionary _circles = new Dictionary();
private const float CircleWorldSize = 300f;
public static void Update()
{
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
//IL_0130: Unknown result type (might be due to invalid IL or missing references)
//IL_0101: Unknown result type (might be due to invalid IL or missing references)
//IL_0102: Unknown result type (might be due to invalid IL or missing references)
DateTime utcNow = DateTime.UtcNow;
foreach (KeyValuePair item in _circles.ToList())
{
if (item.Value.EndUtc <= utcNow)
{
Remove(item.Key);
}
}
if ((Object)(object)Player.m_localPlayer == (Object)null)
{
return;
}
string playerName = Player.m_localPlayer.GetPlayerName();
if (_circles.TryGetValue(playerName, out var value) && value.Active && !(Time.time < value.NextPositionSend))
{
value.NextPositionSend = Time.time + 10f;
Vector3 position = ((Component)Player.m_localPlayer).transform.position;
if (value.Pin != null)
{
value.Pin.m_pos = position;
value.Pin.m_name = "RECHERCHÉ: " + playerName;
SetPinWorldSize(value.Pin, 300f);
}
BountyRpc.SendBountyPositionToServer(playerName, position);
}
}
public static void Set(string playerName, bool active, Vector3 position, int tier, DateTime endUtc)
{
//IL_0184: Unknown result type (might be due to invalid IL or missing references)
//IL_018e: Unknown result type (might be due to invalid IL or missing references)
//IL_0194: Unknown result type (might be due to invalid IL or missing references)
//IL_014f: Unknown result type (might be due to invalid IL or missing references)
//IL_0159: Unknown result type (might be due to invalid IL or missing references)
//IL_015f: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)Minimap.instance == (Object)null)
{
return;
}
bool flag = (Object)(object)Player.m_localPlayer != (Object)null && Player.m_localPlayer.GetPlayerName() == playerName;
if (!active)
{
Remove(playerName);
return;
}
if (flag)
{
if (_circles.TryGetValue(playerName, out var value))
{
if (value.Pin != null)
{
Minimap.instance.RemovePin(value.Pin);
}
value.Pin = null;
value.Active = true;
value.Tier = tier;
value.EndUtc = endUtc;
}
else
{
_circles[playerName] = new CircleState
{
Pin = null,
Active = true,
Tier = tier,
EndUtc = endUtc,
NextPositionSend = 0f
};
}
return;
}
string text = "RECHERCHÉ: " + playerName;
if (_circles.TryGetValue(playerName, out var value2))
{
value2.Active = true;
value2.Tier = tier;
value2.EndUtc = endUtc;
if (value2.Pin != null)
{
Minimap.instance.RemovePin(value2.Pin);
}
PinData pin = Minimap.instance.AddPin(position, (PinType)13, text, false, false, 0L, default(PlatformUserID));
SetPinWorldSize(pin, 300f);
value2.Pin = pin;
}
else
{
PinData pin2 = Minimap.instance.AddPin(position, (PinType)13, text, false, false, 0L, default(PlatformUserID));
SetPinWorldSize(pin2, 300f);
_circles[playerName] = new CircleState
{
Pin = pin2,
Active = true,
Tier = tier,
EndUtc = endUtc,
NextPositionSend = 0f
};
}
}
private static void Remove(string playerName)
{
if (_circles.TryGetValue(playerName, out var value))
{
if ((Object)(object)Minimap.instance != (Object)null && value.Pin != null)
{
Minimap.instance.RemovePin(value.Pin);
}
_circles.Remove(playerName);
}
}
private static void SetPinWorldSize(PinData pin, float size)
{
FieldInfo field = typeof(PinData).GetField("m_worldSize", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
if (field != null)
{
field.SetValue(pin, size);
}
}
}
internal static class BountyStatusEffectController
{
private const string EffectName = "SE_PvpModes_Bounty";
private static readonly int EffectHash = StringExtensionMethods.GetStableHashCode("SE_PvpModes_Bounty");
private static DateTime _currentEndUtc;
private static SE_Stats _effectPrefab;
private static bool _registered;
private static bool _pendingActive;
private static DateTime _pendingEndUtc;
private static string _pendingPlayerName;
public static void UpdateRegistration()
{
if (_registered || (Object)(object)ObjectDB.instance == (Object)null)
{
return;
}
StatusEffect statusEffect = ObjectDB.instance.GetStatusEffect(EffectHash);
if ((Object)(object)statusEffect != (Object)null)
{
_effectPrefab = (SE_Stats)(object)((statusEffect is SE_Stats) ? statusEffect : null);
_registered = true;
return;
}
SE_Stats val = (_effectPrefab = ScriptableObject.CreateInstance());
((Object)val).name = "SE_PvpModes_Bounty";
((StatusEffect)val).m_name = "Recherché";
((StatusEffect)val).m_tooltip = "Votre position approximative est révélée aux autres joueurs.";
((StatusEffect)val).m_ttl = 60f;
Sprite sprite = GetSprite("SoftDeath");
if ((Object)(object)sprite != (Object)null)
{
((StatusEffect)val).m_icon = sprite;
}
else
{
Logger.LogWarning((object)"[PvPOverhaul] SoftDeath icon not found");
}
ObjectDB.instance.m_StatusEffects.Add((StatusEffect)(object)val);
_registered = true;
Logger.LogInfo((object)"[PvPOverhaul] Bounty StatusEffect registered");
if (!string.IsNullOrEmpty(_pendingPlayerName))
{
string pendingPlayerName = _pendingPlayerName;
bool pendingActive = _pendingActive;
DateTime pendingEndUtc = _pendingEndUtc;
_pendingPlayerName = null;
SetLocalBountyStatus(pendingPlayerName, pendingActive, pendingEndUtc);
}
}
public static void SetLocalBountyStatus(string playerName, bool active, DateTime endUtc)
{
if (!_registered || (Object)(object)Player.m_localPlayer == (Object)null)
{
_pendingPlayerName = playerName;
_pendingActive = active;
_pendingEndUtc = endUtc;
}
UpdateRegistration();
if (!_registered || (Object)(object)Player.m_localPlayer == (Object)null || Player.m_localPlayer.GetPlayerName() != playerName)
{
return;
}
SEMan sEMan = ((Character)Player.m_localPlayer).GetSEMan();
if (!active)
{
sEMan.RemoveStatusEffect(EffectHash, false);
return;
}
float num = (float)(endUtc - DateTime.UtcNow).TotalSeconds;
if (num <= 0f)
{
sEMan.RemoveStatusEffect(EffectHash, false);
return;
}
bool flag = sEMan.HaveStatusEffect(EffectHash);
if (!(_currentEndUtc == endUtc && flag))
{
_currentEndUtc = endUtc;
if (flag)
{
sEMan.RemoveStatusEffect(EffectHash, false);
}
if ((Object)(object)_effectPrefab != (Object)null)
{
((StatusEffect)_effectPrefab).m_ttl = num;
}
StatusEffect val = (((Object)(object)_effectPrefab != (Object)null) ? sEMan.AddStatusEffect((StatusEffect)(object)_effectPrefab, true, 0, 0f) : sEMan.AddStatusEffect(EffectHash, true, 0, 0f));
if ((Object)(object)val != (Object)null)
{
val.m_ttl = num;
val.m_time = 0f;
}
}
}
private static Sprite GetSprite(string name)
{
Sprite[] array = Resources.FindObjectsOfTypeAll();
foreach (Sprite val in array)
{
if (((Object)val).name == name)
{
return val;
}
}
return null;
}
}
internal class BountyStatusCommand : ConsoleCommand
{
public override string Name => "pvp_bounty";
public override string Help => "Affiche la bounty d'un joueur. Usage: pvp_bounty nom";
public override void Run(string[] args)
{
if (args.Length < 1)
{
Console.instance.Print("Usage: pvp_bounty nom");
}
else if (!SynchronizationManager.Instance.PlayerIsAdmin)
{
Console.instance.Print("Only server admins can use this command.");
}
else
{
BountyRpc.RequestBountyStatus(args[0]);
}
}
}
internal class BountyResetCommand : ConsoleCommand
{
public override string Name => "pvp_resetbounty";
public override string Help => "Reset la bounty et les kills d'un joueur. Usage: pvp_resetbounty nom";
public override void Run(string[] args)
{
if (args.Length < 1)
{
Console.instance.Print("Usage: pvp_resetbounty nom");
}
else if (!SynchronizationManager.Instance.PlayerIsAdmin)
{
Console.instance.Print("Only server admins can use this command.");
}
else
{
BountyRpc.RequestBountyReset(args[0]);
}
}
}
[HarmonyPatch(typeof(Player), "OnDeath")]
internal static class PlayerDeathBountyPatch
{
private static void Postfix(Player __instance)
{
if (!((Object)(object)__instance == (Object)null))
{
Logger.LogInfo((object)("[PvPOverhaul BOUNTY] Player.OnDeath detected: " + __instance.GetPlayerName()));
BountySystem.Instance.TryReportDeath(__instance);
}
}
}
[HarmonyPatch(typeof(Character), "OnDeath")]
internal static class CharacterDeathBountyPatch
{
private static void Postfix(Character __instance)
{
Player val = (Player)(object)((__instance is Player) ? __instance : null);
if (val != null)
{
Logger.LogInfo((object)("[PvPOverhaul BOUNTY] Character.OnDeath detected: " + val.GetPlayerName()));
BountySystem.Instance.TryReportDeath(val);
}
}
}
internal static class PvpCombatHelper
{
private static readonly int CombatHash = StringExtensionMethods.GetStableHashCode("SE_Combat");
public static bool IsInPvpCombat(Player player)
{
if ((Object)(object)player == (Object)null)
{
return false;
}
SEMan sEMan = ((Character)player).GetSEMan();
if (sEMan != null && sEMan.HaveStatusEffect(CombatHash))
{
return true;
}
if ((Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid())
{
ZDO zDO = ((Character)player).m_nview.GetZDO();
if (zDO != null && zDO.GetBool("VPT_PlayerInCombat", false))
{
return true;
}
}
return false;
}
public static void ApplyPvpCombatStatus(Player player, Player templateSource = null)
{
if ((Object)(object)player == (Object)null)
{
return;
}
if ((Object)(object)((Character)player).m_nview != (Object)null && ((Character)player).m_nview.IsValid())
{
ZDO zDO = ((Character)player).m_nview.GetZDO();
if (zDO != null)
{
zDO.Set("VPT_PlayerInCombat", true);
}
}
SEMan sEMan = ((Character)player).GetSEMan();
if (sEMan == null)
{
return;
}
if (sEMan.HaveStatusEffect(CombatHash))
{
sEMan.AddStatusEffect(CombatHash, true, 0, 0f);
return;
}
ObjectDB instance = ObjectDB.instance;
StatusEffect val = ((instance != null) ? instance.GetStatusEffect(CombatHash) : null);
if ((Object)(object)val == (Object)null && (Object)(object)templateSource != (Object)null)
{
SEMan sEMan2 = ((Character)templateSource).GetSEMan();
if (sEMan2 != null)
{
foreach (StatusEffect statusEffect in sEMan2.GetStatusEffects())
{
if ((Object)(object)statusEffect != (Object)null && statusEffect.NameHash() == CombatHash)
{
val = statusEffect;
break;
}
}
}
}
if ((Object)(object)val != (Object)null)
{
sEMan.AddStatusEffect(val, true, 0, 0f);
}
}
}
internal static class PlayerCombatHandler
{
public static void EnterCombat(Character character)
{
if (!((Object)(object)character == (Object)null) && !((Object)(object)character.m_nview == (Object)null) && character.m_nview.IsValid())
{
ZDO zDO = character.m_nview.GetZDO();
if (zDO != null)
{
zDO.Set("VPT_PlayerInCombat", true);
}
character.m_nview.InvokeRPC("EnterCombatRpc", Array.Empty