Repository navigation
Expand file tree
/
Copy pathGodModeModule.cs
More file actions
144 lines (128 loc) · 5.79 KB
/
Copy pathGodModeModule.cs
File metadata and controls
144 lines (128 loc) · 5.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
using System.Reflection;
using BepInEx.Configuration;
using HarmonyLib;
using UnityEngine;
namespace HTFToolkit
{
/// <summary>
/// 锁血(无敌)。两层拦截:
/// 1. 前缀拦截 PlayerVitals.TakeDamage —— 覆盖所有伤害入口(子弹/近战/中毒/火烧/饥饿)
/// 2. 前缀拦截 PlayerVitals.LocalHit —— 客户端本地命中入口
/// ⚠️ v1.8.6 起不再碰 PlayerManager.InGodMode:那是游戏内置开发者作弊位,
/// 除跳过服务器伤害结算外,还直接解锁跳跃条件(PlayerMovement.JumpInput 里的
/// "|| PlayerManager.InGodMode")→ 开锁血即无限连跳;且该位是静态的,主机上
/// 会让全房间玩家免伤(TakeDamage 对每个玩家结算时都检查这一个静态位)。
/// 单人/主机下 TakeDamage 在本机运行,前缀拦截已完全覆盖,无需 InGodMode。
/// v1.9.0 客户端路径:伤害由主机结算,本机 Harmony 拦截无效。改用游戏内置
/// AFK 免伤通道——TakeDamage 结算条件含 (!_player.IsAfk || _player.AfkFromPause),
/// Server.SetIsAfk 为零校验 ServerRpc → 客户端自设 (IsAfk=true, AfkFromPause=false)
/// 即可让主机跳过对自己的伤害结算;另挂死亡兜底(ResurrectPlayer 自动复活)。
/// 副作用:AFK 状态下不掉饱食度(无 UI 标记,关闭锁血即恢复)。
/// </summary>
internal static class GodModeModule
{
public static bool Enabled;
private static PropertyInfo _inGodModeProp;
// ---- 客户端 AFK 免伤状态 ----
private static float _lastAfkAttempt = -10f;
private static bool _clientNotified;
private static bool _wasDead;
public static void BindConfig(ConfigFile cfg)
{
// 暂无可调参数,占位
}
public static void ApplyPatches(Harmony harmony)
{
_inGodModeProp = AccessTools.Property(typeof(PlayerManager), "InGodMode");
var takeDamage = AccessTools.Method(typeof(PlayerVitals), "TakeDamage");
var localHit = AccessTools.Method(typeof(PlayerVitals), "LocalHit");
var prefix = new HarmonyMethod(AccessTools.Method(typeof(GodModeModule), nameof(SkipWhenGodMode)));
if (takeDamage != null)
harmony.Patch(takeDamage, prefix: prefix);
if (localHit != null)
harmony.Patch(localHit, prefix: prefix);
}
/// <summary>锁血开启时返回 false,跳过原伤害方法(Harmony 前缀返回 false = 跳过原逻辑)。</summary>
private static bool SkipWhenGodMode()
{
return !Enabled;
}
/// <summary>
/// 每帧客户端侧维持(Plugin.Update 调用):
/// - 开启中:确保 IsAfk=true(服务器侧被复位/重连后自愈),死亡时自动复活
/// - 内部已做主机/客户端分流,主机与单人直接短路返回(Harmony 前缀已覆盖)
/// </summary>
public static void ClientTick()
{
if (!Enabled)
return;
try
{
if (Compat.IsHost || !Compat.ClientRpcReady)
return;
Player p = Player.LocalPlayer;
if (p == null)
return;
// 维持 AFK 免伤:服务器/本机状态被复位时每 2 秒重发一次
if (!p.IsAfk && Time.unscaledTime - _lastAfkAttempt > 2f)
{
_lastAfkAttempt = Time.unscaledTime;
Server.Instance.SetIsAfk(p, true, false);
if (!_clientNotified)
{
_clientNotified = true;
ToastModule.Show("锁血: 客户端模式(AFK 免伤)", Color.white);
}
}
// 死亡兜底:免疫外伤害(坠落/出界等)仍可能致死,自动复活
bool dead = p.Vitals != null && p.Vitals.Health <= 0
&& p.Dying != null && p.Dying.DeadPlayer != null;
if (dead && !_wasDead)
{
_wasDead = true;
Server.Instance.ResurrectPlayer(p, p.Dying.DeadPlayer);
Plugin.Log?.LogInfo("GodMode client: auto resurrect");
}
else if (!dead)
{
_wasDead = false;
}
}
catch (System.Exception e)
{
Plugin.Log?.LogWarning($"GodMode client tick: {e.Message}");
}
}
/// <summary>
/// 关闭锁血时调用:清除 InGodMode 残留(v1.8.5 及之前的历史问题),
/// 并退出客户端 AFK 免伤状态。
/// </summary>
public static void ClearGodModeResidue()
{
try
{
_inGodModeProp?.SetValue(null, false, null);
}
catch
{
// 静默失败:最坏情况残留无限跳,重启游戏可恢复
}
// 客户端:退出 AFK 免伤(与 EndGameUI 的 SetIsAfk(false, false) 同款)
try
{
if (Compat.ClientRpcReady && !Compat.IsHost && Player.LocalPlayer != null
&& Player.LocalPlayer.IsAfk)
{
Server.Instance.SetIsAfk(Player.LocalPlayer, false, false);
ToastModule.Show("锁血: 已退出客户端免伤", Color.white);
}
}
catch
{
// 静默:下一帧 ClientTick 会按 Enabled=false 跳过
}
_wasDead = false;
_clientNotified = false;
}
}
}