From 506f095bcb528468f25a637977efcc408590ae67 Mon Sep 17 00:00:00 2001 From: Michael Smith Date: Sun, 21 Jan 2024 21:01:00 +0000 Subject: Add a half-decent custom crosshair --- compile | 2 ++ compile.bat | 2 ++ src/hexcolour.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/hexcolour.h | 36 ++++++++++++++++++++++ src/portalcolours.c | 47 ++++++---------------------- src/xhair.c | 88 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 222 insertions(+), 38 deletions(-) create mode 100644 src/hexcolour.c create mode 100644 src/hexcolour.h create mode 100644 src/xhair.c diff --git a/compile b/compile index abdd5a7..bcda4ec 100755 --- a/compile +++ b/compile @@ -70,6 +70,7 @@ src="\ gamedata.c gameinfo.c gameserver.c + hexcolour.c hook.c hud.c kvsys.c @@ -79,6 +80,7 @@ src="\ nosleep.c portalcolours.c sst.c + xhair.c x86.c" if [ "$dbg" = 1 ]; then src="$src \ dbg.c diff --git a/compile.bat b/compile.bat index 97bbb6f..a50dbb0 100644 --- a/compile.bat +++ b/compile.bat @@ -77,6 +77,7 @@ setlocal DisableDelayedExpansion :+ gamedata.c :+ gameinfo.c :+ gameserver.c +:+ hexcolour.c :+ hook.c :+ hud.c :+ kvsys.c @@ -88,6 +89,7 @@ setlocal DisableDelayedExpansion :+ portalcolours.c :+ rinput.c :+ sst.c +:+ xhair.c :+ x86.c :: just tack these on, whatever (repeated condition because of expansion memes) if "%dbg%"=="1" set src=%src% src/dbg.c diff --git a/src/hexcolour.c b/src/hexcolour.c new file mode 100644 index 0000000..7d0ad43 --- /dev/null +++ b/src/hexcolour.c @@ -0,0 +1,85 @@ +/* + * Copyright © 2024 Michael Smith + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "intdefs.h" + +void hexcolour_rgb(uchar out[static 4], const char *s) { + const char *p = s; + for (uchar *q = out; q - out < 3; ++q) { + if (*p >= '0' && *p <= '9') { + *q = *p++ - '0' << 4; + } + else if ((*p | 32) >= 'a' && (*p | 32) <= 'f') { + *q = 10 + (*p++ | 32) - 'a' << 4; + } + else { + // screw it, just fall back on white, I guess. + // note: this also handles *p == '\0' so we don't overrun the string + memset(out, 255, 4); // should be a single mov + return; + } + // repetitive unrolled nonsense + if (*p >= '0' && *p <= '9') { + *q |= *p++ - '0'; + } + else if ((*p | 32) >= 'a' && (*p | 32) <= 'f') { + *q |= 10 + (*p++ | 32) - 'a'; + } + else { + memset(out, 255, 4); // should be a single mov + return; + } + } + //out[3] = 255; // never changes! +} + +void hexcolour_rgba(uchar out[static 4], const char *s) { + const char *p = s; + // same again but with 4 pairs of digits instead of 3! + for (uchar *q = out; q - out < 4; ++q) { + if (*p >= '0' && *p <= '9') { + *q = *p++ - '0' << 4; + } + else if ((*p | 32) >= 'a' && (*p | 32) <= 'f') { + *q = 10 + (*p++ | 32) - 'a' << 4; + } + else { + if (q - out == 3 && !*p) { + // ONLY if both the last alpha digits are missing: use provided + // RGB at full opacity. otherwise same white fallback as before. + out[3] = 255; + return; + } + memset(out, 255, 4); + return; + } + // even more repetitive unrolled nonsense + if (*p >= '0' && *p <= '9') { + *q |= *p++ - '0'; + } + else if ((*p | 32) >= 'a' && (*p | 32) <= 'f') { + *q |= 10 + (*p++ | 32) - 'a'; + } + else { + memset(out, 255, 4); + return; + } + } +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/hexcolour.h b/src/hexcolour.h new file mode 100644 index 0000000..def95f4 --- /dev/null +++ b/src/hexcolour.h @@ -0,0 +1,36 @@ +/* + * Copyright © 2024 Michael Smith + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include "intdefs.h" + +/* + * Parses a user-provided RGB hex string, writing the RGBA bytes to out. May or + * may not modify the alpha byte; it is assumed to always be set to 255. That + * probably sounds dumb but makes sense for our specific use cases. + * + * Falls back on white if the input isn't valid. This also makes sense for our + * use cases. + */ +void hexcolour_rgb(uchar out[static 4], const char *s); + +/* + * Parses a user-provided RGBA hex string, writing the RGBA bytes to out. + * If both the alpha digits are missing, provides full opacity instead. If the + * value is malformed in some other way, falls back on solid white. + */ +void hexcolour_rgba(uchar out[static 4], const char *s); + +// vi: sw=4 ts=4 noet tw=80 cc=80 diff --git a/src/portalcolours.c b/src/portalcolours.c index 0eb3a30..3167c0b 100644 --- a/src/portalcolours.c +++ b/src/portalcolours.c @@ -1,5 +1,5 @@ /* - * Copyright © 2023 Michael Smith + * Copyright © 2024 Michael Smith * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -19,8 +19,9 @@ #include "con_.h" #include "engineapi.h" #include "errmsg.h" -#include "gametype.h" #include "feature.h" +#include "gametype.h" +#include "hexcolour.h" #include "hook.h" #include "intdefs.h" #include "mem.h" @@ -34,55 +35,25 @@ REQUIRE_GLOBAL(clientlib) // It's like the thing Portal Tools does, but at runtime! -DEF_CVAR_UNREG(sst_portal_colour0, "Crosshair colour for gravity beam (hex)", +DEF_CVAR_UNREG(sst_portal_colour0, "Crosshair colour for gravity beam (RGB hex)", "F2CAA7", CON_ARCHIVE | CON_HIDDEN) -DEF_CVAR_UNREG(sst_portal_colour1, "Crosshair colour for left portal (hex)", +DEF_CVAR_UNREG(sst_portal_colour1, "Crosshair colour for left portal (RGB hex)", "40A0FF", CON_ARCHIVE | CON_HIDDEN) -DEF_CVAR_UNREG(sst_portal_colour2, "Crosshair colour for right portal (hex)", +DEF_CVAR_UNREG(sst_portal_colour2, "Crosshair colour for right portal (RGB hex)", "FFA020", CON_ARCHIVE | CON_HIDDEN) static struct rgba colours[3] = { {242, 202, 167, 255}, {64, 160, 255, 255}, {255, 160, 32, 255}}; -static void hexparse(uchar out[static 4], const char *s) { - const char *p = s; - for (uchar *q = out; q - out < 3; ++q) { - if (*p >= '0' && *p <= '9') { - *q = *p++ - '0' << 4; - } - else if ((*p | 32) >= 'a' && (*p | 32) <= 'f') { - *q = 10 + (*p++ | 32) - 'a' << 4; - } - else { - // screw it, just fall back on white, I guess. - // note: this also handles *p == '\0' so we don't overrun the string - memset(out, 255, 4); // write 4 rather than 3, prolly faster? - return; - } - // repetitive unrolled nonsense - if (*p >= '0' && *p <= '9') { - *q |= *p++ - '0'; - } - else if ((*p | 32) >= 'a' && (*p | 32) <= 'f') { - *q |= 10 + (*p++ | 32) - 'a'; - } - else { - memset(out, 255, 4); - return; - } - } - //out[3] = 255; // never changes! -} - static void colourcb(struct con_var *v) { // this is stupid and ugly and has no friends, too bad! if (v == sst_portal_colour0) { - hexparse(colours[0].bytes, con_getvarstr(v)); + hexcolour_rgb(colours[0].bytes, con_getvarstr(v)); } else if (v == sst_portal_colour1) { - hexparse(colours[1].bytes, con_getvarstr(v)); + hexcolour_rgb(colours[1].bytes, con_getvarstr(v)); } else /* sst_portal_colour2 */ { - hexparse(colours[2].bytes, con_getvarstr(v)); + hexcolour_rgb(colours[2].bytes, con_getvarstr(v)); } } diff --git a/src/xhair.c b/src/xhair.c new file mode 100644 index 0000000..e2f7bfb --- /dev/null +++ b/src/xhair.c @@ -0,0 +1,88 @@ +/* + * Copyright © 2024 Michael Smith + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED “AS IS” AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH + * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, + * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ + +#include + +#include "con_.h" +#include "engineapi.h" +#include "feature.h" +#include "hexcolour.h" +#include "hud.h" +#include "intdefs.h" + +FEATURE("crosshair drawing") +REQUIRE(hud) + +DEF_CVAR(sst_xhair, "Enable custom crosshair", 0, CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR(sst_xhair_colour, "Colour for alternative crosshair (RGBA hex)", + "FFFFFF", CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR_MIN(sst_xhair_thickness, "Thickness of custom crosshair in pixels", 2, + 1, CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR_MIN(sst_xhair_size, "Length of lines in custom crosshair in pixels", 8, + 0, CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR_MIN(sst_xhair_gap, "Gap between lines in custom crosshair in pixels", + 16, 0, CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR(sst_xhair_dot, "Whether to draw dot in middle of custom crosshair", + 1, CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR(sst_xhair_outline, "Whether to draw outline around custom crosshair", + 0, CON_ARCHIVE | CON_HIDDEN) + +static struct rgba colour = {255, 255, 255, 255}; + +static void colourcb(struct con_var *v) { + hexcolour_rgba(colour.bytes, con_getvarstr(v)); +} + +static inline void drawrect(int x0, int y0, int x1, int y1, struct rgba colour, + bool outline) { + hud_drawrect(x0, y0, x1, y1, colour, true); + if (outline) hud_drawrect(x0, y0, x1, y1, (struct rgba){.a = 255}, false); +} +HANDLE_EVENT(HudPaint, void) { + if (!con_getvari(sst_xhair)) return; + int w, h; + hud_screensize(&w, &h); + int thick = con_getvari(sst_xhair_thickness); + int thick1 = (thick + 1) / 2, thick2 = thick - thick1; + int sz = con_getvari(sst_xhair_size); + int gap = con_getvari(sst_xhair_gap); + int gap1 = (gap + 1) / 2, gap2 = gap - gap1; + int x = w / 2, y = h / 2; + bool ol = !!con_getvari(sst_xhair_outline); + if (sz) { + drawrect(x - thick1, y - sz - gap1, x + thick2, y - gap1, colour, ol); + drawrect(x - thick1, y + gap2, x + thick2, y + sz + gap2, colour, ol); + drawrect(x - sz - gap1, y - thick1, x - gap1, y + thick2, colour, ol); + drawrect(x + gap2, y - thick1, x + sz + gap2, y + thick2, colour, ol); + } + if (con_getvari(sst_xhair_dot) && (gap >= thick || ol)) { + drawrect(x - thick1, y - thick1, x + thick2, y + thick2, colour, ol); + } +} + +INIT { + sst_xhair->base.flags &= ~CON_HIDDEN; + sst_xhair_colour->base.flags &= ~CON_HIDDEN; + sst_xhair_colour->cb = &colourcb; + sst_xhair_thickness->base.flags &= ~CON_HIDDEN; + sst_xhair_size->base.flags &= ~CON_HIDDEN; + sst_xhair_gap->base.flags &= ~CON_HIDDEN; + sst_xhair_dot->base.flags &= ~CON_HIDDEN; + sst_xhair_outline->base.flags &= ~CON_HIDDEN; + return true; +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 -- cgit v1.2.3