summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/gui.cpp126
-rw-r--r--src/hl2h.h7
-rw-r--r--src/menu.cpp50
-rw-r--r--src/studio.cpp35
4 files changed, 162 insertions, 56 deletions
diff --git a/src/gui.cpp b/src/gui.cpp
index 3cbb40a..89888da 100644
--- a/src/gui.cpp
+++ b/src/gui.cpp
@@ -195,7 +195,8 @@ struct Ctrl {
195 const char *desc; 195 const char *desc;
196 bool *bval; // CK_TOGGLE 196 bool *bval; // CK_TOGGLE
197 int *ival; // CK_SLIDER 197 int *ival; // CK_SLIDER
198 int *rgb; // CK_COLOR: three ints, 0..255 198 int *rgb; // CK_COLOR: three or four ints, 0..255 (see has_alpha)
199 bool has_alpha; // CK_COLOR: rgb[3] is an editable alpha channel
199 const char *const *opts; // CK_DROPDOWN 200 const char *const *opts; // CK_DROPDOWN
200 int nopts; // CK_DROPDOWN 201 int nopts; // CK_DROPDOWN
201 int min, max; 202 int min, max;
@@ -256,6 +257,7 @@ static void add_ctrl(int card, int kind, const char *label, const char *desc,
256 c.action = action; 257 c.action = action;
257 c.zero_unlimited = false; 258 c.zero_unlimited = false;
258 c.rgb = nullptr; 259 c.rgb = nullptr;
260 c.has_alpha = false;
259 c.opts = nullptr; 261 c.opts = nullptr;
260 c.nopts = 0; 262 c.nopts = 0;
261 c.hue = 0.0f; 263 c.hue = 0.0f;
@@ -287,6 +289,14 @@ static void add_color(int card, const char *label, const char *desc,
287 add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE); 289 add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE);
288 g_ctrls[i].rgb = rgb; 290 g_ctrls[i].rgb = rgb;
289} 291}
292// Same picker, plus an editable alpha bar; rgb must hold four ints.
293static void add_color_alpha(int card, const char *label, const char *desc,
294 int *rgba) {
295 int i = g_nctrl;
296 add_ctrl(card, CK_COLOR, label, desc, 0, 255, nullptr, nullptr, ACT_NONE);
297 g_ctrls[i].rgb = rgba;
298 g_ctrls[i].has_alpha = true;
299}
290static void add_dropdown(int card, const char *label, const char *desc, 300static void add_dropdown(int card, const char *label, const char *desc,
291 const char *const *opts, int nopts, int *value) { 301 const char *const *opts, int nopts, int *value) {
292 int i = g_nctrl; 302 int i = g_nctrl;
@@ -456,15 +466,15 @@ static void build_cards() {
456 &g.vis_on); 466 &g.vis_on);
457 add_dropdown(chams, "Visible material", "Material for the visible pass.", 467 add_dropdown(chams, "Visible material", "Material for the visible pass.",
458 chams_mat_names, 7, &g.vis_mat); 468 chams_mat_names, 7, &g.vis_mat);
459 add_color(chams, "Visible colour", "Colour for the visible pass.", 469 add_color_alpha(chams, "Visible colour", "Colour and alpha for the visible pass.",
460 g.vis_rgb); 470 g.vis_rgb);
461 add_toggle(chams, "Through walls", "Draw a pass with the depth test off.", 471 add_toggle(chams, "Through walls", "Draw a pass with the depth test off.",
462 &g.inv_on); 472 &g.inv_on);
463 add_dropdown(chams, "Invisible material", 473 add_dropdown(chams, "Invisible material",
464 "Material for the through-wall pass.", chams_mat_names, 7, 474 "Material for the through-wall pass.", chams_mat_names, 7,
465 &g.inv_mat); 475 &g.inv_mat);
466 add_color(chams, "Invisible colour", 476 add_color_alpha(chams, "Invisible colour",
467 "Colour for the through-wall pass.", g.inv_rgb); 477 "Colour and alpha for the through-wall pass.", g.inv_rgb);
468 }; 478 };
469 add_chams_group("Hostile", g_chams_hostile); 479 add_chams_group("Hostile", g_chams_hostile);
470 add_chams_group("Neutral", g_chams_neutral); 480 add_chams_group("Neutral", g_chams_neutral);
@@ -475,8 +485,8 @@ static void build_cards() {
475 &g_chams_viewmodel); 485 &g_chams_viewmodel);
476 add_dropdown(chams, "Viewmodel material", "Material for the viewmodel.", 486 add_dropdown(chams, "Viewmodel material", "Material for the viewmodel.",
477 chams_mat_names, 7, &g_chams_vm_mat); 487 chams_mat_names, 7, &g_chams_vm_mat);
478 add_color(chams, "Viewmodel colour", "Colour for the viewmodel.", 488 add_color_alpha(chams, "Viewmodel colour", "Colour and alpha for the viewmodel.",
479 g_chams_vm_rgb); 489 g_chams_vm_rgb);
480 int view = add_card(2, nullptr, "Camera / display", 490 int view = add_card(2, nullptr, "Camera / display",
481 "Camera FOV, viewmodel FOV and HDR brightness.", true); 491 "Camera FOV, viewmodel FOV and HDR brightness.", true);
482 add_slider(view, "Camera FOV", 492 add_slider(view, "Camera FOV",
@@ -970,7 +980,7 @@ static int button_w(const char *label) {
970 980
971static int label_row_h(const Ctrl &c); 981static int label_row_h(const Ctrl &c);
972static bool picker_open_for(const Ctrl &c); 982static bool picker_open_for(const Ctrl &c);
973static int picker_panel_h(); 983static int picker_panel_h(bool with_alpha);
974static bool card_in_category(int i); 984static bool card_in_category(int i);
975static Rect card_rect(int index, int y); 985static Rect card_rect(int index, int y);
976static int card_h(const Card &c); 986static int card_h(const Card &c);
@@ -978,7 +988,7 @@ static int card_h(const Card &c);
978static int row_h(const Ctrl &c) { 988static int row_h(const Ctrl &c) {
979 int h = label_row_h(c); 989 int h = label_row_h(c);
980 if (picker_open_for(c)) 990 if (picker_open_for(c))
981 h += picker_panel_h(); 991 h += picker_panel_h(c.has_alpha);
982 return h; 992 return h;
983} 993}
984 994
@@ -989,6 +999,7 @@ static int row_h(const Ctrl &c) {
989static constexpr int PICKER_SV_W = 180; 999static constexpr int PICKER_SV_W = 180;
990static constexpr int PICKER_SV_H = 110; 1000static constexpr int PICKER_SV_H = 110;
991static constexpr int PICKER_HUE_H = 12; 1001static constexpr int PICKER_HUE_H = 12;
1002static constexpr int PICKER_ALPHA_H = 12;
992static constexpr int PICKER_GAP = 8; 1003static constexpr int PICKER_GAP = 8;
993 1004
994static bool picker_open_for(const Ctrl &c) { 1005static bool picker_open_for(const Ctrl &c) {
@@ -996,9 +1007,11 @@ static bool picker_open_for(const Ctrl &c) {
996 &c == &g_ctrls[g_picker_ctrl]; 1007 &c == &g_ctrls[g_picker_ctrl];
997} 1008}
998 1009
999static int picker_panel_h() { 1010static int picker_panel_h(bool with_alpha) {
1000 return S((float)(PICKER_GAP + PICKER_SV_H + PICKER_GAP + PICKER_HUE_H + 1011 int h = PICKER_GAP + PICKER_SV_H + PICKER_GAP + PICKER_HUE_H + PICKER_GAP;
1001 PICKER_GAP)); 1012 if (with_alpha)
1013 h += PICKER_ALPHA_H + PICKER_GAP;
1014 return S((float)h);
1002} 1015}
1003 1016
1004static int label_row_h(const Ctrl &c) { 1017static int label_row_h(const Ctrl &c) {
@@ -1069,16 +1082,17 @@ static void rgb_to_hsv(int r, int g, int b, float &h, float &s, float &v) {
1069} 1082}
1070 1083
1071struct PickerRects { 1084struct PickerRects {
1072 Rect panel, sv, hue, preview; 1085 Rect panel, sv, hue, alpha, preview;
1073}; 1086};
1074 1087
1075static PickerRects picker_rects(const Rect &cr, int row_y, int base_h) { 1088static PickerRects picker_rects(const Rect &cr, int row_y, int base_h,
1089 bool with_alpha) {
1076 PickerRects p{}; 1090 PickerRects p{};
1077 int px = cr.x + S(16.0f); 1091 int px = cr.x + S(16.0f);
1078 int py = row_y + base_h; 1092 int py = row_y + base_h;
1079 int pw = card_content_w() - S(32.0f); 1093 int pw = card_content_w() - S(32.0f);
1080 if (pw > S(340.0f)) pw = S(340.0f); 1094 if (pw > S(340.0f)) pw = S(340.0f);
1081 p.panel = Rect{px, py, pw, picker_panel_h()}; 1095 p.panel = Rect{px, py, pw, picker_panel_h(with_alpha)};
1082 int preview_w = S(84.0f); 1096 int preview_w = S(84.0f);
1083 int svw = S((float)PICKER_SV_W); 1097 int svw = S((float)PICKER_SV_W);
1084 if (svw + preview_w + S(16.0f) > pw) 1098 if (svw + preview_w + S(16.0f) > pw)
@@ -1089,6 +1103,8 @@ static PickerRects picker_rects(const Rect &cr, int row_y, int base_h) {
1089 S((float)PICKER_SV_H)}; 1103 S((float)PICKER_SV_H)};
1090 p.hue = Rect{p.sv.x, p.sv.y + p.sv.h + S(PICKER_GAP), svw, 1104 p.hue = Rect{p.sv.x, p.sv.y + p.sv.h + S(PICKER_GAP), svw,
1091 S((float)PICKER_HUE_H)}; 1105 S((float)PICKER_HUE_H)};
1106 p.alpha = Rect{p.hue.x, p.hue.y + p.hue.h + S(PICKER_GAP), svw,
1107 S((float)PICKER_ALPHA_H)};
1092 p.preview = Rect{p.sv.x + p.sv.w + S(12.0f), p.sv.y, preview_w, S(52.0f)}; 1108 p.preview = Rect{p.sv.x + p.sv.w + S(12.0f), p.sv.y, preview_w, S(52.0f)};
1093 return p; 1109 return p;
1094} 1110}
@@ -1175,7 +1191,7 @@ static bool picker_rects_for(int ctrl_index, PickerRects &out) {
1175 if (!control_rect(ctrl_index, card, row)) 1191 if (!control_rect(ctrl_index, card, row))
1176 return false; 1192 return false;
1177 Ctrl &ct = g_ctrls[ctrl_index]; 1193 Ctrl &ct = g_ctrls[ctrl_index];
1178 out = picker_rects(card, row.y, label_row_h(ct)); 1194 out = picker_rects(card, row.y, label_row_h(ct), ct.has_alpha);
1179 return true; 1195 return true;
1180} 1196}
1181 1197
@@ -1191,18 +1207,47 @@ static void picker_apply(Ctrl &ct, const PickerRects &p) {
1191 if (v > 1.0f) v = 1.0f; 1207 if (v > 1.0f) v = 1.0f;
1192 ct.sat = s; 1208 ct.sat = s;
1193 ct.val = v; 1209 ct.val = v;
1210 hsv_to_rgb(ct.hue, ct.sat, ct.val, ct.rgb[0], ct.rgb[1], ct.rgb[2]);
1194 } else if (g_picker_drag == 2) { 1211 } else if (g_picker_drag == 2) {
1195 float h = 360.0f * (float)(g_mouse_x - p.hue.x) / (float)p.hue.w; 1212 float h = 360.0f * (float)(g_mouse_x - p.hue.x) / (float)p.hue.w;
1196 if (h < 0.0f) h = 0.0f; 1213 if (h < 0.0f) h = 0.0f;
1197 if (h >= 360.0f) h = 359.9f; 1214 if (h >= 360.0f) h = 359.9f;
1198 ct.hue = h; 1215 ct.hue = h;
1216 hsv_to_rgb(ct.hue, ct.sat, ct.val, ct.rgb[0], ct.rgb[1], ct.rgb[2]);
1217 } else if (g_picker_drag == 3 && ct.has_alpha) {
1218 if (p.alpha.w <= 0)
1219 return;
1220 int a = (int)(255.0f * (float)(g_mouse_x - p.alpha.x) /
1221 (float)p.alpha.w +
1222 0.5f);
1223 if (a < 0) a = 0;
1224 if (a > 255) a = 255;
1225 ct.rgb[3] = a;
1226 }
1227}
1228
1229static void draw_checker(const Rect &r, const Rect *clip) {
1230 int sq = S(6.0f);
1231 if (sq < 4)
1232 sq = 4;
1233 for (int yy = 0; yy < r.h; yy += sq) {
1234 for (int xx = 0; xx < r.w; xx += sq) {
1235 int w = sq < r.w - xx ? sq : r.w - xx;
1236 int h = sq < r.h - yy ? sq : r.h - yy;
1237 bool odd = ((xx / sq) + (yy / sq)) & 1;
1238 fill(Rect{r.x + xx, r.y + yy, w, h},
1239 odd ? Col{62, 66, 74, 255} : Col{38, 41, 47, 255}, clip);
1240 }
1199 } 1241 }
1200 hsv_to_rgb(ct.hue, ct.sat, ct.val, ct.rgb[0], ct.rgb[1], ct.rgb[2]);
1201} 1242}
1202 1243
1203static void draw_picker(const Rect &clip, const Rect &cr, const Ctrl &ct, 1244static void draw_picker(const Rect &clip, const Rect &cr, const Ctrl &ct,
1204 int row_y) { 1245 int row_y) {
1205 PickerRects p = picker_rects(cr, row_y, label_row_h(ct)); 1246 PickerRects p = picker_rects(cr, row_y, label_row_h(ct), ct.has_alpha);
1247 int pr = ct.rgb ? ct.rgb[0] : 255;
1248 int pg = ct.rgb ? ct.rgb[1] : 255;
1249 int pb = ct.rgb ? ct.rgb[2] : 255;
1250 int pa = (ct.has_alpha && ct.rgb) ? ct.rgb[3] : 255;
1206 fill(p.panel, Col{20, 23, 29, 250}, &clip); 1251 fill(p.panel, Col{20, 23, 29, 250}, &clip);
1207 outline(p.panel, C_BORDER, &clip); 1252 outline(p.panel, C_BORDER, &clip);
1208 1253
@@ -1236,6 +1281,23 @@ static void draw_picker(const Rect &clip, const Rect &cr, const Ctrl &ct,
1236 } 1281 }
1237 outline(p.hue, Col{0, 0, 0, 255}, &clip); 1282 outline(p.hue, Col{0, 0, 0, 255}, &clip);
1238 1283
1284 // Alpha bar: checkerboard behind a transparent->opaque colour gradient.
1285 if (ct.has_alpha) {
1286 draw_checker(p.alpha, &clip);
1287 int ax = S(4.0f);
1288 if (ax < 2) ax = 2;
1289 for (int xx = 0; xx < p.alpha.w; xx += ax) {
1290 int a = p.alpha.w > 1 ? 255 * xx / (p.alpha.w - 1) : 255;
1291 int rw = ax < p.alpha.w - xx ? ax : p.alpha.w - xx;
1292 fill(Rect{p.alpha.x + xx, p.alpha.y, rw, p.alpha.h},
1293 Col{pr, pg, pb, a}, &clip);
1294 }
1295 outline(p.alpha, Col{0, 0, 0, 255}, &clip);
1296 int amx = p.alpha.x + (int)((float)pa / 255.0f * (float)p.alpha.w);
1297 line(amx, p.alpha.y - S(2.0f), amx, p.alpha.y + p.alpha.h + S(2.0f),
1298 Col{255, 255, 255, 255});
1299 }
1300
1239 // Selection markers. 1301 // Selection markers.
1240 int mx = p.sv.x + (int)(ct.sat * (float)p.sv.w); 1302 int mx = p.sv.x + (int)(ct.sat * (float)p.sv.w);
1241 int my = p.sv.y + (int)((1.0f - ct.val) * (float)p.sv.h); 1303 int my = p.sv.y + (int)((1.0f - ct.val) * (float)p.sv.h);
@@ -1249,17 +1311,21 @@ static void draw_picker(const Rect &clip, const Rect &cr, const Ctrl &ct,
1249 Col{255, 255, 255, 255}); 1311 Col{255, 255, 255, 255});
1250 1312
1251 // Preview swatch and hex value. 1313 // Preview swatch and hex value.
1252 int pr = ct.rgb ? ct.rgb[0] : 255; 1314 draw_checker(p.preview, &clip);
1253 int pg = ct.rgb ? ct.rgb[1] : 255; 1315 fill(p.preview, Col{pr, pg, pb, pa}, &clip);
1254 int pb = ct.rgb ? ct.rgb[2] : 255;
1255 fill(p.preview, Col{pr, pg, pb, 255}, &clip);
1256 outline(p.preview, C_BORDER, &clip); 1316 outline(p.preview, C_BORDER, &clip);
1257 char hex[16]; 1317 char hex[16];
1258 snprintf(hex, sizeof(hex), "#%02X%02X%02X", pr, pg, pb); 1318 if (ct.has_alpha)
1319 snprintf(hex, sizeof(hex), "#%02X%02X%02X%02X", pr, pg, pb, pa);
1320 else
1321 snprintf(hex, sizeof(hex), "#%02X%02X%02X", pr, pg, pb);
1259 text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f), hex, 1322 text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f), hex,
1260 g_font_body, g_body_h, p.preview.w, C_TEXT); 1323 g_font_body, g_body_h, p.preview.w, C_TEXT);
1261 char rgb[48]; 1324 char rgb[48];
1262 snprintf(rgb, sizeof(rgb), "%d %d %d", pr, pg, pb); 1325 if (ct.has_alpha)
1326 snprintf(rgb, sizeof(rgb), "%d %d %d %d", pr, pg, pb, pa);
1327 else
1328 snprintf(rgb, sizeof(rgb), "%d %d %d", pr, pg, pb);
1263 text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f) + g_body_h, 1329 text_in(clip, p.preview.x, p.preview.y + p.preview.h + S(8.0f) + g_body_h,
1264 rgb, g_font_body, g_body_h, p.preview.w, C_DIM); 1330 rgb, g_font_body, g_body_h, p.preview.w, C_DIM);
1265} 1331}
@@ -1581,7 +1647,7 @@ static void handle_mouse_click() {
1581 int base = label_row_h(ct); 1647 int base = label_row_h(ct);
1582 bool is_open = (g_picker_ctrl == idx); 1648 bool is_open = (g_picker_ctrl == idx);
1583 if (is_open) { 1649 if (is_open) {
1584 PickerRects p = picker_rects(cr, by, base); 1650 PickerRects p = picker_rects(cr, by, base, ct.has_alpha);
1585 if (p.sv.has(mx, my)) { 1651 if (p.sv.has(mx, my)) {
1586 g_picker_drag = 1; 1652 g_picker_drag = 1;
1587 picker_apply(g_ctrls[idx], p); 1653 picker_apply(g_ctrls[idx], p);
@@ -1592,6 +1658,11 @@ static void handle_mouse_click() {
1592 picker_apply(g_ctrls[idx], p); 1658 picker_apply(g_ctrls[idx], p);
1593 return; 1659 return;
1594 } 1660 }
1661 if (ct.has_alpha && p.alpha.has(mx, my)) {
1662 g_picker_drag = 3;
1663 picker_apply(g_ctrls[idx], p);
1664 return;
1665 }
1595 } 1666 }
1596 Rect sw{cr.x + cr.w - S(96.0f), by + (base - S(20.0f)) / 2, 1667 Rect sw{cr.x + cr.w - S(96.0f), by + (base - S(20.0f)) / 2,
1597 S(72.0f), S(20.0f)}; 1668 S(72.0f), S(20.0f)};
@@ -2015,9 +2086,12 @@ static void draw_card(const Rect &clip, int index, int y) {
2015 int pr = ct.rgb ? ct.rgb[0] : 255; 2086 int pr = ct.rgb ? ct.rgb[0] : 255;
2016 int pg = ct.rgb ? ct.rgb[1] : 255; 2087 int pg = ct.rgb ? ct.rgb[1] : 255;
2017 int pb = ct.rgb ? ct.rgb[2] : 255; 2088 int pb = ct.rgb ? ct.rgb[2] : 255;
2089 int pa = (ct.has_alpha && ct.rgb) ? ct.rgb[3] : 255;
2018 Rect sw{cr.x + cr.w - S(96.0f), 2090 Rect sw{cr.x + cr.w - S(96.0f),
2019 by + (base_h - S(20.0f)) / 2, S(72.0f), S(20.0f)}; 2091 by + (base_h - S(20.0f)) / 2, S(72.0f), S(20.0f)};
2020 fill(sw, Col{pr, pg, pb, 255}, &clip); 2092 if (pa < 255)
2093 draw_checker(sw, &clip);
2094 fill(sw, Col{pr, pg, pb, pa}, &clip);
2021 outline(sw, picker_open_for(ct) ? C_ACCENT : C_BORDER, &clip); 2095 outline(sw, picker_open_for(ct) ? C_ACCENT : C_BORDER, &clip);
2022 if (picker_open_for(ct)) 2096 if (picker_open_for(ct))
2023 draw_picker(clip, cr, ct, by); 2097 draw_picker(clip, cr, ct, by);
diff --git a/src/hl2h.h b/src/hl2h.h
index 4c9012f..f7274f1 100644
--- a/src/hl2h.h
+++ b/src/hl2h.h
@@ -236,15 +236,16 @@ extern bool g_proj_fuse, g_proj_radius;
236struct ChamsGroup { 236struct ChamsGroup {
237 bool vis_on; 237 bool vis_on;
238 int vis_mat; 238 int vis_mat;
239 int vis_rgb[3]; 239 int vis_rgb[4]; // r, g, b, a (0..255)
240 bool inv_on; 240 bool inv_on;
241 int inv_mat; 241 int inv_mat;
242 int inv_rgb[3]; 242 int inv_rgb[4];
243}; 243};
244extern ChamsGroup g_chams_hostile, g_chams_neutral, g_chams_friendly; 244extern ChamsGroup g_chams_hostile, g_chams_neutral, g_chams_friendly;
245extern bool g_chams_viewmodel; 245extern bool g_chams_viewmodel;
246extern int g_chams_vm_mat; 246extern int g_chams_vm_mat;
247extern int g_chams_vm_rgb[3];extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on; 247extern int g_chams_vm_rgb[4];
248extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on;
248extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3]; 249extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3];
249extern bool g_fog_skybox, g_fog_disable; 250extern bool g_fog_skybox, g_fog_disable;
250extern int g_fog_rgb[3], g_fog_start, g_fog_end; 251extern int g_fog_rgb[3], g_fog_start, g_fog_end;
diff --git a/src/menu.cpp b/src/menu.cpp
index bdb9531..873f5ed 100644
--- a/src/menu.cpp
+++ b/src/menu.cpp
@@ -64,12 +64,15 @@ FeatureState g_projectiles{true, 0}; // grenade / rocket ESP
64bool g_proj_fuse = true; 64bool g_proj_fuse = true;
65bool g_proj_radius = true; 65bool g_proj_radius = true;
66FeatureState g_chams{false, 0}; 66FeatureState g_chams{false, 0};
67ChamsGroup g_chams_hostile{true, 3, {255, 60, 60}, true, 3, {255, 60, 60}}; 67ChamsGroup g_chams_hostile{true, 3, {255, 60, 60, 255}, true, 3,
68ChamsGroup g_chams_neutral{true, 3, {220, 220, 220}, false, 3, {220, 220, 220}}; 68 {255, 60, 60, 255}};
69ChamsGroup g_chams_friendly{true, 3, {60, 255, 60}, false, 3, {60, 255, 60}}; 69ChamsGroup g_chams_neutral{true, 3, {220, 220, 220, 255}, false, 3,
70 {220, 220, 220, 255}};
71ChamsGroup g_chams_friendly{true, 3, {60, 255, 60, 255}, false, 3,
72 {60, 255, 60, 255}};
70bool g_chams_viewmodel = false; 73bool g_chams_viewmodel = false;
71int g_chams_vm_mat = 3; 74int g_chams_vm_mat = 3;
72int g_chams_vm_rgb[3] = {200, 200, 255}; 75int g_chams_vm_rgb[4] = {200, 200, 255, 255};
73bool g_item_health = true; 76bool g_item_health = true;
74bool g_item_ammo = true; 77bool g_item_ammo = true;
75bool g_item_armor = true; 78bool g_item_armor = true;
@@ -244,17 +247,18 @@ void config_save() {
244 fprintf(f, "[chams]\nactive=%d\nbind=%d\n", g_chams.active, g_chams.bind); 247 fprintf(f, "[chams]\nactive=%d\nbind=%d\n", g_chams.active, g_chams.bind);
245 auto save_chams = [&](const char *name, const ChamsGroup &g) { 248 auto save_chams = [&](const char *name, const ChamsGroup &g) {
246 fprintf(f, "[%s]\nvis=%d\nvis_mat=%d\nvis_r=%d\nvis_g=%d\nvis_b=%d\n" 249 fprintf(f, "[%s]\nvis=%d\nvis_mat=%d\nvis_r=%d\nvis_g=%d\nvis_b=%d\n"
247 "inv=%d\ninv_mat=%d\ninv_r=%d\ninv_g=%d\ninv_b=%d\n", 250 "vis_a=%d\ninv=%d\ninv_mat=%d\ninv_r=%d\ninv_g=%d\ninv_b=%d\n"
251 "inv_a=%d\n",
248 name, (int)g.vis_on, g.vis_mat, g.vis_rgb[0], g.vis_rgb[1], 252 name, (int)g.vis_on, g.vis_mat, g.vis_rgb[0], g.vis_rgb[1],
249 g.vis_rgb[2], (int)g.inv_on, g.inv_mat, g.inv_rgb[0], 253 g.vis_rgb[2], g.vis_rgb[3], (int)g.inv_on, g.inv_mat,
250 g.inv_rgb[1], g.inv_rgb[2]); 254 g.inv_rgb[0], g.inv_rgb[1], g.inv_rgb[2], g.inv_rgb[3]);
251 }; 255 };
252 save_chams("chams_hostile", g_chams_hostile); 256 save_chams("chams_hostile", g_chams_hostile);
253 save_chams("chams_neutral", g_chams_neutral); 257 save_chams("chams_neutral", g_chams_neutral);
254 save_chams("chams_friendly", g_chams_friendly); 258 save_chams("chams_friendly", g_chams_friendly);
255 fprintf(f, "[chams_vm]\non=%d\nmat=%d\nr=%d\ng=%d\nb=%d\n", 259 fprintf(f, "[chams_vm]\non=%d\nmat=%d\nr=%d\ng=%d\nb=%d\na=%d\n",
256 (int)g_chams_viewmodel, g_chams_vm_mat, g_chams_vm_rgb[0], 260 (int)g_chams_viewmodel, g_chams_vm_mat, g_chams_vm_rgb[0],
257 g_chams_vm_rgb[1], g_chams_vm_rgb[2]); 261 g_chams_vm_rgb[1], g_chams_vm_rgb[2], g_chams_vm_rgb[3]);
258 fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" 262 fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n"
259 "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n" 263 "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n"
260 "usable=%d\n", 264 "usable=%d\n",
@@ -432,11 +436,13 @@ static bool config_load() {
432 if (v > 6) v = 6; 436 if (v > 6) v = 6;
433 g.vis_mat = v; 437 g.vis_mat = v;
434 } else if (strcmp(key, "vis_r") == 0 || strcmp(key, "vis_g") == 0 || 438 } else if (strcmp(key, "vis_r") == 0 || strcmp(key, "vis_g") == 0 ||
435 strcmp(key, "vis_b") == 0) { 439 strcmp(key, "vis_b") == 0 || strcmp(key, "vis_a") == 0) {
436 int v = atoi(val); 440 int v = atoi(val);
437 if (v < 0) v = 0; 441 if (v < 0) v = 0;
438 if (v > 255) v = 255; 442 if (v > 255) v = 255;
439 g.vis_rgb[key[4] == 'r' ? 0 : key[4] == 'g' ? 1 : 2] = v; 443 int idx = key[4] == 'r' ? 0 : key[4] == 'g' ? 1
444 : key[4] == 'b' ? 2 : 3;
445 g.vis_rgb[idx] = v;
440 } else if (strcmp(key, "inv") == 0) 446 } else if (strcmp(key, "inv") == 0)
441 g.inv_on = atoi(val) != 0; 447 g.inv_on = atoi(val) != 0;
442 else if (strcmp(key, "inv_mat") == 0) { 448 else if (strcmp(key, "inv_mat") == 0) {
@@ -445,11 +451,13 @@ static bool config_load() {
445 if (v > 6) v = 6; 451 if (v > 6) v = 6;
446 g.inv_mat = v; 452 g.inv_mat = v;
447 } else if (strcmp(key, "inv_r") == 0 || strcmp(key, "inv_g") == 0 || 453 } else if (strcmp(key, "inv_r") == 0 || strcmp(key, "inv_g") == 0 ||
448 strcmp(key, "inv_b") == 0) { 454 strcmp(key, "inv_b") == 0 || strcmp(key, "inv_a") == 0) {
449 int v = atoi(val); 455 int v = atoi(val);
450 if (v < 0) v = 0; 456 if (v < 0) v = 0;
451 if (v > 255) v = 255; 457 if (v > 255) v = 255;
452 g.inv_rgb[key[4] == 'r' ? 0 : key[4] == 'g' ? 1 : 2] = v; 458 int idx = key[4] == 'r' ? 0 : key[4] == 'g' ? 1
459 : key[4] == 'b' ? 2 : 3;
460 g.inv_rgb[idx] = v;
453 } 461 }
454 } 462 }
455 if (in_chams_vm) { 463 if (in_chams_vm) {
@@ -461,11 +469,13 @@ static bool config_load() {
461 if (v > 6) v = 6; 469 if (v > 6) v = 6;
462 g_chams_vm_mat = v; 470 g_chams_vm_mat = v;
463 } else if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 || 471 } else if (strcmp(key, "r") == 0 || strcmp(key, "g") == 0 ||
464 strcmp(key, "b") == 0) { 472 strcmp(key, "b") == 0 || strcmp(key, "a") == 0) {
465 int v = atoi(val); 473 int v = atoi(val);
466 if (v < 0) v = 0; 474 if (v < 0) v = 0;
467 if (v > 255) v = 255; 475 if (v > 255) v = 255;
468 g_chams_vm_rgb[key[0] == 'r' ? 0 : key[0] == 'g' ? 1 : 2] = v; 476 int idx = key[0] == 'r' ? 0 : key[0] == 'g' ? 1
477 : key[0] == 'b' ? 2 : 3;
478 g_chams_vm_rgb[idx] = v;
469 } 479 }
470 } 480 }
471 if (in_aim) { 481 if (in_aim) {
@@ -693,14 +703,18 @@ void config_reset_defaults() {
693 g_proj_fuse = true; 703 g_proj_fuse = true;
694 g_proj_radius = true; 704 g_proj_radius = true;
695 g_chams = {false, 0}; 705 g_chams = {false, 0};
696 g_chams_hostile = {true, 3, {255, 60, 60}, true, 3, {255, 60, 60}}; 706 g_chams_hostile = {true, 3, {255, 60, 60, 255}, true, 3,
697 g_chams_neutral = {true, 3, {220, 220, 220}, false, 3, {220, 220, 220}}; 707 {255, 60, 60, 255}};
698 g_chams_friendly = {true, 3, {60, 255, 60}, false, 3, {60, 255, 60}}; 708 g_chams_neutral = {true, 3, {220, 220, 220, 255}, false, 3,
709 {220, 220, 220, 255}};
710 g_chams_friendly = {true, 3, {60, 255, 60, 255}, false, 3,
711 {60, 255, 60, 255}};
699 g_chams_viewmodel = false; 712 g_chams_viewmodel = false;
700 g_chams_vm_mat = 3; 713 g_chams_vm_mat = 3;
701 g_chams_vm_rgb[0] = 200; 714 g_chams_vm_rgb[0] = 200;
702 g_chams_vm_rgb[1] = 200; 715 g_chams_vm_rgb[1] = 200;
703 g_chams_vm_rgb[2] = 255; 716 g_chams_vm_rgb[2] = 255;
717 g_chams_vm_rgb[3] = 255;
704 g_item_health = true; 718 g_item_health = true;
705 g_item_ammo = true; 719 g_item_ammo = true;
706 g_item_armor = true; 720 g_item_armor = true;
diff --git a/src/studio.cpp b/src/studio.cpp
index bee6db5..07ebec3 100644
--- a/src/studio.cpp
+++ b/src/studio.cpp
@@ -248,6 +248,15 @@ void chams_color(void *mat, float r, float g, float b) {
248 vfunc<ColorFn>(mat, 28)(mat, r, g, b); 248 vfunc<ColorFn>(mat, 28)(mat, r, g, b);
249} 249}
250 250
251void chams_alpha(void *mat, float a) {
252 using FindVarFn = void *(*)(void *, const char *, bool *, bool);
253 using SetFloatFn = void (*)(void *, float);
254 bool found = false;
255 void *var = vfunc<FindVarFn>(mat, 11)(mat, "$alpha", &found, false);
256 if (var)
257 vfunc<SetFloatFn>(var, 3)(var, a);
258}
259
251void chams_apply(void *mat, bool ignorez, int style, bool force_white, 260void chams_apply(void *mat, bool ignorez, int style, bool force_white,
252 const int *rgb, bool znearer) { 261 const int *rgb, bool znearer) {
253 using FlagFn = void (*)(void *, int, bool); 262 using FlagFn = void (*)(void *, int, bool);
@@ -255,10 +264,12 @@ void chams_apply(void *mat, bool ignorez, int style, bool force_white,
255 float g = force_white ? 1.0f : (float)rgb[1] / 255.0f; 264 float g = force_white ? 1.0f : (float)rgb[1] / 255.0f;
256 float b = force_white ? 1.0f : (float)rgb[2] / 255.0f; 265 float b = force_white ? 1.0f : (float)rgb[2] / 255.0f;
257 chams_color(mat, r, g, b); 266 chams_color(mat, r, g, b);
267 chams_alpha(mat, (float)rgb[3] / 255.0f);
258 // 0 Flat, 1 Wireframe, 2 Additive, 3 Solid, 4 White, 5 Fullbright 268 // 0 Flat, 1 Wireframe, 2 Additive, 3 Solid, 4 White, 5 Fullbright
259 // (additive white glow), 6 Glow outline (solid base; the wireframe overlay 269 // (additive white glow), 6 Glow outline (solid base; the wireframe overlay
260 // is a second draw with ZNEARER so its contour is not hidden by the base). 270 // is a second draw with ZNEARER so its contour is not hidden by the base).
261 vfunc<FlagFn>(mat, 29)(mat, 1 << 15 /* IGNOREZ */, ignorez); 271 vfunc<FlagFn>(mat, 29)(mat, 1 << 15 /* IGNOREZ */, ignorez);
272 vfunc<FlagFn>(mat, 29)(mat, 1 << 21 /* TRANSLUCENT */, rgb[3] < 255);
262 vfunc<FlagFn>(mat, 29)(mat, 1 << 10 /* ZNEARER */, znearer); 273 vfunc<FlagFn>(mat, 29)(mat, 1 << 10 /* ZNEARER */, znearer);
263 vfunc<FlagFn>(mat, 29)(mat, 1 << 28 /* WIREFRAME */, style == 1); 274 vfunc<FlagFn>(mat, 29)(mat, 1 << 28 /* WIREFRAME */, style == 1);
264 vfunc<FlagFn>(mat, 29)(mat, 1 << 7 /* ADDITIVE */, 275 vfunc<FlagFn>(mat, 29)(mat, 1 << 7 /* ADDITIVE */,
@@ -270,12 +281,14 @@ void chams_apply(void *mat, bool ignorez, int style, bool force_white,
270void chams_reset(void *mat) { 281void chams_reset(void *mat) {
271 using FlagFn = void (*)(void *, int, bool); 282 using FlagFn = void (*)(void *, int, bool);
272 vfunc<FlagFn>(mat, 29)(mat, 1 << 15, false); 283 vfunc<FlagFn>(mat, 29)(mat, 1 << 15, false);
284 vfunc<FlagFn>(mat, 29)(mat, 1 << 21, false);
273 vfunc<FlagFn>(mat, 29)(mat, 1 << 10, false); 285 vfunc<FlagFn>(mat, 29)(mat, 1 << 10, false);
274 vfunc<FlagFn>(mat, 29)(mat, 1 << 28, false); 286 vfunc<FlagFn>(mat, 29)(mat, 1 << 28, false);
275 vfunc<FlagFn>(mat, 29)(mat, 1 << 7, false); 287 vfunc<FlagFn>(mat, 29)(mat, 1 << 7, false);
276 vfunc<FlagFn>(mat, 29)(mat, 1 << 12, false); 288 vfunc<FlagFn>(mat, 29)(mat, 1 << 12, false);
277 vfunc<FlagFn>(mat, 29)(mat, 1 << 6, false); 289 vfunc<FlagFn>(mat, 29)(mat, 1 << 6, false);
278 chams_color(mat, 1.0f, 1.0f, 1.0f); 290 chams_color(mat, 1.0f, 1.0f, 1.0f);
291 chams_alpha(mat, 1.0f);
279} 292}
280 293
281void hkdModelExecute(void *self, const void *state, const void *info, 294void hkdModelExecute(void *self, const void *state, const void *info,
@@ -306,20 +319,24 @@ void hkdModelExecute(void *self, const void *state, const void *info,
306 // Fullbright is an additive, self-illuminated version of the 319 // Fullbright is an additive, self-illuminated version of the
307 // chosen colour: lift it toward white so it reads brighter than 320 // chosen colour: lift it toward white so it reads brighter than
308 // the plain additive style. 321 // the plain additive style.
309 int brgb[3]; 322 int brgb[4];
310 const int *use_rgb = rgb; 323 const int *use_rgb = rgb;
311 if (style == 5) { 324 if (style == 5) {
312 for (int i = 0; i < 3; i++) 325 for (int i = 0; i < 3; i++)
313 brgb[i] = rgb[i] + (255 - rgb[i]) / 2; 326 brgb[i] = rgb[i] + (255 - rgb[i]) / 4;
327 brgb[3] = rgb[3];
314 use_rgb = brgb; 328 use_rgb = brgb;
315 } 329 }
316 // Additive blending vanishes against bright backgrounds (sky, 330 // Additive blending vanishes against bright backgrounds (sky,
317 // white walls, strong fog), which made the viewmodel disappear in 331 // white walls, strong fog), which made the viewmodel disappear in
318 // some places. Lay down a dim opaque base first so the first-person 332 // some places. Lay down a faint translucent base so the first-person
319 // model always has a visible silhouette; the additive pass still 333 // model keeps a silhouette on bright backgrounds without turning
320 // adds the glow on top. 334 // into an opaque blob; the additive pass still provides the glow.
321 if (is_vm && (style == 2 || style == 5)) { 335 if (is_vm && (style == 2 || style == 5) && rgb[3] > 0) {
322 int drgb[3] = {rgb[0] / 2, rgb[1] / 2, rgb[2] / 2}; 336 int ba = rgb[3] / 8;
337 if (ba < 16)
338 ba = 16;
339 int drgb[4] = {rgb[0], rgb[1], rgb[2], ba};
323 chams_apply(mat, ignorez, 3, false, drgb, false); 340 chams_apply(mat, ignorez, 3, false, drgb, false);
324 g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */); 341 g_force_override(self, mat, 0 /* OVERRIDE_NORMAL */);
325 g_orig_model_exec(self, state, info, bone); 342 g_orig_model_exec(self, state, info, bone);
@@ -333,8 +350,8 @@ void hkdModelExecute(void *self, const void *state, const void *info,
333 // the contour and bias it forward so the base cannot bury it. 350 // the contour and bias it forward so the base cannot bury it.
334 void *wire = g_style_mats[1]; 351 void *wire = g_style_mats[1];
335 if (wire && wire != mat) { 352 if (wire && wire != mat) {
336 int wrgb[3] = {(rgb[0] + 255) / 2, (rgb[1] + 255) / 2, 353 int wrgb[4] = {(rgb[0] + 255) / 2, (rgb[1] + 255) / 2,
337 (rgb[2] + 255) / 2}; 354 (rgb[2] + 255) / 2, rgb[3]};
338 chams_apply(wire, ignorez, 1, false, wrgb, true); 355 chams_apply(wire, ignorez, 1, false, wrgb, true);
339 g_force_override(self, wire, 0); 356 g_force_override(self, wire, 0);
340 g_orig_model_exec(self, state, info, bone); 357 g_orig_model_exec(self, state, info, bone);