Repository navigation
Expand file tree
/
Copy pathHealthBarsModule.cs
More file actions
213 lines (187 loc) · 8.72 KB
/
Copy pathHealthBarsModule.cs
File metadata and controls
213 lines (187 loc) · 8.72 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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
using System.Collections.Generic;
using BepInEx.Configuration;
using UnityEngine;
namespace HTFToolkit
{
/// <summary>
/// 生物头顶血条:遍历 ItemManager.Items 里的活体 Creature,
/// 把 Hp/MaxHp 画到屏幕上(WorldToScreenPoint)。
/// v1.8.6:血量读 Compat.CreatureHp(SyncVar 本体)——客户端进朋友房间时
/// Creature.Hp 属性可能永远停在 0(spawn 快照时序错开),导致血条全部不显示。
/// </summary>
internal static class HealthBarsModule
{
public static bool Enabled;
private static int _diag;
private static bool _diagLogged;
private static ConfigEntry<float> _maxDistance;
private static ConfigEntry<float> _width;
private static ConfigEntry<float> _height;
private static ConfigEntry<bool> _showText;
private static ConfigEntry<bool> _showBossOnly;
public static void BindConfig(ConfigFile cfg)
{
_maxDistance = cfg.Bind("血条", "最大距离", 150f, "超过该距离不显示血条");
_width = cfg.Bind("血条", "宽度", 150f, "血条宽度(像素)");
_height = cfg.Bind("血条", "高度", 12f, "血条高度(像素)");
_showText = cfg.Bind("血条", "显示数值", true, "血条上方显示 当前/最大 HP");
_showBossOnly = cfg.Bind("血条", "仅Boss", false, "开启后只显示 Boss 血条");
}
public static void Draw()
{
// 无条件记录一次:确认 Draw 是否被调用、以及开关状态(排查"血条不显示")
if (!_diagLogged)
{
_diagLogged = true;
Plugin.Log?.LogInfo($"[HealthBarDiag] Draw invoked, Enabled={Enabled}");
}
if (!Enabled || Event.current == null || Event.current.type != EventType.Repaint)
return;
// ⚠️ 不能用 Camera.main:本作相机未打 MainCamera 标签 → Camera.main 恒为 null →
// 血条函数第一行就 return(v1.9.4 之前的血条一次都没画过就是这个原因)。
// 用游戏自己维护的当前相机 GameInfo.CurCamera(自瞄模块同样绕开 Camera.main 才可用)。
Camera cam = null;
try
{
cam = GameInfo.CurCamera;
}
catch
{
cam = null;
}
if (cam == null)
cam = Camera.main; // 兜底
if (cam == null)
{
if (_diag < 3)
{
_diag++;
Plugin.Log?.LogWarning("[HealthBarDiag] camera is null → 血条无法绘制");
}
return;
}
// 一次性诊断:统计遍历与过滤情况(定位血条为何不显示)
int diagTotal = 0, diagCreature = 0, diagAlive = 0, diagDrawn = 0;
bool doDiag = _diag < 3;
float maxSqr = _maxDistance.Value * _maxDistance.Value;
float sqrMax = Mathf.Max(maxSqr, 0.01f);
Vector3 camPos = cam.transform.position;
foreach (KeyValuePair<Transform, Item> kv in ItemManager.Items)
{
Item item = kv.Value;
if (doDiag) diagTotal++;
// Creature 继承 Item,直接类型判断即可(Creature 内部也会 _creature = this,两种取法等价)
Creature creature = item as Creature;
if (creature == null && item != null)
creature = item.Creature; // 兼容:某些物品上挂着关联生物
if (doDiag && creature != null) diagCreature++;
if (!Compat.CreatureAlive(creature))
continue;
if (doDiag) diagAlive++;
int hp = Compat.CreatureHp(creature);
bool isBoss = creature.BossType != BossType.None;
// 分母:普通生物用 MaxHp;当前全局 boss 有玩家人数加成,用 BossManager 同步的上限
int maxHp = creature.MaxHp;
if (isBoss && BossManager.Boss == creature && BossManager.BossMaxHp > maxHp)
maxHp = BossManager.BossMaxHp;
if (maxHp <= 0)
continue;
if (_showBossOnly.Value && !isBoss)
continue;
Rigidbody rig = creature.Rig;
Vector3 center = rig ? rig.worldCenterOfMass : creature.transform.position;
Vector3 toCenter = center - camPos;
if (toCenter.sqrMagnitude > sqrMax)
continue;
// 位置:头顶
Vector3 world = center + Vector3.up * 1.2f;
Vector3 screen = cam.WorldToScreenPoint(world);
if (screen.z <= 0f)
continue;
float x = screen.x;
float y = Screen.height - screen.y; // GUI 坐标 y 向下
float frac = Mathf.Clamp01(hp / (float)maxHp);
float w = _width.Value;
float h = _height.Value;
// 背景
DrawRect(new Rect(x - w / 2f - 1f, y - h / 2f - 1f, w + 2f, h + 2f), Color.black);
// 血条
Color c = isBoss ? new Color(1f, 0.25f, 0.15f)
: frac > 0.5f ? new Color(0.3f, 0.9f, 0.3f)
: frac > 0.25f ? new Color(0.95f, 0.85f, 0.2f)
: new Color(0.95f, 0.3f, 0.25f);
DrawRect(new Rect(x - w / 2f, y - h / 2f, w * frac, h), c);
if (_showText.Value)
{
// v1.9.12 两行布局:上行名字(中文名)、下行血量。宽度按内容自适应,杜绝换行截断。
float fs = Mathf.Max(12, UIScale.HintFont);
if (_textStyle == null)
{
_textStyle = new GUIStyle(GUI.skin.label)
{
alignment = TextAnchor.MiddleCenter,
fontStyle = FontStyle.Bold,
wordWrap = false,
richText = true
};
}
_textStyle.fontSize = (int)fs;
string name = null;
try
{
name = creature.GetName();
}
catch
{
name = null;
}
if (string.IsNullOrWhiteSpace(name))
name = "???";
if (name.Length > 16)
name = name.Substring(0, 15) + "…";
if (isBoss)
name = "<color=#ff6b5e>" + name + "</color>";
string hpText = $"{hp}/{maxHp}";
_gcName.text = name;
_gcHp.text = hpText;
Vector2 s1 = _textStyle.CalcSize(_gcName);
Vector2 s2 = _textStyle.CalcSize(_gcHp);
float tw = Mathf.Max(s1.x, s2.x) + 10f;
float lineH = fs * 1.35f;
float top = y - h / 2f - lineH * 2f - 4f;
DrawLabel(new Rect(x - tw / 2f, top, tw, lineH), name, _textStyle);
DrawLabel(new Rect(x - tw / 2f, top + lineH + 2f, tw, lineH), hpText, _textStyle);
}
if (doDiag) diagDrawn++;
}
if (doDiag)
{
_diag++;
Plugin.Log?.LogInfo(
$"[HealthBarDiag] enabled={Enabled} cam={(cam != null ? cam.name : "null")} " +
$"items={diagTotal} creatures={diagCreature} alive={diagAlive} drawn={diagDrawn} " +
$"maxDist={_maxDistance.Value} bossOnly={_showBossOnly.Value}");
}
}
private static void DrawRect(Rect r, Color c)
{
Color prev = GUI.color;
GUI.color = c;
GUI.DrawTexture(r, Texture2D.whiteTexture);
GUI.color = prev;
}
private static GUIStyle _textStyle;
private static GUIContent _gcName = new GUIContent();
private static GUIContent _gcHp = new GUIContent();
private static void DrawLabel(Rect r, string text, GUIStyle style)
{
Color prev = GUI.color;
GUI.color = Color.black;
Rect shadow = new Rect(r.x + 1, r.y + 1, r.width, r.height);
GUI.Label(shadow, text, style);
GUI.color = Color.white;
GUI.Label(r, text, style);
GUI.color = prev;
}
}
}