using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet;
using FishNet.Managing.Timing;
using FishNet.Object;
using FishNet.Object.Delegating;
using FishNet.Object.Synchronizing;
using FishNet.Object.Synchronizing.Internal;
using FishNet.Serializing;
using FishNet.Transporting;
using HarmonyLib;
using Newtonsoft.Json;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("BetterCorpses")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("更好的尸体:玩家尸体可卖钱/赌博/抽奖,复活强制等待 + 拍活吃尸体")]
[assembly: AssemblyFileVersion("1.2.4.0")]
[assembly: AssemblyInformationalVersion("1.2.4")]
[assembly: AssemblyProduct("BetterCorpses")]
[assembly: AssemblyTitle("BetterCorpses")]
[assembly: AssemblyVersion("1.2.4.0")]
namespace BetterCorpses;
public static class Config
{
public static ConfigEntry<bool> Master;
public static ConfigEntry<bool> RespawnTime;
public static ConfigEntry<bool> CorpseDrop;
public static ConfigEntry<bool> Sell;
public static ConfigEntry<bool> Gamble;
public static ConfigEntry<bool> Lottery;
public static ConfigEntry<bool> Cannibal;
public static ConfigEntry<int> CannibalChance;
public static ConfigEntry<float> ForcedWaitSeconds;
public static ConfigEntry<float> GiveUpHoldSeconds;
public static ConfigEntry<int> PriceMin;
public static ConfigEntry<int> PriceMax;
public static bool RespawnOn
{
get
{
if (Master.Value)
{
return RespawnTime.Value;
}
return false;
}
}
public static bool EconomyOn
{
get
{
if (Master.Value)
{
return CorpseDrop.Value;
}
return false;
}
}
public static bool SellOn
{
get
{
if (EconomyOn)
{
return Sell.Value;
}
return false;
}
}
public static bool GambleOn
{
get
{
if (EconomyOn)
{
return Gamble.Value;
}
return false;
}
}
public static bool LotteryOn
{
get
{
if (EconomyOn)
{
return Lottery.Value;
}
return false;
}
}
public static bool CannibalOn
{
get
{
if (Master.Value)
{
return Cannibal.Value;
}
return false;
}
}
public static void Init(BaseUnityPlugin plugin)
{
//IL_012c: Unknown result type (might be due to invalid IL or missing references)
//IL_0136: Expected O, but got Unknown
//IL_016e: Unknown result type (might be due to invalid IL or missing references)
//IL_0178: Expected O, but got Unknown
//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
//IL_01ba: Expected O, but got Unknown
//IL_01ef: Unknown result type (might be due to invalid IL or missing references)
//IL_01f9: Expected O, but got Unknown
//IL_022e: Unknown result type (might be due to invalid IL or missing references)
//IL_0238: Expected O, but got Unknown
Master = plugin.Config.Bind<bool>("General", "Master", true, Loc.Get("cfg.Master"));
RespawnTime = plugin.Config.Bind<bool>("General", "RespawnTime", true, Loc.Get("cfg.RespawnTime"));
CorpseDrop = plugin.Config.Bind<bool>("General", "CorpseDrop", true, Loc.Get("cfg.CorpseDrop"));
Sell = plugin.Config.Bind<bool>("General", "Sell", true, Loc.Get("cfg.Sell"));
Gamble = plugin.Config.Bind<bool>("General", "Gamble", true, Loc.Get("cfg.Gamble"));
Lottery = plugin.Config.Bind<bool>("General", "Lottery", true, Loc.Get("cfg.Lottery"));
Cannibal = plugin.Config.Bind<bool>("General", "Cannibal", true, Loc.Get("cfg.Cannibal"));
CannibalChance = plugin.Config.Bind<int>("General", "CannibalChance", 50, new ConfigDescription(Loc.Get("cfg.CannibalChance"), (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
ForcedWaitSeconds = plugin.Config.Bind<float>("Respawn", "ForcedWaitSeconds", 5f, new ConfigDescription(Loc.Get("cfg.ForcedWaitSeconds"), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 600f), Array.Empty<object>()));
GiveUpHoldSeconds = plugin.Config.Bind<float>("Respawn", "GiveUpHoldSeconds", 1f, new ConfigDescription(Loc.Get("cfg.GiveUpHoldSeconds"), (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 60f), Array.Empty<object>()));
PriceMin = plugin.Config.Bind<int>("Economy", "PriceMin", -50, new ConfigDescription(Loc.Get("cfg.PriceMin"), (AcceptableValueBase)(object)new AcceptableValueRange<int>(-10000, 10000), Array.Empty<object>()));
PriceMax = plugin.Config.Bind<int>("Economy", "PriceMax", 50, new ConfigDescription(Loc.Get("cfg.PriceMax"), (AcceptableValueBase)(object)new AcceptableValueRange<int>(-10000, 10000), Array.Empty<object>()));
}
}
internal static class AutoRespawn
{
private class Pending
{
public Player Player;
public float FireTime;
public Vector3 Pos;
public Quaternion Rot;
}
private const float DelaySeconds = 1f;
private static readonly List<Pending> _pending = new List<Pending>();
public static void Schedule(Player player, Vector3 pos, Quaternion rot)
{
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
try
{
if ((Object)(object)player == (Object)null || ((NetworkBehaviour)player).IsDeinitializing || (Object)(object)player.Vitals == (Object)null)
{
return;
}
float num = Time.time + 1f;
if (Config.RespawnOn)
{
float serverDeathTime = DeathTimer.GetServerDeathTime(player);
if (serverDeathTime >= 0f)
{
num = Mathf.Max(num, serverDeathTime + Config.ForcedWaitSeconds.Value);
}
}
_pending.Add(new Pending
{
Player = player,
FireTime = num,
Pos = pos,
Rot = rot
});
}
catch (Exception ex)
{
Plugin.LogWarn("AutoRespawn.Schedule: " + ex.Message);
}
}
public static void Tick()
{
try
{
if (_pending.Count == 0)
{
return;
}
for (int num = _pending.Count - 1; num >= 0; num--)
{
Pending pending = _pending[num];
if (!(Time.time < pending.FireTime))
{
_pending.RemoveAt(num);
Fire(pending);
}
}
}
catch (Exception ex)
{
Plugin.LogWarn("AutoRespawn.Tick: " + ex.Message);
}
}
private static void Fire(Pending p)
{
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
try
{
Player player = p.Player;
if (!((Object)(object)player == (Object)null) && !((NetworkBehaviour)player).IsDeinitializing && !((Object)(object)player.Vitals == (Object)null) && player.Vitals.Health <= 0 && !((Object)(object)Server.Instance == (Object)null) && ((NetworkBehaviour)Server.Instance).IsServerInitialized && !DeathTimer.ServerInWait(player))
{
Server.Instance.RespawnPlayer(player, p.Pos, p.Rot);
}
}
catch (Exception ex)
{
Plugin.LogWarn("AutoRespawn.Fire: " + ex.Message);
}
}
}
internal static class CorpseCannibal
{
private const int ReviverHealAmount = 12;
public static void TryCannibalize(Player revived, DeadPlayer corpse)
{
try
{
if (!Config.CannibalOn || (Object)(object)revived == (Object)null || (Object)(object)corpse == (Object)null || (Object)(object)revived.Vitals == (Object)null || revived.Vitals.Health <= 0)
{
return;
}
Player val = ((Item)corpse).Holder ?? ((Item)corpse).LastHolder;
if (!((Object)(object)val == (Object)null) && !((Object)(object)val.Vitals == (Object)null) && !((Object)(object)revived == (Object)(object)val) && Random.Range(0, 100) < Config.CannibalChance.Value)
{
int num = HalfOfVanillaRevive(revived);
revived.Vitals._syncedHealth.Value = Mathf.Clamp(num, 0, 100);
val.Vitals.Heal(12);
CannibalFx component = ((Component)val).GetComponent<CannibalFx>();
if ((Object)(object)component != (Object)null)
{
component.PlayEatEffects();
}
Plugin.LogInfo("[BetterCorpses] Cannibalism: " + val.SteamName + " ate " + revived.SteamName + "'s corpse (revived at " + num + " HP, reviver healed " + 12 + ")");
}
}
catch (Exception ex)
{
Plugin.LogWarn("CorpseCannibal: " + ex.Message);
}
}
private static int HalfOfVanillaRevive(Player revived)
{
try
{
if (AccessTools.Field(typeof(PlayerVitals), "_healthOnRes").GetValue(revived.Vitals) is int num)
{
return Mathf.FloorToInt((float)num * 0.5f);
}
}
catch (Exception ex)
{
Plugin.LogWarn("HalfOfVanillaRevive: " + ex.Message);
}
return 12;
}
}
internal static class DeathTimer
{
private static float _localDeathTime = -1f;
private static readonly Dictionary<Player, float> _serverDeath = new Dictionary<Player, float>();
public static bool LocalInWait
{
get
{
if (_localDeathTime < 0f)
{
return false;
}
return Time.time - _localDeathTime < Config.ForcedWaitSeconds.Value;
}
}
public static float LocalRemaining
{
get
{
if (_localDeathTime < 0f)
{
return 0f;
}
return Mathf.Max(0f, Config.ForcedWaitSeconds.Value - (Time.time - _localDeathTime));
}
}
internal static float NetworkTime()
{
try
{
if ((Object)(object)InstanceFinder.TimeManager != (Object)null)
{
return (float)InstanceFinder.TimeManager.TicksToTime((TickType)0);
}
}
catch (Exception ex)
{
Plugin.LogWarn("DeathTimer.NetworkTime: " + ex.Message);
}
return Time.time;
}
public static void RecordLocalDeath()
{
_localDeathTime = Time.time;
}
public static void ClearLocal()
{
_localDeathTime = -1f;
}
public static void RecordServerDeath(Player p)
{
if (!((Object)(object)p == (Object)null))
{
_serverDeath[p] = Time.time;
}
}
public static void ClearServer(Player p)
{
if (!((Object)(object)p == (Object)null))
{
_serverDeath.Remove(p);
}
}
public static bool ServerInWait(Player p)
{
if ((Object)(object)p == (Object)null)
{
return false;
}
if (_serverDeath.TryGetValue(p, out var value))
{
return Time.time - value < Config.ForcedWaitSeconds.Value;
}
return false;
}
public static float GetServerDeathTime(Player p)
{
if ((Object)(object)p == (Object)null)
{
return -1f;
}
if (!_serverDeath.TryGetValue(p, out var value))
{
return -1f;
}
return value;
}
internal static float GetCorpseDeathTime(DeadPlayer corpse)
{
try
{
if ((Object)(object)corpse == (Object)null)
{
return -1f;
}
Player player = corpse.Player;
if ((Object)(object)player != (Object)null)
{
CannibalFx component = ((Component)player).GetComponent<CannibalFx>();
if ((Object)(object)component != (Object)null && component.DeathTime >= 0f)
{
return component.DeathTime;
}
}
CorpseEconomy component2 = ((Component)corpse).GetComponent<CorpseEconomy>();
if ((Object)(object)component2 != (Object)null)
{
return component2.DeathTime;
}
}
catch (Exception ex)
{
Plugin.LogWarn("GetCorpseDeathTime: " + ex.Message);
}
return -1f;
}
public static bool CorpseInWait(DeadPlayer corpse)
{
float corpseDeathTime = GetCorpseDeathTime(corpse);
if (corpseDeathTime < 0f)
{
return false;
}
return NetworkTime() - corpseDeathTime < Config.ForcedWaitSeconds.Value;
}
public static float CorpseRemaining(DeadPlayer corpse)
{
float corpseDeathTime = GetCorpseDeathTime(corpse);
if (corpseDeathTime < 0f)
{
return 0f;
}
return Mathf.Max(0f, Config.ForcedWaitSeconds.Value - (NetworkTime() - corpseDeathTime));
}
}
public static class Loc
{
public enum Lang
{
Chinese,
English
}
private static Dictionary<string, string> _zh;
private static Dictionary<string, string> _en;
private static bool _loaded;
public static Lang Current { get; private set; }
public static void Init()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_0009: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Invalid comparison between Unknown and I4
SystemLanguage systemLanguage = Application.systemLanguage;
if (systemLanguage - 40 <= 1)
{
Current = Lang.Chinese;
}
else
{
Current = Lang.English;
}
}
private static string FileName(Lang lang)
{
if (lang != Lang.Chinese)
{
return "en-US.json";
}
return "zh-CN.json";
}
private static void EnsureLoaded()
{
if (_loaded)
{
return;
}
_loaded = true;
try
{
_zh = LoadTable(Lang.Chinese);
}
catch (Exception ex)
{
if (Plugin.Log != null)
{
Plugin.Log.LogError((object)("zh-CN 加载失败: " + ex.Message));
}
}
try
{
_en = LoadTable(Lang.English);
}
catch (Exception ex2)
{
if (Plugin.Log != null)
{
Plugin.Log.LogError((object)("en-US 加载失败: " + ex2.Message));
}
}
if (_zh == null)
{
_zh = new Dictionary<string, string>();
}
if (_en == null)
{
_en = new Dictionary<string, string>();
}
}
private static Dictionary<string, string> LoadTable(Lang lang)
{
string text = FileName(lang);
string embeddedText = GetEmbeddedText(text);
if (embeddedText == null)
{
if (Plugin.Log != null)
{
Plugin.Log.LogWarning((object)("未找到内嵌本地化资源: " + text));
}
return null;
}
try
{
return JsonConvert.DeserializeObject<Dictionary<string, string>>(embeddedText);
}
catch (Exception ex)
{
if (Plugin.Log != null)
{
Plugin.Log.LogError((object)("JSON 解析失败: " + text + " -> " + ex.Message));
}
return null;
}
}
private static string GetEmbeddedText(string fileName)
{
Assembly executingAssembly = Assembly.GetExecutingAssembly();
string[] manifestResourceNames = executingAssembly.GetManifestResourceNames();
foreach (string text in manifestResourceNames)
{
if (!text.EndsWith("." + fileName, StringComparison.OrdinalIgnoreCase))
{
continue;
}
using Stream stream = executingAssembly.GetManifestResourceStream(text);
if (stream == null)
{
return null;
}
using StreamReader streamReader = new StreamReader(stream);
return streamReader.ReadToEnd();
}
return null;
}
public static string Get(string key)
{
EnsureLoaded();
Dictionary<string, string> dictionary = ((Current == Lang.Chinese) ? _zh : _en);
if (dictionary != null && dictionary.TryGetValue(key, out var value) && value != null)
{
return value;
}
Dictionary<string, string> dictionary2 = ((Current == Lang.Chinese) ? _en : _zh);
if (dictionary2 != null && dictionary2.TryGetValue(key, out var value2) && value2 != null)
{
return value2;
}
return "[" + key + "]";
}
public static string Get(string key, params object[] args)
{
try
{
return string.Format(Get(key), args);
}
catch (Exception)
{
return Get(key);
}
}
}
public class CannibalFx : NetworkBehaviour
{
public readonly SyncVar<float> _deathTime = new SyncVar<float>(-1f, default(SyncTypeSettings));
private float _pendingDeathTime = -1f;
private bool _hasPendingDeathTime;
private bool _nwEarlyExcuted;
private bool _nwLateExcuted;
public float DeathTime => _deathTime.Value;
public virtual void Awake()
{
((NetworkBehaviour)this).NetworkInitialize___Early();
((NetworkBehaviour)this).NetworkInitialize___Late();
}
public override void OnStartServer()
{
((NetworkBehaviour)this).OnStartServer();
Plugin.LogInfo("[BetterCorpses] CannibalFx OnStartServer, IsServerInitialized=" + ((NetworkBehaviour)this).IsServerInitialized);
}
private void Update()
{
try
{
if (((NetworkBehaviour)this).IsServerInitialized && _hasPendingDeathTime)
{
_deathTime.Value = _pendingDeathTime;
_hasPendingDeathTime = false;
Plugin.LogInfo("[BetterCorpses] CannibalFx._deathTime flushed on server: " + _pendingDeathTime.ToString("0.0"));
}
}
catch (Exception ex)
{
Plugin.LogWarn("CannibalFx.Update: " + ex.Message);
}
}
public void SetDeathTime(float t)
{
try
{
if (((NetworkBehaviour)this).IsServerInitialized)
{
_deathTime.Value = t;
Plugin.LogInfo("[BetterCorpses] CannibalFx.SetDeathTime direct: " + t.ToString("0.0") + ", init=" + ((NetworkBehaviour)this).IsServerInitialized);
}
else
{
_pendingDeathTime = t;
_hasPendingDeathTime = true;
Plugin.LogInfo("[BetterCorpses] CannibalFx.SetDeathTime stashed: " + t.ToString("0.0") + ", init=" + ((NetworkBehaviour)this).IsServerInitialized);
}
}
catch (Exception ex)
{
Plugin.LogWarn("CannibalFx.SetDeathTime: " + ex.Message);
}
}
public void PlayEatEffects()
{
//IL_000b: 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)
try
{
if (((NetworkBehaviour)this).IsServerInitialized)
{
Channel val = (Channel)0;
PooledWriter val2 = WriterPool.Retrieve();
((NetworkBehaviour)this).SendObserversRpc(0u, val2, val, (DataOrderType)0, false, false, false);
val2.Store();
}
}
catch (Exception ex)
{
Plugin.LogWarn("CannibalFx.PlayEatEffects: " + ex.Message);
}
}
private void RpcLogic___PlayEatEffects()
{
//IL_0034: 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)
try
{
Player component = ((Component)this).GetComponent<Player>();
if (!((Object)(object)component == (Object)null))
{
AudioManager.PlayPlayerClip("Swallow", component, true, (AudioDistance)1, 1f, 0.1f);
ParticleManager.Play("Smoke", component.Transform.position, Vector3.up);
}
}
catch (Exception ex)
{
Plugin.LogWarn("CannibalFx RpcLogic: " + ex.Message);
}
}
private void RpcReader___PlayEatEffects(PooledReader r, Channel channel)
{
if (((NetworkBehaviour)this).IsClientInitialized)
{
RpcLogic___PlayEatEffects();
}
}
public override void NetworkInitialize___Early()
{
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0037: Expected O, but got Unknown
if (!_nwEarlyExcuted)
{
_nwEarlyExcuted = true;
((NetworkBehaviour)this).NetworkInitialize___Early();
((SyncBase)_deathTime).InitializeEarly((NetworkBehaviour)(object)this, 0u, false);
((NetworkBehaviour)this).RegisterObserversRpc(0u, new ClientRpcDelegate(RpcReader___PlayEatEffects));
}
}
public override void NetworkInitialize___Late()
{
if (!_nwLateExcuted)
{
_nwLateExcuted = true;
((NetworkBehaviour)this).NetworkInitialize___Late();
((SyncBase)_deathTime).InitializeLate();
}
}
public override void NetworkInitializeIfDisabled()
{
((NetworkBehaviour)this).NetworkInitialize___Early();
((NetworkBehaviour)this).NetworkInitialize___Late();
}
}
public class CorpseEconomy : NetworkBehaviour
{
public readonly SyncVar<int> _price = new SyncVar<int>(0, default(SyncTypeSettings));
public readonly SyncVar<float> _deathTime = new SyncVar<float>(-1f, default(SyncTypeSettings));
private float _pendingDeathTime = -1f;
private bool _hasPendingDeathTime;
private int _pendingPrice;
private bool _hasPendingPrice;
private bool _nwEarlyExcuted;
private bool _nwLateExcuted;
public int Price => _price.Value;
public float DeathTime => _deathTime.Value;
public virtual void Awake()
{
((NetworkBehaviour)this).NetworkInitialize___Early();
((NetworkBehaviour)this).NetworkInitialize___Late();
}
private void Update()
{
try
{
if (((NetworkBehaviour)this).IsServerInitialized)
{
if (_hasPendingDeathTime)
{
_deathTime.Value = _pendingDeathTime;
_hasPendingDeathTime = false;
}
if (_hasPendingPrice)
{
_price.Value = _pendingPrice;
_hasPendingPrice = false;
}
}
}
catch
{
}
}
public void SetDeathTime(float t)
{
try
{
if (((NetworkBehaviour)this).IsServerInitialized)
{
_deathTime.Value = t;
return;
}
_pendingDeathTime = t;
_hasPendingDeathTime = true;
}
catch
{
}
}
public void SetPrice(int price)
{
try
{
if (((NetworkBehaviour)this).IsServerInitialized)
{
_price.Value = price;
return;
}
_pendingPrice = price;
_hasPendingPrice = true;
}
catch
{
}
}
public override void NetworkInitialize___Early()
{
if (!_nwEarlyExcuted)
{
_nwEarlyExcuted = true;
((NetworkBehaviour)this).NetworkInitialize___Early();
((SyncBase)_price).InitializeEarly((NetworkBehaviour)(object)this, 0u, false);
((SyncBase)_deathTime).InitializeEarly((NetworkBehaviour)(object)this, 1u, false);
}
}
public override void NetworkInitialize___Late()
{
if (!_nwLateExcuted)
{
_nwLateExcuted = true;
((NetworkBehaviour)this).NetworkInitialize___Late();
((SyncBase)_price).InitializeLate();
((SyncBase)_deathTime).InitializeLate();
}
}
public override void NetworkInitializeIfDisabled()
{
((NetworkBehaviour)this).NetworkInitialize___Early();
((NetworkBehaviour)this).NetworkInitialize___Late();
}
}
[HarmonyPatch(typeof(PlayerDying), "ServerDie")]
internal static class Patch_ServerDie
{
private static void Postfix(PlayerDying __instance)
{
try
{
DeadPlayer deadPlayer = __instance.DeadPlayer;
if ((Object)(object)deadPlayer == (Object)null)
{
return;
}
Player player = deadPlayer.Player;
float deathTime = DeathTimer.NetworkTime();
if ((Object)(object)player != (Object)null)
{
DeathTimer.RecordServerDeath(player);
CannibalFx component = ((Component)player).GetComponent<CannibalFx>();
if ((Object)(object)component != (Object)null)
{
component.SetDeathTime(deathTime);
}
}
CorpseEconomy component2 = ((Component)deadPlayer).GetComponent<CorpseEconomy>();
if ((Object)(object)component2 != (Object)null)
{
component2.SetDeathTime(deathTime);
}
Plugin.LogInfo("[BetterCorpses] death recorded owner=" + (((Object)(object)player != (Object)null) ? player.SteamName : "?") + " netTime=" + deathTime.ToString("0.0"));
if (Config.EconomyOn)
{
int num = Random.Range(Config.PriceMin.Value, Config.PriceMax.Value + 1);
try
{
AccessTools.Field(typeof(Item), "_worth").SetValue(deadPlayer, num);
}
catch (Exception ex)
{
Plugin.LogWarn("Patch_ServerDie _worth: " + ex.Message);
}
if ((Object)(object)component2 != (Object)null)
{
component2.SetPrice(num);
}
}
}
catch (Exception ex2)
{
Plugin.LogWarn("Patch_ServerDie: " + ex2.Message);
}
}
}
[HarmonyPatch(typeof(PlayerDying), "DeathEffects")]
internal static class Patch_DeathEffects
{
private static void Postfix(PlayerDying __instance)
{
try
{
if (!((NetworkBehaviour)__instance).Owner.IsLocalClient)
{
return;
}
DeathTimer.RecordLocalDeath();
if (!Config.RespawnOn)
{
return;
}
try
{
AccessTools.Field(typeof(PlayerDying), "_totalGiveUpTime").SetValue(__instance, Config.GiveUpHoldSeconds.Value);
}
catch (Exception ex)
{
Plugin.LogWarn("GiveUpHoldSeconds: " + ex.Message);
}
}
catch (Exception ex2)
{
Plugin.LogWarn("Patch_DeathEffects: " + ex2.Message);
}
}
}
[HarmonyPatch(typeof(PlayerDying), "ResurrectEffect")]
internal static class Patch_ResurrectEffect
{
private static void Postfix(PlayerDying __instance)
{
try
{
DeathTimer.ClearLocal();
Player component = ((Component)__instance).GetComponent<Player>();
if ((Object)(object)component != (Object)null)
{
DeathTimer.ClearServer(component);
}
}
catch (Exception ex)
{
Plugin.LogWarn("Patch_ResurrectEffect: " + ex.Message);
}
}
}
[HarmonyPatch(typeof(PlayerDying), "LocalResurrect")]
internal static class Patch_LocalResurrect
{
private static void Prefix(PlayerDying __instance)
{
try
{
if (Config.Master.Value && (Object)(object)__instance.DeadPlayer == (Object)null)
{
AccessTools.Field(typeof(PlayerDying), "_waitingForTpOnRespawn").SetValue(__instance, true);
}
}
catch (Exception ex)
{
Plugin.LogWarn("Patch_LocalResurrect: " + ex.Message);
}
}
}
[HarmonyPatch(typeof(PlayerDying), "MouseClick")]
internal static class Patch_MouseClick
{
private static bool Prefix()
{
if (!Config.RespawnOn)
{
return true;
}
return !DeathTimer.LocalInWait;
}
}
[HarmonyPatch(typeof(DeadPlayer), "PrimaryInput")]
internal static class Patch_DeadPlayerPrimaryInput
{
private static bool Prefix(DeadPlayer __instance)
{
if (!Config.RespawnOn)
{
return true;
}
return !DeathTimer.CorpseInWait(__instance);
}
}
[HarmonyPatch(typeof(Server), "RpcLogic___ResurrectPlayer___2247010027")]
internal static class Patch_ResurrectPlayer
{
private static bool Prefix(object[] __args, out bool __state)
{
Player val = (Player)((__args != null && __args.Length != 0) ? /*isinst with value type is only supported in some contexts*/: null);
DeadPlayer val2 = (DeadPlayer)((__args != null && __args.Length > 1) ? /*isinst with value type is only supported in some contexts*/: null);
if (!((Object)(object)val != (Object)null) || ((NetworkBehaviour)val).IsDeinitializing || !((Object)(object)val.Vitals != (Object)null) || val.Vitals.Health > 0 || !((Object)(object)val2 != (Object)null) || ((NetworkBehaviour)val2).IsDeinitializing)
{
__state = false;
return true;
}
if (Config.RespawnOn && (DeathTimer.ServerInWait(val) || DeathTimer.CorpseInWait(val2)))
{
__state = false;
return false;
}
__state = true;
return true;
}
private static void Postfix(object[] __args, bool __state)
{
if (__state && __args != null && __args.Length >= 2)
{
object obj = __args[0];
Player val = (Player)((obj is Player) ? obj : null);
object obj2 = __args[1];
DeadPlayer corpse = (DeadPlayer)((obj2 is DeadPlayer) ? obj2 : null);
if ((Object)(object)val != (Object)null)
{
DeathTimer.ClearServer(val);
}
CorpseCannibal.TryCannibalize(val, corpse);
}
}
}
[HarmonyPatch(typeof(Server), "RpcLogic___RespawnPlayer___2210451296")]
internal static class Patch_RespawnPlayer
{
private static bool Prefix(object[] __args, out bool __state)
{
__state = true;
if (!Config.RespawnOn)
{
return true;
}
Player val = (Player)((__args != null && __args.Length != 0) ? /*isinst with value type is only supported in some contexts*/: null);
if ((Object)(object)val != (Object)null && DeathTimer.ServerInWait(val))
{
__state = false;
return false;
}
return true;
}
private static void Postfix(object[] __args, bool __state)
{
if (__state && __args != null && __args.Length != 0)
{
object obj = __args[0];
Player val = (Player)((obj is Player) ? obj : null);
if ((Object)(object)val != (Object)null)
{
DeathTimer.ClearServer(val);
}
}
}
}
[HarmonyPatch(typeof(NPC), "EatItem")]
internal static class Patch_NpcEatCorpse
{
private static bool Prefix(NPC __instance, NPCQuest quest, byte questIndex, Item item)
{
//IL_0130: Unknown result type (might be due to invalid IL or missing references)
//IL_0131: Unknown result type (might be due to invalid IL or missing references)
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_0032: Invalid comparison between Unknown and I4
//IL_006a: 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_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
if (!Config.SellOn)
{
return true;
}
if ((Object)(object)item == (Object)null || (Object)(object)item.DeadPlayer == (Object)null)
{
return true;
}
if ((Object)(object)quest == (Object)null || (int)quest.Type != 1)
{
return true;
}
if ((Object)(object)Server.Instance == (Object)null || !((NetworkBehaviour)Server.Instance).IsServerInitialized)
{
return true;
}
if (item.IsDestroying || ((NetworkBehaviour)item).IsDeinitializing)
{
return false;
}
try
{
Vector3 position = ((Component)item).transform.position;
Quaternion rotation = ((Component)item).transform.rotation;
Player player = item.DeadPlayer.Player;
MoneyManager.SellItem(item);
byte b = (byte)AccessTools.Field(typeof(NPC), "_id").GetValue(__instance);
item.DestroyItem((byte)2, b);
try
{
NPCManager.Instance.SendEatEffects(b);
}
catch (Exception ex)
{
Plugin.LogWarn("SendEatEffects: " + ex.Message);
}
try
{
AccessTools.Method(typeof(NPC), "OnQuestProgression", (Type[])null, (Type[])null).Invoke(__instance, new object[1] { questIndex });
}
catch (Exception ex2)
{
Plugin.LogWarn("OnQuestProgression: " + ex2.Message);
}
if ((Object)(object)player != (Object)null)
{
AutoRespawn.Schedule(player, position, rotation);
}
}
catch (Exception ex3)
{
Plugin.LogWarn("Patch_NpcEatCorpse: " + ex3.Message);
}
return false;
}
}
[HarmonyPatch(typeof(CasinoBox), "FixedUpdate")]
internal static class Patch_CasinoBoxCorpses
{
private static bool Prefix(CasinoBox __instance)
{
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_0085: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Unknown result type (might be due to invalid IL or missing references)
//IL_0095: Unknown result type (might be due to invalid IL or missing references)
//IL_009a: Unknown result type (might be due to invalid IL or missing references)
//IL_009b: Unknown result type (might be due to invalid IL or missing references)
//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
if (!Config.GambleOn)
{
return true;
}
if ((Object)(object)Server.Instance == (Object)null || !((NetworkBehaviour)Server.Instance).IsServerInitialized || ((Object)(object)CasinoManager.Instance != (Object)null && CasinoManager.IsBetting))
{
return true;
}
try
{
object? value = AccessTools.Field(typeof(CasinoBox), "_col").GetValue(__instance);
BoxCollider val = (BoxCollider)((value is BoxCollider) ? value : null);
if ((Object)(object)val == (Object)null)
{
return true;
}
Vector3 val2 = ((Component)val).transform.TransformPoint(val.center);
Vector3 val3 = Vector3.Scale(val.size * 0.5f, ((Component)__instance).transform.localScale);
Collider[] array = Physics.OverlapBox(val2, val3, ((Component)__instance).transform.rotation, LayerMask.op_Implicit(GameInfo.ItemLayer));
List<Item> list = new List<Item>();
Collider[] array2 = array;
foreach (Collider val4 in array2)
{
if (((Component)((Component)val4).transform).CompareTag("Item"))
{
Item val5 = ItemManager.Get(val4);
if ((Object)(object)val5 != (Object)null && !list.Contains(val5) && (!Object.op_Implicit((Object)(object)val5.Creature) || val5.Creature.IsDead))
{
list.Add(val5);
}
}
}
CasinoManager.SetBetItems(list);
return false;
}
catch (Exception ex)
{
Plugin.LogWarn("Patch_CasinoBoxCorpses: " + ex.Message);
return true;
}
}
}
[HarmonyPatch(typeof(BetButton), "ToggleAll")]
internal static class Patch_BetButtonToggleAll
{
internal static bool SuppressModEnable;
private static bool Prefix(ref bool to)
{
try
{
if (!Config.GambleOn)
{
return true;
}
if (to || SuppressModEnable)
{
return true;
}
CasinoManager instance = CasinoManager.Instance;
if ((Object)(object)instance == (Object)null)
{
return true;
}
if (instance.TotalWorth != 0)
{
to = true;
}
}
catch (Exception ex)
{
Plugin.LogWarn("Patch_BetButtonToggleAll: " + ex.Message);
}
return true;
}
}
[HarmonyPatch(typeof(CasinoManager), "get_HasPlacedBet")]
internal static class Patch_HasPlacedBet
{
private static void Postfix(ref bool __result)
{
try
{
if (Config.GambleOn)
{
CasinoManager instance = CasinoManager.Instance;
if (!((Object)(object)instance == (Object)null) && instance.TotalWorth != 0)
{
__result = true;
}
}
}
catch (Exception ex)
{
Plugin.LogWarn("Patch_HasPlacedBet: " + ex.Message);
}
}
}
[HarmonyPatch(typeof(CasinoManager), "RpcLogic___BetResultEffects___2774100791")]
internal static class Patch_BetResultEffects
{
private static void Prefix()
{
Patch_BetButtonToggleAll.SuppressModEnable = true;
}
private static void Postfix()
{
Patch_BetButtonToggleAll.SuppressModEnable = false;
}
}
[HarmonyPatch(typeof(SlotMachine), "OnTriggerStay")]
internal static class Patch_SlotMachineCorpses
{
private static bool Prefix(Collider other)
{
//IL_0082: Unknown result type (might be due to invalid IL or missing references)
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_0093: Unknown result type (might be due to invalid IL or missing references)
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
if (!Config.LotteryOn)
{
return true;
}
if ((Object)(object)Server.Instance == (Object)null || !((NetworkBehaviour)Server.Instance).IsServerInitialized || SlotMachine.IsRolling)
{
return true;
}
try
{
Item val = ItemManager.Get(other);
if ((Object)(object)val == (Object)null || (Object)(object)val.DeadPlayer == (Object)null)
{
return true;
}
if (Object.op_Implicit((Object)(object)val.Holder))
{
return true;
}
Player lastHolder = val.LastHolder;
if ((Object)(object)lastHolder == (Object)null)
{
return true;
}
Vector3 position = ((Component)val).transform.position;
Quaternion rotation = ((Component)val).transform.rotation;
Player player = val.DeadPlayer.Player;
val.DestroyItem((byte)4, byte.MaxValue);
SlotMachineManager.RollRandom(lastHolder);
if ((Object)(object)player != (Object)null)
{
AutoRespawn.Schedule(player, position, rotation);
}
return false;
}
catch (Exception ex)
{
Plugin.LogWarn("Patch_SlotMachineCorpses: " + ex.Message);
return true;
}
}
}
[HarmonyPatch(typeof(MoneyManager), "Update")]
internal static class Patch_DriveTick
{
private static void Postfix()
{
Plugin.RuntimeTick();
}
}
[BepInPlugin("top.yw.bettercorpses", "更好的尸体", "1.2.4")]
public class Plugin : BaseUnityPlugin
{
internal static ManualLogSource Log;
public static bool ComponentsAttached;
internal static void LogInfo(string msg)
{
try
{
Log.LogInfo((object)msg);
}
catch
{
}
}
internal static void LogWarn(string msg)
{
try
{
Log.LogWarning((object)msg);
}
catch
{
}
}
private void Awake()
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Expected O, but got Unknown
//IL_0061: Unknown result type (might be due to invalid IL or missing references)
Log = ((BaseUnityPlugin)this).Logger;
Loc.Init();
Config.Init((BaseUnityPlugin)(object)this);
Harmony val = new Harmony("top.yw.bettercorpses");
int num = 0;
int num2 = 0;
Type[] types = typeof(Plugin).Assembly.GetTypes();
foreach (Type type in types)
{
if (type.GetCustomAttributes(typeof(HarmonyPatch), inherit: false).Length != 0)
{
try
{
new PatchClassProcessor(val, type).Patch();
num++;
}
catch (Exception ex)
{
num2++;
LogWarn("补丁应用失败:" + type.Name + " => " + ex.GetType().Name + ": " + ex.Message);
}
}
}
LogInfo("[BetterCorpses] Harmony 补丁应用完成(成功 " + num + " 失败 " + num2 + ")");
}
private void Update()
{
RuntimeTick();
}
public static void RuntimeTick()
{
try
{
EnsureAttached();
}
catch
{
}
try
{
AutoRespawn.Tick();
}
catch
{
}
try
{
HudUI.Tick();
}
catch
{
}
}
private static void EnsureAttached()
{
if (!ComponentsAttached)
{
AttachComponents();
}
}
public static void AttachComponents()
{
if (ComponentsAttached)
{
return;
}
try
{
if (!((Object)(object)GameInfo.DeadPlayerPrefab == (Object)null) && !((Object)(object)GameInfo.PlayerPrefab == (Object)null))
{
GameObject gameObject = ((Component)GameInfo.DeadPlayerPrefab).gameObject;
if ((Object)(object)gameObject.GetComponent<CorpseEconomy>() == (Object)null)
{
gameObject.AddComponent<CorpseEconomy>();
}
GameObject gameObject2 = ((Component)GameInfo.PlayerPrefab).gameObject;
if ((Object)(object)gameObject2.GetComponent<CannibalFx>() == (Object)null)
{
gameObject2.AddComponent<CannibalFx>();
}
ComponentsAttached = true;
LogInfo("[BetterCorpses] CorpseEconomy/CannibalFx 已挂载(DeadPlayerPrefab/PlayerPrefab)");
}
}
catch (Exception ex)
{
LogWarn("AttachComponents 失败(稍后重试): " + ex.Message);
}
}
}
internal static class HudUI
{
private static Canvas _canvas;
private static Text _waitText;
private static Text _corpseWaitText;
private static float _refreshTimer;
private static DeadPlayer _diagCorpse;
private static void Ensure()
{
//IL_0013: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Unknown result type (might be due to invalid IL or missing references)
//IL_005a: 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_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_007b: Unknown result type (might be due to invalid IL or missing references)
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
//IL_00c9: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)_canvas != (Object)null))
{
GameObject val = new GameObject("BetterCorpses_UI");
Object.DontDestroyOnLoad((Object)val);
_canvas = val.AddComponent<Canvas>();
_canvas.renderMode = (RenderMode)0;
_canvas.sortingOrder = 30000;
CanvasScaler obj = val.AddComponent<CanvasScaler>();
obj.uiScaleMode = (ScaleMode)1;
obj.referenceResolution = new Vector2(1920f, 1080f);
val.AddComponent<GraphicRaycaster>();
_waitText = CreateText(val.transform, new Vector2(0.5f, 0.2f), new Vector2(800f, 80f), 44);
((Component)_waitText).gameObject.SetActive(false);
_corpseWaitText = CreateText(val.transform, new Vector2(0.5f, 0.92f), new Vector2(1000f, 90f), 48);
((Component)_corpseWaitText).gameObject.SetActive(false);
}
}
private static Text CreateText(Transform parent, Vector2 anchor, Vector2 size, int fontSize)
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
//IL_0032: 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)
//IL_004a: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: 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_007e: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
//IL_00cc: Unknown result type (might be due to invalid IL or missing references)
GameObject val = new GameObject("WrText", new Type[1] { typeof(RectTransform) });
val.transform.SetParent(parent, false);
RectTransform component = val.GetComponent<RectTransform>();
component.anchorMin = anchor;
component.anchorMax = anchor;
component.pivot = new Vector2(0.5f, 0.5f);
component.anchoredPosition = Vector2.zero;
component.sizeDelta = size;
Text obj = val.AddComponent<Text>();
obj.font = UiFonts.Cjk;
obj.fontSize = fontSize;
((Graphic)obj).color = Color.white;
obj.alignment = (TextAnchor)4;
obj.horizontalOverflow = (HorizontalWrapMode)1;
obj.verticalOverflow = (VerticalWrapMode)1;
Outline obj2 = val.AddComponent<Outline>();
((Shadow)obj2).effectColor = new Color(0f, 0f, 0f, 0.8f);
((Shadow)obj2).effectDistance = new Vector2(1f, -1f);
return obj;
}
public static void Tick()
{
try
{
Ensure();
}
catch
{
return;
}
_refreshTimer -= Time.unscaledDeltaTime;
if (_refreshTimer > 0f)
{
return;
}
_refreshTimer = 0.25f;
try
{
RefreshWaitText();
}
catch
{
}
}
private static void RefreshWaitText()
{
Player localPlayer = Player.LocalPlayer;
if ((Object)(object)localPlayer != (Object)null && (Object)(object)localPlayer.Dying != (Object)null && localPlayer.Dying.IsDead && Config.RespawnOn && DeathTimer.LocalInWait)
{
((Component)_waitText).gameObject.SetActive(true);
_waitText.text = Loc.Get("hud.respawnWait", Mathf.CeilToInt(DeathTimer.LocalRemaining));
((Component)_corpseWaitText).gameObject.SetActive(false);
return;
}
if (Config.RespawnOn && (Object)(object)localPlayer != (Object)null && (Object)(object)localPlayer.Holding != (Object)null)
{
Item heldItem = localPlayer.Holding.HeldItem;
DeadPlayer val = (DeadPlayer)(object)((heldItem is DeadPlayer) ? heldItem : null);
if ((Object)(object)val != (Object)null)
{
if (DeathTimer.CorpseInWait(val))
{
((Component)_corpseWaitText).gameObject.SetActive(true);
_corpseWaitText.text = Loc.Get("hud.corpseWait", Mathf.CeilToInt(DeathTimer.CorpseRemaining(val)));
((Component)_waitText).gameObject.SetActive(false);
return;
}
DiagnoseHeldCorpse(val);
}
}
((Component)_waitText).gameObject.SetActive(false);
((Component)_corpseWaitText).gameObject.SetActive(false);
}
private static void DiagnoseHeldCorpse(DeadPlayer held)
{
try
{
if (!((Object)(object)_diagCorpse == (Object)(object)held))
{
_diagCorpse = held;
Player player = held.Player;
CannibalFx cannibalFx = (((Object)(object)player != (Object)null) ? ((Component)player).GetComponent<CannibalFx>() : null);
CorpseEconomy component = ((Component)held).GetComponent<CorpseEconomy>();
string text = (((Object)(object)player == (Object)null) ? "owner null" : (((Object)(object)cannibalFx == (Object)null) ? "owner has no CannibalFx" : ((cannibalFx.DeathTime < 0f) ? string.Format("fx.deathTime not synced (-1); eco={0}", ((Object)(object)component != (Object)null) ? component.DeathTime.ToString("0.0") : "none") : $"fx.deathTime {cannibalFx.DeathTime:0.0} vs netTime {DeathTimer.NetworkTime():0.0}, wait {Config.ForcedWaitSeconds.Value}")));
Plugin.LogWarn("[BetterCorpses] held corpse not in wait: " + text);
}
}
catch (Exception ex)
{
Plugin.LogWarn("DiagnoseHeldCorpse: " + ex.Message);
}
}
}
internal static class UiFonts
{
private static Font _cjk;
public static Font Cjk
{
get
{
if ((Object)(object)_cjk == (Object)null)
{
string[] array = new string[6] { "Microsoft YaHei", "SimHei", "Microsoft JhengHei", "Noto Sans CJK SC", "Noto Sans SC", "Arial" };
foreach (string text in array)
{
try
{
Font val = Font.CreateDynamicFontFromOSFont(text, 24);
if ((Object)(object)val != (Object)null)
{
_cjk = val;
break;
}
}
catch
{
}
}
if ((Object)(object)_cjk == (Object)null)
{
try
{
_cjk = Resources.GetBuiltinResource<Font>("LegacyRuntime.ttf");
}
catch
{
}
}
if ((Object)(object)_cjk == (Object)null)
{
try
{
_cjk = Resources.GetBuiltinResource<Font>("Arial.ttf");
}
catch
{
}
}
}
return _cjk;
}
}
}