// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 2014-2016 by John "JTE" Muniz. // Copyright (C) 2014-2020 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 "g_game.h" #include "i_video.h" // rendermode #include "p_local.h" // camera_t #include "screen.h" // screen width/height #include "m_random.h" // m_random #include "v_video.h" #include "w_wad.h" #include "z_zone.h" #include "lua_script.h" #include "lua_libs.h" #include "lua_hud.h" #define HUDONLY if (!hud_running) return luaL_error(L, "HUD rendering code should not be called outside of rendering hooks!"); boolean hud_running = false; static UINT8 hud_enabled[(hud_MAX/8)+1]; static UINT8 hudAvailable; // hud hooks field #ifdef LUA_PATCH_SAFETY static patchinfo_t *patchinfo, *patchinfohead; static int numluapatches; #endif // must match enum hud in lua_hud.h static const char *const hud_disable_options[] = { "stagetitle", "textspectator", "score", "time", "rings", "lives", "weaponrings", "powerstones", "teamscores", "nightslink", "nightsdrill", "nightsrings", "nightsscore", "nightstime", "nightsrecords", "rankings", "coopemeralds", "tokens", "tabemblems", "intermissiontally", "intermissionmessages", NULL}; enum hudinfo { hudinfo_x = 0, hudinfo_y, hudinfo_f }; static const char *const hudinfo_opt[] = { "x", "y", "f", 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}; enum hudhook { hudhook_game = 0, hudhook_scores, hudhook_intermission, hudhook_title, hudhook_titlecard }; static const char *const hudhook_opt[] = { "game", "scores", "intermission", "title", "titlecard", NULL}; // alignment types for v.drawString enum align { align_left = 0, align_center, align_right, align_fixed, align_fixedcenter, align_fixedright, align_small, align_smallfixed, align_smallfixedcenter, align_smallfixedright, align_smallcenter, align_smallright, align_smallthin, align_smallthincenter, align_smallthinright, align_smallthinfixed, align_smallthinfixedcenter, align_smallthinfixedright, align_thin, align_thinfixed, align_thinfixedcenter, align_thinfixedright, align_thincenter, align_thinright }; static const char *const align_opt[] = { "left", "center", "right", "fixed", "fixed-center", "fixed-right", "small", "small-fixed", "small-fixed-center", "small-fixed-right", "small-center", "small-right", "small-thin", "small-thin-center", "small-thin-right", "small-thin-fixed", "small-thin-fixed-center", "small-thin-fixed-right", "thin", "thin-fixed", "thin-fixed-center", "thin-fixed-right", "thin-center", "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 }; static const char *const camera_opt[] = { "chase", "aiming", "x", "y", "z", "angle", "subsector", "floorz", "ceilingz", "radius", "height", "momx", "momy", "momz", NULL}; 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; } static 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; case hudinfo_f: lua_pushinteger(L, info->f); 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; case hudinfo_f: info->f = (INT32)luaL_checkinteger(L, 3); break; } return 0; } static int hudinfo_num(lua_State *L) { hudinfo_t *info = *((hudinfo_t **)luaL_checkudata(L, 1, META_HUDINFO)); lua_pushinteger(L, info-hudinfo); return 1; } static int colormap_get(lua_State *L) { const UINT8 *colormap = *((UINT8 **)luaL_checkudata(L, 1, META_COLORMAP)); UINT32 i = luaL_checkinteger(L, 2); if (i >= 256) return luaL_error(L, "colormap index %d out of range (0 - %d)", i, 255); lua_pushinteger(L, colormap[i]); return 1; } static int patch_get(lua_State *L) { #ifdef LUA_PATCH_SAFETY patch_t *patch = *((patch_t **)luaL_checkudata(L, 1, META_PATCH)); #else patchinfo_t *patch = *((patchinfo_t **)luaL_checkudata(L, 1, META_PATCH)); #endif enum patch field = luaL_checkoption(L, 2, NULL, patch_opt); // patches are invalidated when switching renderers 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, SHORT(patch->width)); break; case patch_height: lua_pushinteger(L, SHORT(patch->height)); break; case patch_leftoffset: lua_pushinteger(L, SHORT(patch->leftoffset)); break; case patch_topoffset: lua_pushinteger(L, SHORT(patch->topoffset)); break; } return 1; } static 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 = luaL_checkoption(L, 2, NULL, camera_opt); // 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; } return 1; } // // lib_draw // static int libd_patchExists(lua_State *L) { HUDONLY lua_pushboolean(L, W_LumpExists(luaL_checkstring(L, 1))); return 1; } static int libd_cachePatch(lua_State *L) { #ifdef LUA_PATCH_SAFETY int i; lumpnum_t lumpnum; patchinfo_t *luapat; patch_t *realpatch; HUDONLY luapat = patchinfohead; lumpnum = W_CheckNumForLongName(luaL_checkstring(L, 1)); if (lumpnum == LUMPERROR) lumpnum = W_GetNumForLongName("MISSING"); for (i = 0; i < numluapatches; i++) { // check if already cached if (luapat->wadnum == WADFILENUM(lumpnum) && luapat->lumpnum == LUMPNUM(lumpnum)) { LUA_PushUserdata(L, luapat, META_PATCH); return 1; } luapat = luapat->next; if (!luapat) break; } if (numluapatches > 0) { patchinfo->next = Z_Malloc(sizeof(patchinfo_t), PU_STATIC, NULL); patchinfo = patchinfo->next; } else { patchinfo = Z_Malloc(sizeof(patchinfo_t), PU_STATIC, NULL); patchinfohead = patchinfo; } realpatch = W_CachePatchNum(lumpnum, PU_PATCH); patchinfo->width = realpatch->width; patchinfo->height = realpatch->height; patchinfo->leftoffset = realpatch->leftoffset; patchinfo->topoffset = realpatch->topoffset; patchinfo->wadnum = WADFILENUM(lumpnum); patchinfo->lumpnum = LUMPNUM(lumpnum); LUA_PushUserdata(L, patchinfo, META_PATCH); numluapatches++; #else HUDONLY LUA_PushUserdata(L, W_CachePatchLongName(luaL_checkstring(L, 1), PU_PATCH), META_PATCH); #endif return 1; } // v.getSpritePatch(sprite, [frame, [angle, [rollangle]]]) static int libd_getSpritePatch(lua_State *L) { UINT32 i; // sprite prefix UINT32 frame = 0; // 'A' UINT8 angle = 0; spritedef_t *sprdef; spriteframe_t *sprframe; HUDONLY 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; if (i == SPR_PLAY) // Use getSprite2Patch instead! return 0; sprdef = &sprites[i]; // 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/16 angles to the same patch if (angle != 0) angle--; if (angle >= ((sprframe->rotate & SRF_3DGE) ? 16 : 8)) // out of range? return 0; #ifdef ROTSPRITE if (lua_isnumber(L, 4)) { // rotsprite????? angle_t rollangle = luaL_checkangle(L, 4); INT32 rot = R_GetRollAngle(rollangle); if (rot) { if (!(sprframe->rotsprite.cached & (1<flip & (1<rotsprite.patch[angle][rot], META_PATCH); lua_pushboolean(L, false); lua_pushboolean(L, true); return 3; } } #endif // push both the patch and it's "flip" value LUA_PushUserdata(L, W_CachePatchNum(sprframe->lumppat[angle], PU_PATCH), META_PATCH); lua_pushboolean(L, (sprframe->flip & (1<= MAXSKINS) return luaL_error(L, "skin number %d out of range (0 - %d)", i, MAXSKINS-1); if (i >= numskins) return 0; } else // find skin by name { const char *name = luaL_checkstring(L, 1); for (i = 0; i < numskins; i++) if (fastcmp(skins[i].name, name)) break; if (i >= numskins) return 0; } lua_remove(L, 1); // remove skin now if (lua_isnumber(L, 1)) // sprite number given, e.g. SPR2_STND { j = lua_tonumber(L, 1); if (j & FF_SPR2SUPER) // e.g. SPR2_STND|FF_SPR2SUPER { super = true; j &= ~FF_SPR2SUPER; // remove flag so the next check doesn't fail } if (j >= free_spr2) return 0; } else if (lua_isstring(L, 1)) // sprite prefix name given, e.g. "STND" { const char *name = lua_tostring(L, 1); for (j = 0; j < free_spr2; j++) if (fastcmp(name, spr2names[j])) break; // if you want super flags you'll have to use the optional boolean following this if (j >= free_spr2) return 0; } else return 0; if (lua_isboolean(L, 2)) // optional boolean for superness { super = lua_toboolean(L, 2); // note: this can override FF_SPR2SUPER from sprite number lua_remove(L, 2); // remove } // if it's not boolean then just assume it's the frame number if (super) j |= FF_SPR2SUPER; j = P_GetSkinSprite2(&skins[i], j, NULL); // feed skin and current sprite2 through to change sprite2 used if necessary sprdef = &skins[i].sprites[j]; // 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/16 angles to the same patch if (angle != 0) angle--; if (angle >= ((sprframe->rotate & SRF_3DGE) ? 16 : 8)) // out of range? return 0; #ifdef ROTSPRITE if (lua_isnumber(L, 4)) { // rotsprite????? angle_t rollangle = luaL_checkangle(L, 4); INT32 rot = R_GetRollAngle(rollangle); if (rot) { if (!(sprframe->rotsprite.cached & (1<flip & (1<rotsprite.patch[angle][rot], META_PATCH); lua_pushboolean(L, false); lua_pushboolean(L, true); return 3; } } #endif // push both the patch and it's "flip" value LUA_PushUserdata(L, W_CachePatchNum(sprframe->lumppat[angle], PU_PATCH), META_PATCH); lua_pushboolean(L, (sprframe->flip & (1<wadnum<<16)+luapat->lumpnum, PU_PATCH); #else patch = *((patch_t **)luaL_checkudata(L, 3, META_PATCH)); if (!patch) return LUA_ErrInvalid(L, "patch_t"); #endif flags = luaL_optinteger(L, 4, 0); if (!lua_isnoneornil(L, 5)) colormap = *((UINT8 **)luaL_checkudata(L, 5, META_COLORMAP)); flags &= ~V_PARAMMASK; // Don't let crashes happen. V_DrawFixedPatch(x<wadnum<<16)+luapat->lumpnum, PU_PATCH); #else patch = *((patch_t **)luaL_checkudata(L, 4, META_PATCH)); if (!patch) return LUA_ErrInvalid(L, "patch_t"); #endif flags = luaL_optinteger(L, 5, 0); if (!lua_isnoneornil(L, 6)) colormap = *((UINT8 **)luaL_checkudata(L, 6, META_COLORMAP)); flags &= ~V_PARAMMASK; // Don't let crashes happen. V_DrawFixedPatch(x, y, scale, flags, patch, colormap); return 0; } static int libd_drawStretched(lua_State *L) { fixed_t x, y, hscale, vscale; INT32 flags; patch_t *patch; const UINT8 *colormap = NULL; HUDONLY x = luaL_checkinteger(L, 1); y = luaL_checkinteger(L, 2); hscale = luaL_checkinteger(L, 3); if (hscale < 0) return luaL_error(L, "negative horizontal scale"); vscale = luaL_checkinteger(L, 4); if (vscale < 0) return luaL_error(L, "negative vertical scale"); patch = *((patch_t **)luaL_checkudata(L, 5, META_PATCH)); flags = luaL_optinteger(L, 6, 0); if (!lua_isnoneornil(L, 7)) colormap = *((UINT8 **)luaL_checkudata(L, 7, META_COLORMAP)); flags &= ~V_PARAMMASK; // Don't let crashes happen. V_DrawStretchyFixedPatch(x, y, hscale, vscale, flags, patch, colormap); return 0; } static int libd_drawNum(lua_State *L) { INT32 x, y, flags, num; HUDONLY x = luaL_checkinteger(L, 1); y = luaL_checkinteger(L, 2); num = luaL_checkinteger(L, 3); flags = luaL_optinteger(L, 4, 0); flags &= ~V_PARAMMASK; // Don't let crashes happen. V_DrawTallNum(x, y, flags, num); return 0; } static int libd_drawPaddedNum(lua_State *L) { INT32 x, y, flags, num, digits; HUDONLY x = luaL_checkinteger(L, 1); y = luaL_checkinteger(L, 2); num = labs(luaL_checkinteger(L, 3)); digits = luaL_optinteger(L, 4, 2); flags = luaL_optinteger(L, 5, 0); flags &= ~V_PARAMMASK; // Don't let crashes happen. V_DrawPaddedTallNum(x, y, flags, num, digits); return 0; } static int libd_drawFill(lua_State *L) { INT32 x = luaL_optinteger(L, 1, 0); INT32 y = luaL_optinteger(L, 2, 0); INT32 w = luaL_optinteger(L, 3, BASEVIDWIDTH); INT32 h = luaL_optinteger(L, 4, BASEVIDHEIGHT); INT32 c = luaL_optinteger(L, 5, 31); HUDONLY V_DrawFill(x, y, w, h, c); return 0; } static int libd_drawString(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); enum align align = luaL_checkoption(L, 5, "left", align_opt); flags &= ~V_PARAMMASK; // Don't let crashes happen. HUDONLY 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; case align_fixedcenter: V_DrawCenteredStringAtFixed(x, y, flags, str); break; case align_fixedright: V_DrawRightAlignedStringAtFixed(x, y, flags, str); break; // hu_font, 0.5x scale case align_small: V_DrawSmallString(x, y, flags, str); break; case align_smallfixed: V_DrawSmallStringAtFixed(x, y, flags, str); break; case align_smallfixedcenter: V_DrawCenteredSmallStringAtFixed(x, y, flags, str); break; case align_smallfixedright: V_DrawRightAlignedSmallStringAtFixed(x, y, flags, str); break; case align_smallcenter: V_DrawCenteredSmallString(x, y, flags, str); break; case align_smallright: V_DrawRightAlignedSmallString(x, y, flags, str); break; case align_smallthin: V_DrawSmallThinString(x, y, flags, str); break; case align_smallthincenter: V_DrawCenteredSmallThinString(x, y, flags, str); break; case align_smallthinright: V_DrawRightAlignedSmallThinString(x, y, flags, str); break; case align_smallthinfixed: V_DrawSmallThinStringAtFixed(x, y, flags, str); break; case align_smallthinfixedcenter: V_DrawCenteredSmallThinStringAtFixed(x, y, flags, str); break; case align_smallthinfixedright: V_DrawRightAlignedSmallThinStringAtFixed(x, y, flags, str); break; // tny_font case align_thin: V_DrawThinString(x, y, flags, str); break; case align_thincenter: V_DrawCenteredThinString(x, y, flags, str); break; case align_thinright: V_DrawRightAlignedThinString(x, y, flags, str); break; case align_thinfixed: V_DrawThinStringAtFixed(x, y, flags, str); break; case align_thinfixedcenter: V_DrawCenteredThinStringAtFixed(x, y, flags, str); break; case align_thinfixedright: V_DrawRightAlignedThinStringAtFixed(x, y, flags, str); break; } return 0; } static int libd_drawNameTag(lua_State *L) { INT32 x; INT32 y; const char *str; INT32 flags; UINT16 basecolor; UINT16 outlinecolor; UINT8 *basecolormap = NULL; UINT8 *outlinecolormap = NULL; HUDONLY x = luaL_checkinteger(L, 1); y = luaL_checkinteger(L, 2); str = luaL_checkstring(L, 3); flags = luaL_optinteger(L, 4, 0); basecolor = luaL_optinteger(L, 5, SKINCOLOR_BLUE); outlinecolor = luaL_optinteger(L, 6, SKINCOLOR_ORANGE); if (basecolor != SKINCOLOR_NONE) basecolormap = R_GetTranslationColormap(TC_DEFAULT, basecolor, GTC_CACHE); if (outlinecolor != SKINCOLOR_NONE) outlinecolormap = R_GetTranslationColormap(TC_DEFAULT, outlinecolor, GTC_CACHE); flags &= ~V_PARAMMASK; // Don't let crashes happen. V_DrawNameTag(x, y, flags, FRACUNIT, basecolormap, outlinecolormap, str); return 0; } static int libd_drawScaledNameTag(lua_State *L) { fixed_t x; fixed_t y; const char *str; INT32 flags; fixed_t scale; UINT16 basecolor; UINT16 outlinecolor; UINT8 *basecolormap = NULL; UINT8 *outlinecolormap = NULL; HUDONLY x = luaL_checkfixed(L, 1); y = luaL_checkfixed(L, 2); str = luaL_checkstring(L, 3); flags = luaL_optinteger(L, 4, 0); scale = luaL_optinteger(L, 5, FRACUNIT); if (scale < 0) return luaL_error(L, "negative scale"); basecolor = luaL_optinteger(L, 6, SKINCOLOR_BLUE); outlinecolor = luaL_optinteger(L, 7, SKINCOLOR_ORANGE); if (basecolor != SKINCOLOR_NONE) basecolormap = R_GetTranslationColormap(TC_DEFAULT, basecolor, GTC_CACHE); if (outlinecolor != SKINCOLOR_NONE) outlinecolormap = R_GetTranslationColormap(TC_DEFAULT, outlinecolor, GTC_CACHE); flags &= ~V_PARAMMASK; // Don't let crashes happen. V_DrawNameTag(FixedInt(x), FixedInt(y), flags, scale, basecolormap, outlinecolormap, 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_nameTagWidth(lua_State *L) { HUDONLY lua_pushinteger(L, V_NameTagWidth(luaL_checkstring(L, 1))); return 1; } static int libd_getColormap(lua_State *L) { INT32 skinnum = TC_DEFAULT; skincolornum_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_getStringColormap(lua_State *L) { INT32 flags = luaL_checkinteger(L, 1); UINT8* colormap = NULL; HUDONLY colormap = V_GetStringColormap(flags & V_CHARCOLORMASK); if (colormap) { LUA_PushUserdata(L, colormap, META_COLORMAP); // push as META_COLORMAP userdata, specifically for patches to use! return 1; } return 0; } static int libd_fadeScreen(lua_State *L) { UINT16 color = luaL_checkinteger(L, 1); UINT8 strength = luaL_checkinteger(L, 2); const UINT8 maxstrength = ((color & 0xFF00) ? 32 : 10); HUDONLY if (!strength) return 0; if (strength > maxstrength) return luaL_error(L, "%s fade strength %d out of range (0 - %d)", ((color & 0xFF00) ? "COLORMAP" : "TRANSMAP"), strength, maxstrength); if (strength == maxstrength) // Allow as a shortcut for drawfill... { V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, ((color & 0xFF00) ? 31 : color)); return 0; } V_DrawFadeScreen(color, strength); return 0; } static int libd_width(lua_State *L) { HUDONLY lua_pushinteger(L, vid.width); // push screen width return 1; } static int libd_height(lua_State *L) { HUDONLY lua_pushinteger(L, vid.height); // push screen height return 1; } static int libd_dupx(lua_State *L) { HUDONLY lua_pushinteger(L, vid.dupx); // push integral scale (patch scale) lua_pushfixed(L, vid.fdupx); // push fixed point scale (position scale) return 2; } static int libd_dupy(lua_State *L) { HUDONLY lua_pushinteger(L, vid.dupy); // push integral scale (patch scale) lua_pushfixed(L, vid.fdupy); // push fixed point scale (position scale) return 2; } static 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; } // M_RANDOM ////////////// static int libd_RandomFixed(lua_State *L) { HUDONLY lua_pushfixed(L, M_RandomFixed()); return 1; } static int libd_RandomByte(lua_State *L) { HUDONLY lua_pushinteger(L, M_RandomByte()); return 1; } static int libd_RandomKey(lua_State *L) { INT32 a = (INT32)luaL_checkinteger(L, 1); HUDONLY if (a > 65536) LUA_UsageWarning(L, "v.RandomKey: range > 65536 is undefined behavior"); lua_pushinteger(L, M_RandomKey(a)); return 1; } static int libd_RandomRange(lua_State *L) { INT32 a = (INT32)luaL_checkinteger(L, 1); INT32 b = (INT32)luaL_checkinteger(L, 2); HUDONLY if (b < a) { INT32 c = a; a = b; b = c; } if ((b-a+1) > 65536) LUA_UsageWarning(L, "v.RandomRange: range > 65536 is undefined behavior"); lua_pushinteger(L, M_RandomRange(a, b)); return 1; } // Macros. static int libd_SignedRandom(lua_State *L) { HUDONLY lua_pushinteger(L, M_SignedRandom()); return 1; } static int libd_RandomChance(lua_State *L) { fixed_t p = luaL_checkfixed(L, 1); HUDONLY lua_pushboolean(L, M_RandomChance(p)); return 1; } // 30/10/18 Lat': Get st_translucency'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 static int libd_getlocaltransflag(lua_State *L) { HUDONLY lua_pushinteger(L, (10-st_translucency)*V_10TRANS); return 1; } // Get cv_translucenthud's value for HUD rendering as a normal V_xxTRANS int static int libd_getusertransflag(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 luaL_Reg lib_draw[] = { // cache {"patchExists", libd_patchExists}, {"cachePatch", libd_cachePatch}, {"getSpritePatch", libd_getSpritePatch}, {"getSprite2Patch", libd_getSprite2Patch}, {"getColormap", libd_getColormap}, {"getStringColormap", libd_getStringColormap}, // drawing {"draw", libd_draw}, {"drawScaled", libd_drawScaled}, {"drawStretched", libd_drawStretched}, {"drawNum", libd_drawNum}, {"drawPaddedNum", libd_drawPaddedNum}, {"drawFill", libd_drawFill}, {"drawString", libd_drawString}, {"drawNameTag", libd_drawNameTag}, {"drawScaledNameTag", libd_drawScaledNameTag}, {"fadeScreen", libd_fadeScreen}, // misc {"stringWidth", libd_stringWidth}, {"nameTagWidth", libd_nameTagWidth}, // m_random {"RandomFixed",libd_RandomFixed}, {"RandomByte",libd_RandomByte}, {"RandomKey",libd_RandomKey}, {"RandomRange",libd_RandomRange}, {"SignedRandom",libd_SignedRandom}, // MACRO {"RandomChance",libd_RandomChance}, // MACRO // properties {"width", libd_width}, {"height", libd_height}, {"dupx", libd_dupx}, {"dupy", libd_dupy}, {"renderer", libd_renderer}, {"localTransFlag", libd_getlocaltransflag}, {"userTransFlag", libd_getusertransflag}, {NULL, NULL} }; // // lib_hud // // enable vanilla HUD element static 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 static 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? static 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 static int lib_hudadd(lua_State *L) { enum hudhook field; luaL_checktype(L, 1, LUA_TFUNCTION); field = luaL_checkoption(L, 2, "game", hudhook_opt); if (!lua_lumploading) return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); lua_getfield(L, LUA_REGISTRYINDEX, "HUD"); I_Assert(lua_istable(L, -1)); lua_rawgeti(L, -1, field+2); // HUD[2+] I_Assert(lua_istable(L, -1)); lua_remove(L, -2); lua_pushvalue(L, 1); lua_rawseti(L, -2, (int)(lua_objlen(L, -2) + 1)); hudAvailable |= 1<