From 5f208d6820f42ab138a3c7144728787bd988004c Mon Sep 17 00:00:00 2001 From: Matthew Wozniak Date: Sat, 14 Sep 2024 22:45:35 +0100 Subject: Add basic button input HUD Committer's note: this is heavily modified from the original code contributed by woz (and somewhat improved by bill and aciidz). Copyright notices reflect joint authorship accordingly. woz still gets commit authorship though because it feels wrong to yoink that from someone. :^) And yes, the original code was written in 2022. Time flies. A lot of the code is still kind of hacky and ugly and I'd like to improve it later but there's other things to do so that can wait. --- src/autojump.c | 1 - src/engineapi.c | 13 +- src/engineapi.h | 27 ++++ src/gametype.h | 21 +-- src/inputhud.c | 443 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 490 insertions(+), 15 deletions(-) create mode 100644 src/inputhud.c (limited to 'src') diff --git a/src/autojump.c b/src/autojump.c index cc44573..64ed436 100644 --- a/src/autojump.c +++ b/src/autojump.c @@ -35,7 +35,6 @@ REQUIRE_GLOBAL(factory_client) // note: server will never be null DEF_CVAR(sst_autojump, "Jump upon hitting the ground while holding space", 0, CON_REPLICATE | CON_DEMO | CON_HIDDEN) -#define IN_JUMP 2 #define NIDX 256 // *completely* arbitrary lol static bool justjumped[NIDX] = {0}; static inline int handleidx(ulong h) { return h & (1 << 11) - 1; } diff --git a/src/engineapi.c b/src/engineapi.c index 785245d..5a78a92 100644 --- a/src/engineapi.c +++ b/src/engineapi.c @@ -90,10 +90,15 @@ bool engineapi_init(int pluginver) { } // detect p1 for the benefit of specific features - if (!GAMETYPE_MATCHES(Portal2) && con_findcmd("upgrade_portalgun")) { - _gametype_tag |= _gametype_tag_Portal1; - if (!con_findvar("tf_escort_score_rate")) { - _gametype_tag |= _gametype_tag_Portal1_3420; + if (!GAMETYPE_MATCHES(Portal2)) { + if (con_findcmd("upgrade_portalgun")) { + _gametype_tag |= _gametype_tag_Portal1; + if (!con_findvar("tf_escort_score_rate")) { + _gametype_tag |= _gametype_tag_Portal1_3420; + } + } + else if (con_findcmd("phys_swap")) { + _gametype_tag |= _gametype_tag_HL2series; } } diff --git a/src/engineapi.h b/src/engineapi.h index 4f96b73..308e34e 100644 --- a/src/engineapi.h +++ b/src/engineapi.h @@ -154,6 +154,33 @@ struct CServerPlugin /* : IServerPluginHelpers */ { }; extern struct CServerPlugin *pluginhandler; +// input button bits +#define IN_ATTACK (1 << 0) +#define IN_JUMP (1 << 1) +#define IN_DUCK (1 << 2) +#define IN_FORWARD (1 << 3) +#define IN_BACK (1 << 4) +#define IN_USE (1 << 5) +#define IN_CANCEL (1 << 6) +#define IN_LEFT (1 << 7) +#define IN_RIGHT (1 << 8) +#define IN_MOVELEFT (1 << 9) +#define IN_MOVERIGHT (1 << 10) +#define IN_ATTACK2 (1 << 11) +#define IN_RUN (1 << 12) +#define IN_RELOAD (1 << 13) +#define IN_ALT1 (1 << 14) +#define IN_ALT2 (1 << 15) +#define IN_SCORE (1 << 16) +#define IN_SPEED (1 << 17) +#define IN_WALK (1 << 18) +#define IN_ZOOM (1 << 19) +#define IN_WEAPON1 (1 << 20) +#define IN_WEAPON2 (1 << 21) +#define IN_BULLRUSH (1 << 22) +#define IN_GRENADE1 (1 << 23) +#define IN_GRENADE2 (1 << 24) + /* * Called on plugin init to attempt to initialise various core interfaces. * This includes console/cvar initialisation and populating gametype and diff --git a/src/gametype.h b/src/gametype.h index 81b860a..35a43be 100644 --- a/src/gametype.h +++ b/src/gametype.h @@ -38,22 +38,23 @@ extern u64 _gametype_tag; /* games needing game-specific stuff, but not tied to a singular branch */ #define _gametype_tag_Portal1 (1 << 8) +#define _gametype_tag_HL2series (1 << 9) /* HL2, episodes, and mods */ /* VEngineClient versions */ -#define _gametype_tag_Client015 (1 << 9) -#define _gametype_tag_Client014 (1 << 10) -#define _gametype_tag_Client013 (1 << 11) -#define _gametype_tag_Client012 (1 << 12) -#define _gametype_tag_Server021 (1 << 13) +#define _gametype_tag_Client015 (1 << 10) +#define _gametype_tag_Client014 (1 << 11) +#define _gametype_tag_Client013 (1 << 12) +#define _gametype_tag_Client012 (1 << 13) +#define _gametype_tag_Server021 (1 << 14) /* ServerGameDLL versions */ -#define _gametype_tag_SrvDLL009 (1 << 14) // 2013-ish -#define _gametype_tag_SrvDLL005 (1 << 15) // mostly everything else, it seems +#define _gametype_tag_SrvDLL009 (1 << 15) // 2013-ish +#define _gametype_tag_SrvDLL005 (1 << 16) // mostly everything else, it seems /* games needing version-specific stuff */ -#define _gametype_tag_Portal1_3420 (1 << 16) -#define _gametype_tag_L4D2_2147plus (1 << 17) -#define _gametype_tag_TheLastStand (1 << 18) /* The JAiZ update */ +#define _gametype_tag_Portal1_3420 (1 << 17) +#define _gametype_tag_L4D2_2147plus (1 << 18) +#define _gametype_tag_TheLastStand (1 << 19) /* The JAiZ update */ /* Matches for any multiple possible tags */ #define _gametype_tag_L4D (_gametype_tag_L4D1 | _gametype_tag_L4D2) diff --git a/src/inputhud.c b/src/inputhud.c new file mode 100644 index 0000000..c4f7342 --- /dev/null +++ b/src/inputhud.c @@ -0,0 +1,443 @@ +/* + * Copyright © 2022 Matthew Wozniak + * Copyright © 2022 Willian Henrique + * Copyright © 2024 Michael Smith + +#include "con_.h" +#include "engineapi.h" +#include "event.h" +#include "errmsg.h" +#include "gamedata.h" +#include "gametype.h" +#include "hexcolour.h" +#include "hook.h" +#include "hud.h" +#include "intdefs.h" +#include "feature.h" +#include "mem.h" +#include "vcall.h" +#include "x86.h" +#include "x86util.h" + +FEATURE("button input HUD") +REQUIRE_GAMEDATA(vtidx_CreateMove) +REQUIRE_GAMEDATA(vtidx_DecodeUserCmdFromBuffer) +REQUIRE_GAMEDATA(vtidx_GetUserCmd) +REQUIRE_GAMEDATA(vtidx_VClient_DecodeUserCmdFromBuffer) +REQUIRE_GLOBAL(factory_client) +REQUIRE(hud) + +DEF_CVAR(sst_inputhud, "Enable button input HUD", 0, CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR(sst_inputhud_bgcolour_normal, + "Input HUD default key background colour (RGBA hex)", "4040408C", + CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR(sst_inputhud_bgcolour_pressed, + "Input HUD pressed key background colour (RGBA hex)", "202020C8", + CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR(sst_inputhud_fgcolour, "Input HUD text colour (RGBA hex)", "F0F0F0FF", + CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR_MINMAX(sst_inputhud_scale, "Input HUD size (multiple of minimum)", + 1.5, 1, 4, CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR_MINMAX(sst_inputhud_x, + "Input HUD x position (fraction between screen left and right)", + 0.02, 0, 1, CON_ARCHIVE | CON_HIDDEN) +DEF_CVAR_MINMAX(sst_inputhud_y, + "Input HUD y position (fraction between screen top and bottom)", + 0.95, 0, 1, CON_ARCHIVE | CON_HIDDEN) + +static void *input; +static int heldbuttons = 0, tappedbuttons = 0; + +static struct rgba colours[3] = { + {64, 64, 64, 140}, + {16, 16, 16, 200}, + {240, 240, 240, 255} +}; + +static void colourcb(struct con_var *v) { + if (v == sst_inputhud_bgcolour_normal) { + hexcolour_rgba(colours[0].bytes, con_getvarstr(v)); + } + else if (v == sst_inputhud_bgcolour_pressed) { + hexcolour_rgba(colours[1].bytes, con_getvarstr(v)); + } + else /* v == sst_inputhud_fg */ { + hexcolour_rgba(colours[2].bytes, con_getvarstr(v)); + } +} + +struct CUserCmd { + void **vtable; + int cmd, tick; + struct vec3f angles; + float fmove, smove, umove; + int buttons; + char impulse; + int weaponselect, weaponsubtype; + int rngseed; + short mousedx, mousedy; + // client only: + bool predicted; + struct CUtlVector *entgroundcontact; +}; + +#define vtidx_GetUserCmd_l4dbased vtidx_GetUserCmd +DECL_VFUNC_DYN(struct CUserCmd *, GetUserCmd, int) +DECL_VFUNC_DYN(struct CUserCmd *, GetUserCmd_l4dbased, int, int) + +typedef void (*VCALLCONV CreateMove_func)(void *, int, float, bool); +static CreateMove_func orig_CreateMove; +static void VCALLCONV hook_CreateMove(void *this, int seq, float ft, + bool active) { + orig_CreateMove(this, seq, ft, active); + struct CUserCmd *cmd = GetUserCmd(this, seq); + // trick: to ensure every input (including scroll wheel) is displayed for at + // least a frame, even at sub-tickrate framerates, we accumulate tapped + // buttons with bitwise or. once these are drawn, tappedbuttons is cleared, + // but heldbuttons maintains its state, so stuff doesn't flicker constantly + if (cmd) { heldbuttons = cmd->buttons; tappedbuttons |= cmd->buttons; } +} +// basically a dupe, but calling the other version of GetUserCmd +static void VCALLCONV hook_CreateMove_l4dbased(void *this, int seq, float ft, + bool active) { + orig_CreateMove(this, seq, ft, active); + struct CUserCmd *cmd = GetUserCmd_l4dbased(this, -1, seq); + if (cmd) { heldbuttons = cmd->buttons; tappedbuttons |= cmd->buttons; } +} + +typedef void (*VCALLCONV DecodeUserCmdFromBuffer_func)(void *, void *, int); +typedef void (*VCALLCONV DecodeUserCmdFromBuffer_l4dbased_func)(void *, int, + void *, int); +static union { + DecodeUserCmdFromBuffer_func prel4d; + DecodeUserCmdFromBuffer_l4dbased_func l4dbased; +} _orig_DecodeUserCmdFromBuffer; +#define orig_DecodeUserCmdFromBuffer _orig_DecodeUserCmdFromBuffer.prel4d +#define orig_DecodeUserCmdFromBuffer_l4dbased \ + _orig_DecodeUserCmdFromBuffer.l4dbased +static void VCALLCONV hook_DecodeUserCmdFromBuffer(void *this, void *reader, + int seq) { + orig_DecodeUserCmdFromBuffer(this, reader, seq); + struct CUserCmd *cmd = GetUserCmd(this, seq); + if (cmd) { heldbuttons = cmd->buttons; tappedbuttons |= cmd->buttons; } +} +static void VCALLCONV hook_DecodeUserCmdFromBuffer_l4dbased(void *this, + int slot, void *reader, int seq) { + orig_DecodeUserCmdFromBuffer_l4dbased(this, slot, reader, seq); + struct CUserCmd *cmd = GetUserCmd_l4dbased(this, slot, seq); + if (cmd) { heldbuttons = cmd->buttons; tappedbuttons |= cmd->buttons; } +} + +static inline int bsf(uint x) { + // this should generate xor , ; bsfl , . + // doing a straight bsf (e.g. via BitScanForward or __builtin_ctz) creates + // a false dependency on many CPUs, which compilers don't understand somehow + int ret = 0; +#if defined(__GNUC__) || defined(__clang__) + __asm__ volatile ( + "bsfl %1, %0\n" + : "+r" (ret) + : "r" (x) + ); + return ret; +#else +#error need some sort of inline asm, or a non-broken(!) bitscan intrinsic +#endif +} + +// IMPORTANT: these things must all match the button order in engineapi.h +static const struct { + hud_wchar *s; + int len; +} text[] = { + /* IN_ATTACK */ {L"Pri", 3}, + /* IN_JUMP */ {L"Jump", 4}, + /* IN_DUCK */ {L"Duck", 4}, + /* IN_FORWARD */ {L"Fwd", 3}, + /* IN_BACK */ {L"Back", 4}, + /* IN_USE */ {L"Use", 3}, + /* IN_CANCEL */ {0}, + /* IN_LEFT */ {L"LTurn", 5}, + /* IN_RIGHT */ {L"RTurn", 5}, + /* IN_MOVELEFT */ {L"Left", 4}, + /* IN_MOVERIGHT */ {L"Right", 5}, + /* IN_ATTACK2 */ {L"Sec", 3}, + /* IN_RUN */ {0}, + /* IN_RELOAD */ {L"Rld", 3}, + /* IN_ALT1 */ {0}, + /* IN_ALT2 */ {0}, + /* IN_SCORE */ {0}, + /* IN_SPEED */ {L"Speed", 5}, + /* IN_WALK */ {L"Walk", 4}, + /* IN_ZOOM */ {L"Zoom", 4} + // ignoring the rest +}; + +struct layout { + int mask; + schar w, h; + struct { schar x, y; } pos[20]; // XXX: should make flexible??? +}; + +// input layouts (since some games don't use all input bits) {{{ + +static const struct layout layout_hl2 = { + IN_ATTACK | IN_JUMP | IN_DUCK | IN_FORWARD | IN_BACK | IN_USE | IN_LEFT | + IN_RIGHT | IN_MOVELEFT | IN_MOVERIGHT | IN_ATTACK2 | IN_RELOAD | IN_SPEED | + IN_WALK | IN_ZOOM, + 15, 6, + { + // F 1 2 + // L B R U R Z + // W S D J l r + /* IN_ATTACK */ {10, 0}, /* IN_JUMP */ { 6, 4}, + /* IN_DUCK */ { 4, 4}, /* IN_FORWARD */ { 3, 0}, + /* IN_BACK */ { 3, 2}, /* IN_USE */ { 9, 2}, + /* IN_CANCEL */ {0}, /* IN_LEFT */ {10, 4}, + /* IN_RIGHT */ {12, 4}, /* IN_MOVELEFT */ { 1, 2}, + /* IN_MOVERIGHT */ { 5, 2}, /* IN_ATTACK2 */ {12, 0}, + /* IN_RUN */ {0}, /* IN_RELOAD */ {11, 2}, + /* IN_ALT1 */ {0}, /* IN_ALT2 */ {0}, + /* IN_SCORE */ {0}, /* IN_SPEED */ { 2, 4}, + /* IN_WALK */ { 0, 4}, /* IN_ZOOM */ {13, 2} + } +}; + +static const struct layout layout_portal1 = { + IN_ATTACK | IN_JUMP | IN_DUCK | IN_FORWARD | IN_BACK | IN_USE | IN_LEFT | + IN_RIGHT | IN_MOVELEFT | IN_MOVERIGHT | IN_ATTACK2, + 11, 6, + { + // F 1 2 + // L B R U + // D J l r + /* IN_ATTACK */ {7, 0}, /* IN_JUMP */ {3, 4}, + /* IN_DUCK */ {1, 4}, /* IN_FORWARD */ {2, 0}, + /* IN_BACK */ {2, 2}, /* IN_USE */ {8, 2}, + /* IN_CANCEL */ {0}, /* IN_LEFT */ {7, 4}, + /* IN_RIGHT */ {9, 4}, /* IN_MOVELEFT */ {0, 2}, + /* IN_MOVERIGHT */ {4, 2}, /* IN_ATTACK2 */ {9, 0} + } +}; + +// TODO(compat): add portal2 layout once there's hud gamedata for portal 2 +//static const struct layout layout_portal2 = { +// IN_ATTACK | IN_JUMP | IN_DUCK | IN_FORWARD | IN_BACK | IN_USE | IN_LEFT | +// IN_RIGHT | IN_MOVELEFT | IN_MOVERIGHT | IN_ATTACK2 | IN_ZOOM, +// 11, 6, +// { +// // F 1 2 +// // L B R U Z +// // D J l r +// /* IN_ATTACK */ {7, 0}, /* IN_JUMP */ {3, 4}, +// /* IN_DUCK */ {1, 4}, /* IN_FORWARD */ {2, 0}, +// /* IN_BACK */ {2, 2}, /* IN_USE */ {7, 2}, +// /* IN_CANCEL */ {0}, /* IN_LEFT */ {7, 4}, +// /* IN_RIGHT */ {9, 4}, /* IN_MOVELEFT */ {0, 2}, +// /* IN_MOVERIGHT */ {4, 2}, /* IN_ATTACK2 */ {9, 0}, +// /* IN_RUN */ {0}, /* IN_RELOAD */ {0}, +// /* IN_ALT1 */ {0}, /* IN_ALT2 */ {0}, +// /* IN_SCORE */ {0}, /* IN_SPEED */ {0}, +// /* IN_WALK */ {0}, /* IN_ZOOM */ {9, 2} +// } +//}; + +static const struct layout layout_l4d = { + IN_ATTACK | IN_JUMP | IN_DUCK | IN_FORWARD | IN_BACK | IN_USE | IN_LEFT | + IN_RIGHT | IN_MOVELEFT | IN_MOVERIGHT | IN_ATTACK2 | IN_SPEED | IN_ZOOM, + 11, 6, + { + // F 1 2 + // L B R U Z + // W D J l r + /* IN_ATTACK */ {7, 0}, /* IN_JUMP */ {4, 4}, + /* IN_DUCK */ {2, 4}, /* IN_FORWARD */ {2, 0}, + /* IN_BACK */ {2, 2}, /* IN_USE */ {7, 2}, + /* IN_CANCEL */ {0}, /* IN_LEFT */ {7, 4}, + /* IN_RIGHT */ {9, 4}, /* IN_MOVELEFT */ {0, 2}, + /* IN_MOVERIGHT */ {4, 2}, /* IN_ATTACK2 */ {9, 0}, + /* IN_RUN */ {0}, /* IN_RELOAD */ {0}, + /* IN_ALT1 */ {0}, /* IN_ALT2 */ {0}, + /* IN_SCORE */ {0}, /* IN_SPEED */ {0, 4}, + /* IN_WALK */ {0}, /* IN_ZOOM */ {9, 2} + } +}; + +// }}} + +static const struct layout *layout = &layout_hl2; + +static const char *const fontnames[] = { + "DebugFixedSmall", + "HudSelectionText", + "CommentaryDefault", + "DefaultVerySmall", + "DefaultSmall", + "Default" +}; +static struct { ulong h; int sz; } fonts[countof(fontnames)]; + +HANDLE_EVENT(HudPaint, void) { + if (!con_getvari(sst_inputhud)) return; + int screenw, screenh; + hud_screensize(&screenw, &screenh); + int basesz = screenw > screenh ? screenw : screenh; + int boxsz = ceilf(basesz * 0.025f); + if (boxsz < 24) boxsz = 24; + boxsz *= con_getvarf(sst_inputhud_scale); + int idealfontsz = boxsz - 8; // NOTE: this is overall text width, see INIT + ulong font = 0; int fontsz = 0; + // get the biggest font that'll fit the box + // XXX: can/should we avoid doing this every frame? + for (int i = 0; i < countof(fonts); ++i) { + // XXX: fonts aren't sorted... should we bother? + if_cold (!fonts[i].h) continue; + if (fonts[i].sz < fontsz) continue; + if (fonts[i].sz <= idealfontsz) font = fonts[i].h; + //else break; // not sorted + } + int gap = (boxsz | 32) >> 5; // minimum 1 pixel gap + int w = (boxsz + gap) * layout->w / 2 - gap; + int h = (boxsz + gap) * layout->h / 2 - gap; + int basex = roundf(con_getvarf(sst_inputhud_x) * (screenw - w)); + int basey = roundf(con_getvarf(sst_inputhud_y) * (screenh - h)); + int buttons = heldbuttons | tappedbuttons; + for (int mask = layout->mask, bitidx, bit; mask; mask ^= bit) { + bitidx = bsf(mask); bit = 1 << bitidx; + // divide sizes by 2 here to allow in-between positioning + int x = basex + layout->pos[bitidx].x * (boxsz + gap) / 2; + int y = basey + layout->pos[bitidx].y * (boxsz + gap) / 2; + hud_drawrect(x, y, x + boxsz, y + boxsz, + colours[!!(buttons & bit)], true); + if_hot (font) { + int tw, th; + hud_textsize(font, text[bitidx].s, &tw, &th); + hud_drawtext(font, x + (boxsz - tw) / 2, y + (boxsz - th) / 2, + colours[2], text[bitidx].s, text[bitidx].len); + } + } + tappedbuttons = 0; +} + +// find the CInput "input" global +static inline bool find_input(void* vclient) { +#ifdef _WIN32 + // the only CHLClient::DecodeUserCmdFromBuffer() does is call a virtual + // function, so find its thisptr being loaded into ECX + void* decodeusercmd = + (*(void***)vclient)[vtidx_VClient_DecodeUserCmdFromBuffer]; + for (uchar *p = (uchar *)decodeusercmd; p - (uchar *)decodeusercmd < 32;) { + if (p[0] == X86_MOVRMW && p[1] == X86_MODRM(0, 1, 5)) { + void **indirect = mem_loadptr(p + 2); + input = *indirect; + return true; + } + NEXT_INSN(p, "input object"); + } +#else +#warning TODO(linux): implement linux equivalent (see demorec.c) +#endif + return false; +} + +INIT { + void *vclient; + if (!(vclient = factory_client("VClient015", 0)) && + !(vclient = factory_client("VClient016", 0)) && + !(vclient = factory_client("VClient017", 0))) { + errmsg_errorx("couldn't get client interface"); + return false; + } + if (!find_input(vclient)) { + errmsg_errorx("couldn't find input global"); + return false; + } + for (int i = 0; i < countof(fontnames); ++i) { + fonts[i].h = hud_getfont(fontnames[i], true); + if (!fonts[i].h) { + errmsg_warnx("couldn't get \"%s\" font", fontnames[i]); + } + else { + int dummy; + // use (roughly) the widest string as a reference for what will fit + hud_textsize(fonts[i].h, L"Speed", &fonts[i].sz, &dummy); + } + } + void **vtable = mem_loadptr(input); + // just unprotect the first few pointers (GetUserCmd is 8) + if (!os_mprot(vtable, sizeof(void *) * 8, PAGE_READWRITE)) { + errmsg_errorsys("couldn't make virtual table writable"); + return false; + } + if (GAMETYPE_MATCHES(L4Dbased)) { + orig_CreateMove = (CreateMove_func)hook_vtable(vtable, vtidx_CreateMove, + (void *)&hook_CreateMove_l4dbased); + orig_DecodeUserCmdFromBuffer = (DecodeUserCmdFromBuffer_func)hook_vtable( + vtable, vtidx_DecodeUserCmdFromBuffer, + (void *)&hook_DecodeUserCmdFromBuffer_l4dbased); + } + else { + orig_CreateMove = (CreateMove_func)hook_vtable(vtable, vtidx_CreateMove, + (void *)&hook_CreateMove); + orig_DecodeUserCmdFromBuffer = (DecodeUserCmdFromBuffer_func)hook_vtable( + vtable, vtidx_DecodeUserCmdFromBuffer, + (void *)&hook_DecodeUserCmdFromBuffer); + } + + if (GAMETYPE_MATCHES(Portal1)) layout = &layout_portal1; + //else if (GAMETYPE_MATCHES(Portal2)) layout = &layout_portal2; + else if (GAMETYPE_MATCHES(L4D)) layout = &layout_l4d; + // TODO(compat): more game-specific layouts! + + sst_inputhud->base.flags &= ~CON_HIDDEN; + sst_inputhud_scale->base.flags &= ~CON_HIDDEN; + sst_inputhud_bgcolour_normal->base.flags &= ~CON_HIDDEN; + sst_inputhud_bgcolour_normal->cb = &colourcb; + sst_inputhud_bgcolour_pressed->base.flags &= ~CON_HIDDEN; + sst_inputhud_bgcolour_pressed->cb = &colourcb; + sst_inputhud_fgcolour->base.flags &= ~CON_HIDDEN; + sst_inputhud_fgcolour->cb = &colourcb; + sst_inputhud_x->base.flags &= ~CON_HIDDEN; + sst_inputhud_y->base.flags &= ~CON_HIDDEN; + + // HACK: default HUD position would clash with L4D player health HUDs and + // HL2 sprint HUD, so move it up. this currently has to be done in a super + // crappy, nasty way to get the defaults to display right in the console... + // TODO(opt): move PREINIT stuff to before cvar init and avoid this nonsense + if (GAMETYPE_MATCHES(L4D)) { + sst_inputhud_y->defaultval = "0.82"; + con_setvarstr(sst_inputhud_y, "0.82"); + } + else if (GAMETYPE_MATCHES(HL2series)) { + sst_inputhud_y->defaultval = "0.75"; + con_setvarstr(sst_inputhud_y, "0.75"); + } + + return true; +} + +END { + void **vtable = mem_loadptr(input); + unhook_vtable(vtable, vtidx_CreateMove, (void *)orig_CreateMove); + // N.B.: since the orig_ function is in a union, we don't have to worry + // about which version we're unhooking + unhook_vtable(vtable, vtidx_DecodeUserCmdFromBuffer, + (void *)orig_DecodeUserCmdFromBuffer); +} + +// vi: sw=4 ts=4 noet tw=80 cc=80 fdm=marker -- cgit v1.2.3