// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2014-2016 by John "JTE" Muniz. // Copyright (C) 2014-2018 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file lua_hudlib.c /// \brief custom HUD rendering library for Lua scripting #include "doomdef.h" #include "fastcmp.h" #include "r_defs.h" #include "r_local.h" #include "st_stuff.h" // hudinfo[] #include "y_inter.h" #include "g_game.h" #include "i_video.h" // rendermode #include "p_local.h" // camera_t #include "screen.h" // screen width/height #include "v_video.h" #include "w_wad.h" #include "z_zone.h" #include "k_kart.h" #include "k_hud.h" // SRB2kart #include "lua_script.h" #include "lua_libs.h" #include "lua_hud.h" #include "lua_hook.h" #ifdef ROTSPRITE #include "r_patchrotation.h" #endif #define HUDONLY if (!hud_running) return luaL_error(L, "HUD rendering code should not be called outside of rendering hooks!"); boolean hud_running = false; boolean hud_interpolate = false; UINT8 hud_interptag = 0; UINT32 hud_interpcounter = 0; boolean hud_interpstring = false; boolean hud_interplatch = false; static UINT8 hud_enabled[(hud_MAX/8)+1]; static UINT8 camnum = 1; // must match enum hud in lua_hud.h static const char *const hud_disable_options[] = { "stagetitle", "textspectator", "time", "gametypeinfo", // Bumpers / Karma / Laps depending on gametype "minimap", "item", "position", "check", // "CHECK" f-zero indicator "minirankings", // Gametype rankings to the left "battlerankingsbumpers", // bumper drawer for battle. Useful if you want to make a custom battle gamemode without bumpers being involved. "battlefullscreen", // battlefullscreen func (WAIT, ATTACK OR PROTECT ...) "battlecomebacktimer", // come back timer in battlefullscreen "wanted", "speedometer", "statdisplay", "inputdisplay", "nametags", "driftgauge", "freeplay", "rankings", NULL}; enum hudinfo { hudinfo_x = 0, hudinfo_y }; static const char *const hudinfo_opt[] = { "x", "y", NULL}; enum patch { patch_valid = 0, patch_width, patch_height, patch_leftoffset, patch_topoffset }; static const char *const patch_opt[] = { "valid", "width", "height", "leftoffset", "topoffset", NULL}; static int patch_fields_ref = LUA_NOREF; // alignment types for v.drawString enum align { align_left = 0, align_center, align_right, align_fixed, align_small, align_smallright, align_thin, align_thinright }; static const char *const align_opt[] = { "left", "center", "right", "fixed", "small", "small-right", "thin", "thin-right", NULL}; // width types for v.stringWidth enum widtht { widtht_normal = 0, widtht_small, widtht_thin }; static const char *const widtht_opt[] = { "normal", "small", "thin", NULL}; enum cameraf { camera_chase = 0, camera_aiming, camera_x, camera_y, camera_z, camera_angle, camera_subsector, camera_floorz, camera_ceilingz, camera_radius, camera_height, camera_momx, camera_momy, camera_momz, camera_pnum, camera_freecam, }; static const char *const camera_opt[] = { "chase", "aiming", "x", "y", "z", "angle", "subsector", "floorz", "ceilingz", "radius", "height", "momx", "momy", "momz", "pnum", "freecam", NULL}; enum hudpatch { hudpatch_item = 0, hudpatch_itemmul }; static const char *const hud_patch_options[] = { "item", "itemmul", NULL}; enum hudoffsets { hudoffsets_item = 0, hudoffsets_time, hudoffsets_gametypeinfo, hudoffsets_countdown, hudoffsets_speedometer, hudoffsets_position, hudoffsets_minirankings, hudoffsets_startcountdown, hudoffsets_check, hudoffsets_minimap, hudoffsets_wanted, hudoffsets_statdisplay, hudoffsets_inputdisplay, }; static const char *const hud_offsets_options[] = { "item", "time", "gametypeinfo", "countdown", "speedometer", "position", "minirankings", "startcountdown", "check", "minimap", "wanted", "statdisplay", "inputdisplay", NULL}; enum huddrawinfo { huddrawinfo_item = 0, huddrawinfo_gametypeinfo, huddrawinfo_minimap }; static const char *const hud_drawinfo_options[] = { "item", "gametypeinfo", "minimap", NULL}; static int camera_fields_ref = LUA_NOREF; static int lib_getHudInfo(lua_State *L) { UINT32 i; lua_remove(L, 1); i = luaL_checkinteger(L, 1); if (i >= NUMHUDITEMS) return luaL_error(L, "hudinfo[] index %d out of range (0 - %d)", i, NUMHUDITEMS-1); LUA_PushUserdata(L, &hudinfo[i], META_HUDINFO); return 1; } FUNCINLINE static ATTRINLINE int lib_hudinfolen(lua_State *L) { lua_pushinteger(L, NUMHUDITEMS); return 1; } static int hudinfo_get(lua_State *L) { hudinfo_t *info = *((hudinfo_t **)luaL_checkudata(L, 1, META_HUDINFO)); enum hudinfo field = luaL_checkoption(L, 2, hudinfo_opt[0], hudinfo_opt); I_Assert(info != NULL); // huditems are always valid switch(field) { case hudinfo_x: lua_pushinteger(L, info->x); break; case hudinfo_y: lua_pushinteger(L, info->y); break; } return 1; } static int hudinfo_set(lua_State *L) { hudinfo_t *info = *((hudinfo_t **)luaL_checkudata(L, 1, META_HUDINFO)); enum hudinfo field = luaL_checkoption(L, 2, hudinfo_opt[0], hudinfo_opt); I_Assert(info != NULL); switch(field) { case hudinfo_x: info->x = (INT32)luaL_checkinteger(L, 3); break; case hudinfo_y: info->y = (INT32)luaL_checkinteger(L, 3); break; } return 0; } FUNCINLINE static ATTRINLINE int hudinfo_num(lua_State *L) { hudinfo_t *info = *((hudinfo_t **)luaL_checkudata(L, 1, META_HUDINFO)); lua_pushinteger(L, info-hudinfo); return 1; } FUNCINLINE static ATTRINLINE int colormap_get(lua_State *L) { return luaL_error(L, "colormap is not a struct."); } static int patch_get(lua_State *L) { patch_t *patch = *((patch_t **)luaL_checkudata(L, 1, META_PATCH)); enum patch field = Lua_optoption(L, 2, -1, patch_fields_ref); if (!patch) { if (field == patch_valid) { lua_pushboolean(L, 0); return 1; } return LUA_ErrInvalid(L, "patch_t"); } switch (field) { case patch_valid: lua_pushboolean(L, patch != NULL); break; case patch_width: lua_pushinteger(L, patch->width); break; case patch_height: lua_pushinteger(L, patch->height); break; case patch_leftoffset: lua_pushinteger(L, patch->leftoffset); break; case patch_topoffset: lua_pushinteger(L, patch->topoffset); break; } return 1; } FUNCINLINE static ATTRINLINE int patch_set(lua_State *L) { return luaL_error(L, LUA_QL("patch_t") " struct cannot be edited by Lua."); } static int camera_get(lua_State *L) { camera_t *cam = *((camera_t **)luaL_checkudata(L, 1, META_CAMERA)); enum cameraf field = Lua_optoption(L, 2, -1, camera_fields_ref); // cameras should always be valid unless I'm a nutter I_Assert(cam != NULL); switch (field) { case camera_chase: lua_pushboolean(L, cam->chase); break; case camera_aiming: lua_pushinteger(L, cam->aiming); break; case camera_x: lua_pushinteger(L, cam->x); break; case camera_y: lua_pushinteger(L, cam->y); break; case camera_z: lua_pushinteger(L, cam->z); break; case camera_angle: lua_pushinteger(L, cam->angle); break; case camera_subsector: LUA_PushUserdata(L, cam->subsector, META_SUBSECTOR); break; case camera_floorz: lua_pushinteger(L, cam->floorz); break; case camera_ceilingz: lua_pushinteger(L, cam->ceilingz); break; case camera_radius: lua_pushinteger(L, cam->radius); break; case camera_height: lua_pushinteger(L, cam->height); break; case camera_momx: lua_pushinteger(L, cam->momx); break; case camera_momy: lua_pushinteger(L, cam->momy); break; case camera_momz: lua_pushinteger(L, cam->momz); break; case camera_pnum: lua_pushinteger(L, camnum); break; case camera_freecam: lua_pushboolean(L, cam->freecam); break; } return 1; } static int lib_getCamera(lua_State *L) { // No cameras on dedicated if (dedicated) return 0; if (lua_type(L, 2) == LUA_TNUMBER) { int i = lua_tonumber(L, 2); if (i < 0 || i > splitscreen) return 0; LUA_PushUserdata(L, &camera[i], META_CAMERA); return 1; } return 0; } static int lib_lenCamera(lua_State *L) { if (dedicated) lua_pushinteger(L, 0); else lua_pushinteger(L, splitscreen+1); // splitscreen == 0 -> 1 active camera, splitscreen == 1 -> 2 cameras and so on return 1; } // // lib_draw // FUNCINLINE static ATTRINLINE int libd_patchExists(lua_State *L) { HUDONLY lua_pushboolean(L, W_LumpExists(luaL_checkstring(L, 1))); return 1; } FUNCINLINE static ATTRINLINE int libd_cachePatch(lua_State *L) { HUDONLY LUA_PushUserdata(L, W_CachePatchName(luaL_checkstring(L, 1), PU_PATCH), META_PATCH); return 1; } #ifdef ROTSPRITE static int libd_cachePatchRotated(lua_State *L) { HUDONLY angle_t rollangle = luaL_checkangle(L, 2); INT32 rot = R_GetRollAngle(rollangle); LUA_PushUserdata(L, W_CachePatchNameRotated(luaL_checkstring(L, 1), rot, PU_PATCH), META_PATCH); return 1; } #endif // this is structured like getSprite2Patch in vanilla 2.2 // v.getSpritePatch(skin, sprite, [frame, [angle, [rollangle]]]) static int libd_getSpritePatch(lua_State *L) { UINT32 i; // sprite prefix INT32 skn = -1; // skin number UINT32 frame = 0; // 'A' UINT8 angle = 0; spritedef_t *sprdef; spriteframe_t *sprframe; #ifdef ROTSPRITE spriteinfo_t *sprinfo; #endif HUDONLY // mashing together the stuff from getSprite2Patch: // get skin first! if (!lua_isnoneornil(L, 1)) // ONLY do this if we have a skin { if (lua_isnumber(L, 1)) // find skin by number { skn = lua_tonumber(L, 1); if (skn < 0 || skn >= MAXSKINS) return luaL_error(L, "skin number %d out of range (0 - %d)", skn, MAXSKINS-1); if (skn >= numskins) return 0; } else // find skin by name { const char *name = luaL_checkstring(L, 1); for (skn = 0; skn < numskins; skn++) if (fastcmp(skins[skn].name, name)) break; if (skn >= numskins) return 0; } } lua_remove(L, 1); // remove skin now if (lua_isnumber(L, 1)) // sprite number given, e.g. SPR_THOK { i = lua_tonumber(L, 1); if (i >= NUMSPRITES) return 0; } else if (lua_isstring(L, 1)) // sprite prefix name given, e.g. "THOK" { const char *name = lua_tostring(L, 1); for (i = 0; i < NUMSPRITES; i++) if (fastcmp(name, sprnames[i])) break; if (i >= NUMSPRITES) return 0; } else return 0; // get outta dodge if we're a playersprite and have no skin if ((i == SPR_PLAY) && (skn < 0)) return luaL_error(L, "You must provide a skin for player sprites!"); if (skn < 0) // standard sprite { sprdef = &sprites[i]; #ifdef ROTSPRITE sprinfo = &spriteinfo[i]; #endif } else // player skin { sprdef = &skins[skn].spritedef; #ifdef ROTSPRITE sprinfo = &skins[skn].sprinfo; #endif } // set frame number frame = luaL_optinteger(L, 2, 0); frame &= FF_FRAMEMASK; // ignore any bits that are not the actual frame, just in case if (frame >= sprdef->numframes) return 0; // set angle number sprframe = &sprdef->spriteframes[frame]; angle = luaL_optinteger(L, 3, 1); // convert WAD editor angle numbers (1-8) to internal angle numbers (0-7) // keep 0 the same since we'll make it default to angle 1 (which is internally 0) // in case somebody didn't know that angle 0 really just maps all 8 angles to the same patch if (angle != 0) angle--; if (angle >= 8) // out of range? angle = (angle & 7); // modulus angle by 8 #ifdef ROTSPRITE // rotsprite????? if (lua_isnumber(L, 4)) { angle_t rollangle = luaL_checkangle(L, 4); INT32 rot = R_GetRollAngle(rollangle); if (rot) { patch_t *rotsprite = Patch_GetRotatedSprite(sprframe, frame, angle, sprframe->flip & (1<lumppat[angle], PU_SPRITE), META_PATCH); lua_pushboolean(L, (sprframe->flip & (1<width/2); mm_y = info.y - (AutomapPic->height/2); // let offsets transfer to the heads, too! if (encoremode) { mm_x += AutomapPic->leftoffset; } else { mm_x -= AutomapPic->leftoffset; } mm_y -= AutomapPic->topoffset; if (centered) { patchw = patchh = 0; // patch is supposedly already centered, don't butt in. } else { // scale patch coords patchw = (patch->width * scale / 2); patchh = (patch->height * scale / 2); } amnumxpos = (FixedMul(x, minimapinfo.zoom) - minimapinfo.offs_x); amnumypos = -(FixedMul(y, minimapinfo.zoom) - minimapinfo.offs_y); if (encoremode) { amnumxpos = -amnumxpos; } amxpos = amnumxpos + ((mm_x + AutomapPic->width / 2)<height / 2)< maxstrength) return luaL_error(L, "%s fade strength %d out of range (0 - %d)", ((color & 0xFF00) ? "COLORMAP" : "TRANSMAP"), strength, maxstrength); lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); list = (huddrawlist_h) lua_touserdata(L, -1); lua_pop(L, 1); if (strength == maxstrength) // Allow as a shortcut for drawfill... { if (LUA_HUD_IsDrawListValid(list)) LUA_HUD_AddDrawFill(list, 0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color)); else V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color)); return 0; } if (LUA_HUD_IsDrawListValid(list)) LUA_HUD_AddFadeScreen(list, color, strength); else V_DrawFadeScreen(color, strength); return 0; } static int libd_drawString(lua_State *L) { huddrawlist_h list; fixed_t x = luaL_checkinteger(L, 1); fixed_t y = luaL_checkinteger(L, 2); const char *str = luaL_checkstring(L, 3); INT32 flags = luaL_optinteger(L, 4, V_ALLOWLOWERCASE); enum align align = luaL_checkoption(L, 5, "left", align_opt); flags &= ~V_PARAMMASK; // Don't let crashes happen. HUDONLY lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); list = (huddrawlist_h) lua_touserdata(L, -1); lua_pop(L, 1); // okay, sorry, this is kind of ugly if (LUA_HUD_IsDrawListValid(list)) LUA_HUD_AddDrawString(list, x, y, str, flags, align); else { switch(align) { // hu_font case align_left: V_DrawString(x, y, flags, str); break; case align_center: V_DrawCenteredString(x, y, flags, str); break; case align_right: V_DrawRightAlignedString(x, y, flags, str); break; case align_fixed: V_DrawStringAtFixed(x, y, flags, str); break; // hu_font, 0.5x scale case align_small: V_DrawSmallString(x, y, flags, str); break; case align_smallright: V_DrawRightAlignedSmallString(x, y, flags, str); break; // tny_font case align_thin: V_DrawThinString(x, y, flags, str); break; case align_thinright: V_DrawRightAlignedThinString(x, y, flags, str); break; } } return 0; } static int libd_drawKartString(lua_State *L) { fixed_t x = luaL_checkinteger(L, 1); fixed_t y = luaL_checkinteger(L, 2); const char *str = luaL_checkstring(L, 3); INT32 flags = luaL_optinteger(L, 4, V_ALLOWLOWERCASE); huddrawlist_h list; flags &= ~V_PARAMMASK; // Don't let crashes happen. HUDONLY lua_getfield(L, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); list = (huddrawlist_h) lua_touserdata(L, -1); lua_pop(L, 1); if (LUA_HUD_IsDrawListValid(list)) LUA_HUD_AddDrawKartString(list, x, y, str, flags); else V_DrawKartString(x, y, flags, str); return 0; } static int libd_stringWidth(lua_State *L) { const char *str = luaL_checkstring(L, 1); INT32 flags = luaL_optinteger(L, 2, V_ALLOWLOWERCASE); enum widtht widtht = luaL_checkoption(L, 3, "normal", widtht_opt); HUDONLY switch(widtht) { case widtht_normal: // hu_font lua_pushinteger(L, V_StringWidth(str, flags)); break; case widtht_small: // hu_font, 0.5x scale lua_pushinteger(L, V_SmallStringWidth(str, flags)); break; case widtht_thin: // tny_font lua_pushinteger(L, V_ThinStringWidth(str, flags)); break; } return 1; } static int libd_getColormap(lua_State *L) { INT32 skinnum = TC_DEFAULT; skincolors_t color = luaL_optinteger(L, 2, 0); UINT8* colormap = NULL; HUDONLY if (lua_isnoneornil(L, 1)) ; // defaults to TC_DEFAULT else if (lua_type(L, 1) == LUA_TNUMBER) // skin number { skinnum = (INT32)luaL_checkinteger(L, 1); if (skinnum < TC_BLINK || skinnum >= MAXSKINS) return luaL_error(L, "skin number %d is out of range (%d - %d)", skinnum, TC_BLINK, MAXSKINS-1); } else // skin name { const char *skinname = luaL_checkstring(L, 1); INT32 i = R_SkinAvailable(skinname); if (i != -1) // if -1, just default to TC_DEFAULT as above skinnum = i; } // all was successful above, now we generate the colormap at last! colormap = R_GetTranslationColormap(skinnum, color, GTC_CACHE); LUA_PushUserdata(L, colormap, META_COLORMAP); // push as META_COLORMAP userdata, specifically for patches to use! return 1; } static int libd_getColorHudPatch(lua_State *L) { HUDONLY enum hudpatch option = luaL_checkoption(L, 1, NULL, hud_patch_options); patch_t *patch; UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, K_GetHudColor(), GTC_CACHE); boolean small, dark; switch (option) { case hudpatch_item: small = lua_optboolean(L, 2); dark = lua_optboolean(L, 3); patch = K_getItemBoxPatch(small, dark); if (!cv_colorizeditembox.value) colormap = NULL; break; case hudpatch_itemmul: small = lua_optboolean(L, 2); patch = K_getItemMulPatch(small); break; default: return 0; // you shouldn't be here } LUA_PushUserdata(L, patch, META_PATCH); if (colormap && K_UseColorHud()) LUA_PushUserdata(L, colormap, META_COLORMAP); else lua_pushnil(L); return 2; } FUNCINLINE static ATTRINLINE int libd_getHudColor(lua_State *L) { HUDONLY lua_pushinteger(L, K_GetHudColor()); return 1; } FUNCINLINE static ATTRINLINE int libd_useColorHud(lua_State *L) { HUDONLY lua_pushboolean(L, K_UseColorHud()); return 1; } FUNCINLINE static ATTRINLINE int libd_width(lua_State *L) { HUDONLY lua_pushinteger(L, vid.width); // push screen width return 1; } FUNCINLINE static ATTRINLINE int libd_height(lua_State *L) { HUDONLY lua_pushinteger(L, vid.height); // push screen height return 1; } FUNCINLINE static ATTRINLINE int libd_dup(lua_State *L) { HUDONLY lua_pushinteger(L, vid.dup); // push integral scale (patch scale) lua_pushfixed(L, vid.fdup); // push fixed point scale (position scale) return 2; } FUNCINLINE static ATTRINLINE int libd_renderer(lua_State *L) { HUDONLY switch (rendermode) { case render_opengl: lua_pushliteral(L, "opengl"); break; // OpenGL renderer case render_soft: lua_pushliteral(L, "software"); break; // Software renderer default: lua_pushliteral(L, "none"); break; // render_none (for dedicated), in case there's any reason this should be run } return 1; } // 30/10/18 Lat': Get cv_translucenthud's value for HUD rendering as a normal V_xxTRANS int // Could as well be thrown in global vars for ease of access but I guess it makes sense for it to be a HUD fn FUNCINLINE static ATTRINLINE int libd_getlocaltransflag(lua_State *L) { HUDONLY lua_pushinteger(L, (10-cv_translucenthud.value)*V_10TRANS); // A bit weird that it's called "translucenthud" yet 10 is fully opaque :V return 1; } static int libd_getDrawInfo(lua_State *L) { HUDONLY enum huddrawinfo option = luaL_checkoption(L, 1, NULL, hud_drawinfo_options); drawinfo_t info; switch(option) { case huddrawinfo_item: K_getItemBoxDrawinfo(&info); break; case huddrawinfo_gametypeinfo: K_getLapsDrawinfo(&info); break; case huddrawinfo_minimap: K_getMinimapDrawinfo(&info); break; default: return 0; // unreachable } lua_pushinteger(L, info.x); lua_pushinteger(L, info.y); lua_pushinteger(L, info.flags); return 3; } static int libd_interpolate(lua_State *L) { HUDONLY // do type checking even if interpolation is disabled boolean newinterpolate; UINT8 newtag; if (lua_isnumber(L, 1)) { newinterpolate = true; newtag = luaL_checkinteger(L, 1); } else { newinterpolate = luaL_checkboolean(L, 1); newtag = 0; } if (!cv_uncappedhud.value) return 0; hud_interpolate = newinterpolate; hud_interptag = newtag; return 0; } static int libd_interpLatch(lua_State *L) { HUDONLY hud_interpstring = hud_interplatch = luaL_checkboolean(L, 1); return 0; } static luaL_Reg lib_draw[] = { {"patchExists", libd_patchExists}, {"cachePatch", libd_cachePatch}, #ifdef ROTSPRITE // Is this ifdef nonsense? Yes. {"cachePatchRotated", libd_cachePatchRotated}, #endif {"draw", libd_draw}, {"drawScaled", libd_drawScaled}, {"drawStretched", libd_drawStretched}, {"drawNum", libd_drawNum}, {"drawPaddedNum", libd_drawPaddedNum}, {"drawPingNum", libd_drawPingNum}, {"drawFill", libd_drawFill}, {"getSpritePatch",libd_getSpritePatch}, {"fadeScreen", libd_fadeScreen}, {"drawString", libd_drawString}, {"drawKartString", libd_drawKartString}, {"stringWidth", libd_stringWidth}, {"getColormap", libd_getColormap}, {"width", libd_width}, {"height", libd_height}, {"dupx", libd_dup}, {"dupy", libd_dup}, {"renderer", libd_renderer}, {"localTransFlag", libd_getlocaltransflag}, {"drawOnMinimap", libd_drawOnMinimap}, {"getColorHudPatch", libd_getColorHudPatch}, {"getDrawInfo", libd_getDrawInfo}, {"getHudColor", libd_getHudColor}, {"useColorHud", libd_useColorHud}, {"interpolate", libd_interpolate}, {"interpLatch", libd_interpLatch}, {NULL, NULL} }; static int lib_draw_ref; // // lib_hud // // enable vanilla HUD element FUNCINLINE static ATTRINLINE int lib_hudenable(lua_State *L) { enum hud option = luaL_checkoption(L, 1, NULL, hud_disable_options); hud_enabled[option/8] |= 1<<(option%8); return 0; } // disable vanilla HUD element FUNCINLINE static ATTRINLINE int lib_huddisable(lua_State *L) { enum hud option = luaL_checkoption(L, 1, NULL, hud_disable_options); hud_enabled[option/8] &= ~(1<<(option%8)); return 0; } // 30/10/18: Lat': How come this wasn't here before? FUNCINLINE static ATTRINLINE int lib_hudenabled(lua_State *L) { enum hud option = luaL_checkoption(L, 1, NULL, hud_disable_options); if (hud_enabled[option/8] & (1<<(option%8))) lua_pushboolean(L, true); else lua_pushboolean(L, false); return 1; } // add a HUD element for rendering extern int lib_hudadd(lua_State *L); static int lib_hudsetvotebackground(lua_State *L) { // Reset to default memset(VoteScreen.luaPrefix, 0, sizeof(VoteScreen.luaPrefix)); VoteScreen.timePerAnimFrame = 2; if (!lua_isnoneornil(L, 1)) { const char *prefix = luaL_checkstring(L, 1); if (strlen(prefix) != 4) { return luaL_argerror(L, 1, "prefix should be 4 characters wide"); } strncpy(VoteScreen.luaPrefix, prefix, 4); strupr(VoteScreen.luaPrefix); } if (!lua_isnoneornil(L, 2)) { INT32 time_per_frame = luaL_checkinteger(L, 2); if (time_per_frame <= 0) return luaL_argerror(L, 2, "time per frame should be 1 or more"); VoteScreen.timePerAnimFrame = time_per_frame; } // Update background if we're already on vote screen if (gamestate == GS_VOTING) Y_VoteScreenCheck(); return 0; } static int lib_hudgetoffsets(lua_State *L) { enum hudoffsets option = luaL_checkoption(L, 1, NULL, hud_offsets_options); INT32 ofx, ofy; #define OFS(it) ofx = cv_##it##_xoffset.value; ofy = cv_##it##_yoffset.value; break; #define OFY(it) ofx = 0; ofy = cv_##it##_yoffset.value; break; switch(option) { case hudoffsets_item: OFS(item) case hudoffsets_time: OFS(time) case hudoffsets_gametypeinfo: OFS(laps) case hudoffsets_countdown: OFS(dnft) case hudoffsets_speedometer: OFS(speed) case hudoffsets_position: OFS(posi) case hudoffsets_minirankings: OFS(face) case hudoffsets_startcountdown: OFS(stcd) case hudoffsets_check: OFY(chek) case hudoffsets_minimap: OFS(mini) case hudoffsets_wanted: OFS(want) case hudoffsets_statdisplay: OFS(stat) case hudoffsets_inputdisplay: OFS(wheel) default: return 0; // unreachable } #undef OFS #undef OFY lua_pushinteger(L, ofx); lua_pushinteger(L, ofy); return 2; } static luaL_Reg lib_hud[] = { {"enable", lib_hudenable}, {"disable", lib_huddisable}, {"enabled", lib_hudenabled}, {"add", lib_hudadd}, {"setVoteBackground", lib_hudsetvotebackground}, {"getOffsets", lib_hudgetoffsets}, {NULL, NULL} }; // // // int LUA_HudLib(lua_State *L) { memset(hud_enabled, 0xff, (hud_MAX/8)+1); lua_newtable(L); luaL_register(L, NULL, lib_draw); lib_draw_ref = luaL_ref(L, LUA_REGISTRYINDEX); luaL_newmetatable(L, META_HUDINFO); lua_pushcfunction(L, hudinfo_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, hudinfo_set); lua_setfield(L, -2, "__newindex"); lua_pushcfunction(L, hudinfo_num); lua_setfield(L, -2, "__len"); lua_pop(L,1); lua_newuserdata(L, 0); lua_createtable(L, 0, 2); lua_pushcfunction(L, lib_getHudInfo); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, lib_hudinfolen); lua_setfield(L, -2, "__len"); lua_setmetatable(L, -2); lua_setglobal(L, "hudinfo"); luaL_newmetatable(L, META_COLORMAP); lua_pushcfunction(L, colormap_get); lua_setfield(L, -2, "__index"); lua_pop(L,1); luaL_newmetatable(L, META_PATCH); lua_pushcfunction(L, patch_get); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, patch_set); lua_setfield(L, -2, "__newindex"); lua_pop(L,1); patch_fields_ref = Lua_CreateFieldTable(L, patch_opt); luaL_newmetatable(L, META_CAMERA); lua_pushcfunction(L, camera_get); lua_setfield(L, -2, "__index"); lua_pop(L,1); lua_newuserdata(L, 0); lua_createtable(L, 0, 2); lua_pushcfunction(L, lib_getCamera); lua_setfield(L, -2, "__index"); lua_pushcfunction(L, lib_lenCamera); lua_setfield(L, -2, "__len"); lua_setmetatable(L, -2); lua_setglobal(L, "cameras"); camera_fields_ref = Lua_CreateFieldTable(L, camera_opt); luaL_register(L, "hud", lib_hud); return 0; } boolean LUA_HudEnabled(enum hud option) { if (!gL || hud_enabled[option/8] & (1<<(option%8))) return true; return false; } void LUA_SetHudHook(int hook, huddrawlist_h list) { lua_getref(gL, lib_draw_ref); lua_pushlightuserdata(gL, list); lua_setfield(gL, LUA_REGISTRYINDEX, "HUD_DRAW_LIST"); // there used to be a switch statement here, its gone now. if (hook == HUD_HOOK(game)) { LUA_PushUserdata(gL, stplyr, META_PLAYER); LUA_PushUserdata(gL, &camera[stplyrnum], META_CAMERA); camnum = stplyrnum + 1; // for compatibility } }