diff --git a/src/deh_soc.c b/src/deh_soc.c index 343beb3012676b93256af35c0bd57ff6ba7e9076..9d571610b6f6847da8179fd06d70d392d3fee72a 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -874,6 +874,7 @@ static void readspriteframe(MYFILE *f, spriteinfo_t *sprinfo, UINT8 frame) char *tmp; INT32 value; char *lastline; + boolean available = false; do { @@ -925,9 +926,15 @@ static void readspriteframe(MYFILE *f, spriteinfo_t *sprinfo, UINT8 frame) value = atoi(word2); // used for numerical settings if (fastcmp(word, "XPIVOT")) - sprinfo->pivot[frame].x = value; + { + sprinfo->frames[frame].pivot.x = value; + available = true; + } else if (fastcmp(word, "YPIVOT")) - sprinfo->pivot[frame].y = value; + { + sprinfo->frames[frame].pivot.y = value; + available = true; + } // TODO: 2.3: Delete else if (fastcmp(word, "ROTAXIS")) deh_warning("SpriteInfo: ROTAXIS is deprecated and will be removed."); @@ -938,6 +945,10 @@ static void readspriteframe(MYFILE *f, spriteinfo_t *sprinfo, UINT8 frame) } } } while (!myfeof(f)); // finish when the line is empty + + if (available) + sprinfo->frames[frame].pivot.available = true; + Z_Free(s); } @@ -955,7 +966,6 @@ void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2) // allocate a spriteinfo spriteinfo_t *info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL); - info->available = true; do { @@ -1074,6 +1084,12 @@ void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2) // read sprite frame and store it in the spriteinfo_t struct readspriteframe(f, info, frame); + set_bit_array(info->available, frame); + + // TODO: 2.3: Delete + info->frames[SPRINFO_DEFAULT_FRAME].pivot.available = true; + set_bit_array(info->available, SPRINFO_DEFAULT_FRAME); + if (sprite2) { INT32 i; diff --git a/src/lua_baselib.c b/src/lua_baselib.c index ecd1ee55e648019fb883917ca361cc45ba8847b2..9e1528445157dadcf8f56444caeb8065e731b845 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -171,6 +171,8 @@ static const struct { {META_SKINCOLOR, "skincolor_t"}, {META_COLORRAMP, "skincolor_t.ramp"}, {META_SPRITEINFO, "spriteinfo_t"}, + {META_SPRITEINFOFRAMELIST,"spriteinfoframe_t[]"}, + {META_SPRITEINFOFRAME,"spriteinfoframe_t"}, {META_PIVOTLIST, "spriteframepivot_t[]"}, {META_FRAMEPIVOT, "spriteframepivot_t"}, diff --git a/src/lua_infolib.c b/src/lua_infolib.c index a65ee23ebc913a62711e9960990c5935662b8dfd..84f787d1e333f7b73792e1ba4de957d6dc683ea8 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -1,7 +1,8 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- +// Copyright (C) 2024 by Kart Krew. // Copyright (C) 2012-2016 by John "JTE" Muniz. -// Copyright (C) 2012-2024 by Sonic Team Junior. +// Copyright (C) 2012-2025 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -236,6 +237,38 @@ static int lib_spr2namelen(lua_State *L) // SPRITE INFO // ///////////////// +struct SpriteInfoFrame { + spriteinfo_t *sprinfo; + UINT16 frame; +}; + +static UINT16 GetSpriteInfoFrameIndex(lua_State *L, int idx) +{ + if (lua_type(L, idx) == LUA_TSTRING) + { + const char *field = luaL_checkstring(L, idx); + + if (fastcmp("default", field)) + { + return SPRINFO_DEFAULT_FRAME; + } + else + { + UINT8 frame = R_Char2Frame(field[0]); + if (frame == 255) + return luaL_error(L, "invalid frame %s", field); + return (UINT16)frame; + } + } + else + { + int frameID = luaL_checknumber(L, idx); + if (frameID < 0 || frameID >= MAXFRAMENUM) + return luaL_error(L, "frame %d out of range (0 - %d)", frameID, MAXFRAMENUM - 1); + return (UINT16)frameID; + } +} + // spriteinfo[] static int lib_getSpriteInfo(lua_State *L) { @@ -263,95 +296,161 @@ static int lib_getSpriteInfo(lua_State *L) #define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to spriteinfo[] (%s)", e); #define TYPEERROR(f, t1, t2) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t1), lua_typename(L, t2))) -static int PopPivotSubTable(spriteframepivot_t *pivot, lua_State *L, int stk, int idx) +static int PopPivotSubTable(spriteinfoframe_t *frame, lua_State *L, int stk); + +static int PopFrameSubTable(spriteinfoframe_t *frame, lua_State *L, int stk) { int okcool = 0; + switch (lua_type(L, stk)) { - case LUA_TTABLE: - lua_pushnil(L); - while (lua_next(L, stk)) + case LUA_TTABLE: + lua_pushnil(L); + while (lua_next(L, stk)) + { + const char *key = luaL_checkstring(L, stk+1); + if (fastcmp(key, "pivot")) { - const char *key = NULL; - lua_Integer ikey = -1; - lua_Integer value = 0; - // x or y? - switch (lua_type(L, stk+1)) - { - case LUA_TSTRING: - key = lua_tostring(L, stk+1); - break; - case LUA_TNUMBER: - ikey = lua_tointeger(L, stk+1); - break; - default: - FIELDERROR("pivot key", va("string or number expected, got %s", luaL_typename(L, stk+1))) - } - // then get value - switch (lua_type(L, stk+2)) - { - case LUA_TNUMBER: - value = lua_tonumber(L, stk+2); - break; - case LUA_TBOOLEAN: - value = (UINT8)lua_toboolean(L, stk+2); - break; - default: - TYPEERROR("pivot value", LUA_TNUMBER, lua_type(L, stk+2)) - } - // finally set omg!!!!!!!!!!!!!!!!!! - if (ikey == 1 || (key && fastcmp(key, "x"))) - pivot[idx].x = (INT32)value; - else if (ikey == 2 || (key && fastcmp(key, "y"))) - pivot[idx].y = (INT32)value; - // TODO: 2.3: Delete - else if (ikey == 3 || (key && fastcmp(key, "rotaxis"))) - LUA_UsageWarning(L, "\"rotaxis\" is deprecated and will be removed.") - else if (ikey == -1 && (key != NULL)) - FIELDERROR("pivot key", va("invalid option %s", key)); - okcool = 1; - lua_pop(L, 1); + if (PopPivotSubTable(frame, L, stk+2)) + okcool = 1; } - break; - default: - TYPEERROR("sprite pivot", LUA_TTABLE, lua_type(L, stk)) + else + { + FIELDERROR("sprite info frame key", va("invalid option %s", key)); + } + + okcool = 1; + + lua_pop(L, 1); + } + break; + default: + TYPEERROR("sprite info frame", LUA_TTABLE, lua_type(L, stk)) } + return okcool; } -static int PopPivotTable(spriteinfo_t *info, lua_State *L, int stk) +static int PopFrameTable(spriteinfo_t *info, lua_State *L, int stk) { - // Just in case? - if (!lua_istable(L, stk)) - TYPEERROR("pivot table", LUA_TTABLE, lua_type(L, stk)); - lua_pushnil(L); - // stk = 0 has the pivot table - // stk = 1 has the frame key - // stk = 2 has the frame table - // stk = 3 has either a string or a number as key - // stk = 4 has the value for the key mentioned above + while (lua_next(L, stk)) { int idx = 0; - const char *framestr = NULL; switch (lua_type(L, stk+1)) { + case LUA_TSTRING: + case LUA_TNUMBER: + idx = GetSpriteInfoFrameIndex(L, stk+1); + break; + default: + TYPEERROR("sprite info frame", LUA_TNUMBER, lua_type(L, stk+1)); + } + + // the values in frames[] are also tables + if (PopFrameSubTable(&info->frames[idx], L, stk+2)) + set_bit_array(info->available, idx); + + lua_pop(L, 1); + } + + return 0; +} + +static int PopPivotSubTable(spriteinfoframe_t *frame, lua_State *L, int stk) +{ + int okcool = 0; + + switch (lua_type(L, stk)) + { + case LUA_TTABLE: + lua_pushnil(L); + while (lua_next(L, stk)) + { + const char *key = NULL; + lua_Integer ikey = -1; + lua_Integer value = 0; + + // x or y? + switch (lua_type(L, stk+1)) + { case LUA_TSTRING: - framestr = lua_tostring(L, stk+1); - idx = R_Char2Frame(framestr[0]); + key = lua_tostring(L, stk+1); break; case LUA_TNUMBER: - idx = lua_tonumber(L, stk+1); + ikey = lua_tointeger(L, stk+1); break; default: - TYPEERROR("pivot frame", LUA_TNUMBER, lua_type(L, stk+1)); + FIELDERROR("pivot key", va("string or number expected, got %s", luaL_typename(L, stk+1))) + } + + // then get value + switch (lua_type(L, stk+2)) + { + case LUA_TNUMBER: + value = lua_tonumber(L, stk+2); + break; + case LUA_TBOOLEAN: + value = (UINT8)lua_toboolean(L, stk+2); + break; + default: + TYPEERROR("pivot value", LUA_TNUMBER, lua_type(L, stk+2)) + } + + // Set it + if (ikey == 1 || (key && fastcmp(key, "x"))) + frame->pivot.x = (INT32)value; + else if (ikey == 2 || (key && fastcmp(key, "y"))) + frame->pivot.y = (INT32)value; + // TODO: 2.3: Delete + else if (ikey == 3 || (key && fastcmp(key, "rotaxis"))) + LUA_UsageWarning(L, "\"rotaxis\" is deprecated and will be removed.") + else if (ikey == -1 && (key != NULL)) + FIELDERROR("pivot key", va("invalid option %s", key)); + + okcool = 1; + + lua_pop(L, 1); + } + break; + default: + TYPEERROR("sprite pivot", LUA_TTABLE, lua_type(L, stk)) + } + + if (okcool) + frame->pivot.available = true; + + return okcool; +} + +static int PopPivotTable(spriteinfo_t *info, lua_State *L, int stk) +{ + lua_pushnil(L); + + while (lua_next(L, stk)) + { + int idx = 0; + switch (lua_type(L, stk+1)) + { + case LUA_TSTRING: + case LUA_TNUMBER: + idx = GetSpriteInfoFrameIndex(L, stk+1); + break; + default: + TYPEERROR("pivot frame", LUA_TNUMBER, lua_type(L, stk+1)); } - if ((idx < 0) || (idx >= MAXFRAMENUM)) - return luaL_error(L, "pivot frame %d out of range (0 - %d)", idx, MAXFRAMENUM - 1); + // the values in pivot[] are also tables - if (PopPivotSubTable(info->pivot, L, stk+2, idx)) - info->available = true; + if (PopPivotSubTable(&info->frames[idx], L, stk+2)) + { + set_bit_array(info->available, idx); + + // TODO: 2.3: Delete + info->frames[SPRINFO_DEFAULT_FRAME].pivot.available = true; + set_bit_array(info->available, SPRINFO_DEFAULT_FRAME); + } + lua_pop(L, 1); } @@ -397,6 +496,14 @@ static int lib_setSpriteInfo(lua_State *L) else FIELDERROR("pivot", va("%s expected, got %s", lua_typename(L, LUA_TTABLE), luaL_typename(L, -1))) } + else if (str && fastcmp(str, "frames")) + { + // frames[] is a table + if (lua_istable(L, 3)) + return PopFrameTable(info, L, 3); + else + FIELDERROR("frames", va("%s expected, got %s", lua_typename(L, LUA_TTABLE), luaL_typename(L, -1))) + } lua_pop(L, 1); } @@ -422,17 +529,26 @@ static int spriteinfo_get(lua_State *L) I_Assert(sprinfo != NULL); // push spriteframepivot_t userdata + // TODO: 2.3: delete if (fastcmp(field, "pivot")) { - // bypass LUA_PushUserdata void **userdata = lua_newuserdata(L, sizeof(void *)); - *userdata = &sprinfo->pivot; + *userdata = sprinfo; luaL_getmetatable(L, META_PIVOTLIST); lua_setmetatable(L, -2); // stack is left with the userdata on top, as if getting it had originally succeeded. return 1; } + else if (fastcmp(field, "frames")) + { + void **userdata = lua_newuserdata(L, sizeof(void *)); + *userdata = sprinfo; + luaL_getmetatable(L, META_SPRITEINFOFRAMELIST); + lua_setmetatable(L, -2); + + return 1; + } else return luaL_error(L, LUA_QL("spriteinfo_t") " has no field named " LUA_QS, field); @@ -465,9 +581,28 @@ static int spriteinfo_set(lua_State *L) // pivot[] is userdata else if (lua_isuserdata(L, 1)) { - spriteframepivot_t *pivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_PIVOTLIST)); - memcpy(&sprinfo->pivot, pivot, sizeof(spriteframepivot_t)); - sprinfo->available = true; // Just in case? + spriteinfo_t *copyinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_PIVOTLIST)); + for (UINT16 i = 0; i <= MAXFRAMENUM; i++) + { + if (in_bit_array(copyinfo->available, i)) + memcpy(&sprinfo->frames[i].pivot, ©info->frames[i].pivot, sizeof(spriteframepivot_t)); + } + } + } + else if (fastcmp(field, "frames")) + { + // frames[] is a table + if (lua_istable(L, 1)) + return PopFrameTable(sprinfo, L, 1); + // frames[] is userdata + else if (lua_isuserdata(L, 1)) + { + spriteinfo_t *copyinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFOFRAMELIST)); + for (UINT16 i = 0; i <= MAXFRAMENUM; i++) + { + if (in_bit_array(copyinfo->available, i)) + memcpy(&sprinfo->frames[i], ©info->frames[i], sizeof(spriteinfoframe_t)); + } } } else @@ -487,21 +622,138 @@ static int spriteinfo_num(lua_State *L) return 1; } -// framepivot_t -static int pivotlist_get(lua_State *L) +// spriteinfoframe_t +static int framelist_get(lua_State *L) +{ + struct SpriteInfoFrame *container; + spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFOFRAMELIST)); + UINT16 frame = GetSpriteInfoFrameIndex(L, 2); + + container = lua_newuserdata(L, sizeof *container); + container->sprinfo = sprinfo; + container->frame = frame; + luaL_getmetatable(L, META_SPRITEINFOFRAME); + lua_setmetatable(L, -2); + + // stack is left with the userdata on top, as if getting it had originally succeeded. + return 1; +} + +static int framelist_set(lua_State *L) +{ + spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_SPRITEINFOFRAMELIST)); + UINT16 frame; + int okcool = 0; + + if (!lua_lumploading) + return luaL_error(L, "Do not alter spriteinfoframe_t from within a hook or coroutine!"); + if (hud_running) + return luaL_error(L, "Do not alter spriteinfoframe_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter spriteinfoframe_t in CMD building code!"); + + frame = GetSpriteInfoFrameIndex(L, 2); + + // frames[] is a table + if (lua_istable(L, 3)) + okcool = PopFrameSubTable(&sprinfo->frames[frame], L, 3); + // frames[] is userdata + else if (lua_isuserdata(L, 3)) + { + struct SpriteInfoFrame *container = luaL_checkudata(L, 3, META_SPRITEINFOFRAME); + memcpy(&sprinfo->frames[frame], + &container->sprinfo->frames[container->frame], + sizeof(spriteinfoframe_t)); + okcool = 1; + } + + if (okcool) + set_bit_array(sprinfo->available, frame); + + return 0; +} + +static int framelist_num(lua_State *L) +{ + lua_pushinteger(L, MAXFRAMENUM); + return 1; +} + +// spriteinfoframe_t +static int sprinfoframe_get(lua_State *L) { - void **userdata; - spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_PIVOTLIST)); + struct SpriteInfoFrame *container = luaL_checkudata(L, 1, META_SPRITEINFOFRAME); const char *field = luaL_checkstring(L, 2); - UINT8 frame; - frame = R_Char2Frame(field[0]); - if (frame == 255) - luaL_error(L, "invalid frame %s", field); + if (fastcmp("pivot", field)) + { + struct SpriteInfoFrame *other_container = lua_newuserdata(L, sizeof *container); + memcpy(other_container, container, sizeof *container); + luaL_getmetatable(L, META_FRAMEPIVOT); + lua_setmetatable(L, -2); + } + else + return luaL_error(L, "Field %s does not exist in spriteinfoframe_t", field); + + return 1; +} + +static int sprinfoframe_set(lua_State *L) +{ + struct SpriteInfoFrame *container = luaL_checkudata(L, 1, META_SPRITEINFOFRAME); + spriteinfoframe_t *frame = &container->sprinfo->frames[container->frame]; + UINT8 *available = container->sprinfo->available; + const char *field = luaL_checkstring(L, 2); + boolean is_available = false; + + if (!lua_lumploading) + return luaL_error(L, "Do not alter spriteinfoframe_t from within a hook or coroutine!"); + if (hud_running) + return luaL_error(L, "Do not alter spriteinfoframe_t in HUD rendering code!"); + if (hook_cmd_running) + return luaL_error(L, "Do not alter spriteinfoframe_t in CMD building code!"); + + I_Assert(frame != NULL); + + if (fastcmp("pivot", field)) + { + // pivot[] is a table + if (lua_istable(L, 3)) + { + if (PopPivotSubTable(frame, L, 3)) + is_available = true; + } + // pivot[] is userdata + else if (lua_isuserdata(L, 3)) + { + struct SpriteInfoFrame *other_container = luaL_checkudata(L, 3, META_FRAMEPIVOT); + memcpy(&frame->pivot, + &other_container->sprinfo->frames[other_container->frame].pivot, + sizeof(spriteframepivot_t)); + is_available = true; + } + } + else + return luaL_error(L, "Field %s does not exist in spriteframepivot_t", field); + + if (is_available) + { + set_bit_array(available, container->frame); + } + + return 0; +} - // bypass LUA_PushUserdata - userdata = lua_newuserdata(L, sizeof(void *)); - *userdata = &framepivot[frame]; +// framepivot_t +static int pivotlist_get(lua_State *L) +{ + struct SpriteInfoFrame *container; + spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_PIVOTLIST)); + UINT16 frame = GetSpriteInfoFrameIndex(L, 2); + + container = lua_newuserdata(L, sizeof *container); + container->sprinfo = sprinfo; + container->frame = frame; luaL_getmetatable(L, META_FRAMEPIVOT); lua_setmetatable(L, -2); @@ -511,11 +763,9 @@ static int pivotlist_get(lua_State *L) static int pivotlist_set(lua_State *L) { - // Because I already know it's a spriteframepivot_t anyway - spriteframepivot_t *pivotlist = *((spriteframepivot_t **)lua_touserdata(L, 1)); - //spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT)); - const char *field = luaL_checkstring(L, 2); - UINT8 frame; + spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_PIVOTLIST)); + UINT16 frame; + int okcool = 0; if (!lua_lumploading) return luaL_error(L, "Do not alter spriteframepivot_t from within a hook or coroutine!"); @@ -524,20 +774,24 @@ static int pivotlist_set(lua_State *L) if (hook_cmd_running) return luaL_error(L, "Do not alter spriteframepivot_t in CMD building code!"); - frame = R_Char2Frame(field[0]); - if (frame == 255) - luaL_error(L, "invalid frame %s", field); + frame = GetSpriteInfoFrameIndex(L, 2); // pivot[] is a table if (lua_istable(L, 3)) - return PopPivotSubTable(pivotlist, L, 3, frame); + okcool = PopPivotSubTable(&sprinfo->frames[frame], L, 3); // pivot[] is userdata else if (lua_isuserdata(L, 3)) { - spriteframepivot_t *copypivot = *((spriteframepivot_t **)luaL_checkudata(L, 3, META_FRAMEPIVOT)); - memcpy(&pivotlist[frame], copypivot, sizeof(spriteframepivot_t)); + struct SpriteInfoFrame *container = luaL_checkudata(L, 3, META_FRAMEPIVOT); + memcpy(&sprinfo->frames[frame].pivot, + &container->sprinfo->frames[container->frame].pivot, + sizeof(spriteframepivot_t)); + okcool = 1; } + if (okcool) + set_bit_array(sprinfo->available, frame); + return 0; } @@ -549,7 +803,8 @@ static int pivotlist_num(lua_State *L) static int framepivot_get(lua_State *L) { - spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT)); + struct SpriteInfoFrame *container = luaL_checkudata(L, 1, META_FRAMEPIVOT); + spriteframepivot_t *framepivot = &container->sprinfo->frames[container->frame].pivot; const char *field = luaL_checkstring(L, 2); I_Assert(framepivot != NULL); @@ -572,7 +827,9 @@ static int framepivot_get(lua_State *L) static int framepivot_set(lua_State *L) { - spriteframepivot_t *framepivot = *((spriteframepivot_t **)luaL_checkudata(L, 1, META_FRAMEPIVOT)); + struct SpriteInfoFrame *container = luaL_checkudata(L, 1, META_FRAMEPIVOT); + spriteframepivot_t *framepivot = &container->sprinfo->frames[container->frame].pivot; + UINT8 *available = container->sprinfo->available; const char *field = luaL_checkstring(L, 2); if (!lua_lumploading) @@ -585,9 +842,15 @@ static int framepivot_set(lua_State *L) I_Assert(framepivot != NULL); if (fastcmp("x", field)) + { framepivot->x = luaL_checkinteger(L, 3); + set_bit_array(available, container->frame); + } else if (fastcmp("y", field)) + { framepivot->y = luaL_checkinteger(L, 3); + set_bit_array(available, container->frame); + } // TODO: 2.3: delete else if (fastcmp("rotaxis", field)) LUA_UsageWarning(L, "\"rotaxis\" is deprecated and will be removed.") @@ -1909,6 +2172,8 @@ int LUA_InfoLib(lua_State *L) LUA_RegisterUserdataMetatable(L, META_COLORRAMP, colorramp_get, colorramp_set, colorramp_len); LUA_RegisterUserdataMetatable(L, META_SFXINFO, sfxinfo_get, sfxinfo_set, sfxinfo_num); LUA_RegisterUserdataMetatable(L, META_SPRITEINFO, spriteinfo_get, spriteinfo_set, spriteinfo_num); + LUA_RegisterUserdataMetatable(L, META_SPRITEINFOFRAMELIST, framelist_get, framelist_set, framelist_num); + LUA_RegisterUserdataMetatable(L, META_SPRITEINFOFRAME, sprinfoframe_get, sprinfoframe_set, NULL); LUA_RegisterUserdataMetatable(L, META_PIVOTLIST, pivotlist_get, pivotlist_set, pivotlist_num); LUA_RegisterUserdataMetatable(L, META_FRAMEPIVOT, framepivot_get, framepivot_set, framepivot_num); LUA_RegisterUserdataMetatable(L, META_LUABANKS, lib_getluabanks, lib_setluabanks, lib_luabankslen); diff --git a/src/lua_libs.h b/src/lua_libs.h index e1585f488a8a6205b1ee967dcfeffac125680fee..b6e61456a8bb5efd2ef7579ad862f2eb0eaf83df 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -29,6 +29,8 @@ extern boolean ignoregameinputs; #define META_SKINCOLOR "SKINCOLOR_T*" #define META_COLORRAMP "SKINCOLOR_T*RAMP" #define META_SPRITEINFO "SPRITEINFO_T*" +#define META_SPRITEINFOFRAMELIST "SPRITEINFOFRAME_T[]" +#define META_SPRITEINFOFRAME "SPRITEINFOFRAME_T*" #define META_PIVOTLIST "SPRITEFRAMEPIVOT_T[]" #define META_FRAMEPIVOT "SPRITEFRAMEPIVOT_T*" diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c index 989665d74179854fcfac552814132671c3d00ed2..8df10848243d6e8eb676a6f85c0880fa51ccd25b 100644 --- a/src/r_patchrotation.c +++ b/src/r_patchrotation.c @@ -63,6 +63,16 @@ patch_t *Patch_GetRotated(patch_t *patch, INT32 angle, boolean flip) return rotsprite->patches[angle]; } +static spriteframepivot_t *GetSpriteInfoRotationPivot(spriteinfo_t *info, UINT16 frame) +{ + if (R_IsSpriteInfoAvailable(info, frame) && info->frames[frame].pivot.available) + { + return &info->frames[frame].pivot; + } + + return NULL; +} + patch_t *Patch_GetRotatedSprite( spriteframe_t *sprite, size_t frame, size_t spriteangle, @@ -90,16 +100,21 @@ patch_t *Patch_GetRotatedSprite( patch_t *patch; INT32 xpivot = 0, ypivot = 0; lumpnum_t lump = sprite->lumppat[spriteangle]; + spriteframepivot_t *pivot; if (lump == LUMPERROR) return NULL; patch = W_CachePatchNum(lump, PU_SPRITE); - if (sprinfo->available) + pivot = GetSpriteInfoRotationPivot(sprinfo, frame); + if (pivot == NULL) + pivot = GetSpriteInfoRotationPivot(sprinfo, SPRINFO_DEFAULT_FRAME); + + if (pivot != NULL) { - xpivot = sprinfo->pivot[frame].x; - ypivot = sprinfo->pivot[frame].y; + xpivot = pivot->x; + ypivot = pivot->y; } else { diff --git a/src/r_picformats.c b/src/r_picformats.c index a45c143b01698de2abbb48f534f543a3a68d845e..94452e20e8a2e660cf228142e4afe945cb40ac77 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -2,8 +2,9 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 2005-2009 by Andrey "entryway" Budko. -// Copyright (C) 2018-2024 by Lactozilla. -// Copyright (C) 2019-2024 by Sonic Team Junior. +// Copyright (C) 2024 by Kart Krew. +// Copyright (C) 2018-2025 by Lactozilla. +// Copyright (C) 2019-2025 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -1479,36 +1480,184 @@ boolean Picture_PNGDimensions(UINT8 *png, INT32 *width, INT32 *height, INT16 *to #endif #endif -// -// R_ParseSpriteInfoFrame -// -// Parse a SPRTINFO frame. -// -static void R_ParseSpriteInfoFrame(spriteinfo_t *info) +struct ParseSpriteInfoState { + boolean spr2; + spriteinfo_t *info; + spritenum_t sprnum; + playersprite_t spr2num; + boolean any; + INT32 skinnumbers[MAXSKINS]; + INT32 foundskins; +}; + +#define PARSER_FRAME (false) +#define PARSER_DEFAULT (true) + +static void R_ParseSpriteInfoSkin(struct ParseSpriteInfoState *parser) { char *sprinfoToken; size_t sprinfoTokenLength; - char *frameChar = NULL; - UINT8 frameFrame = 0xFF; - INT16 frameXPivot = 0; - INT16 frameYPivot = 0; - // Sprite identifier + INT32 skinnum; + char *skinName = NULL; + + // Skin name sprinfoToken = M_GetToken(NULL); if (sprinfoToken == NULL) { - I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite frame should be"); + I_Error("Error parsing SPRTINFO lump: Unexpected end of file where skin frame should be"); } - sprinfoTokenLength = strlen(sprinfoToken); - if (sprinfoTokenLength != 1) + + if (strcmp(sprinfoToken, "*")==0) // All skins { - I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",sprinfoToken); + parser->foundskins = -1; } else - frameChar = sprinfoToken; + { + // copy skin name yada yada + sprinfoTokenLength = strlen(sprinfoToken); + skinName = (char *)Z_Malloc((sprinfoTokenLength+1)*sizeof(char),PU_STATIC,NULL); + M_Memcpy(skinName,sprinfoToken,sprinfoTokenLength*sizeof(char)); + skinName[sprinfoTokenLength] = '\0'; + strlwr(skinName); + + skinnum = R_SkinAvailable(skinName); + if (skinnum == -1) + I_Error("Error parsing SPRTINFO lump: Unknown skin \"%s\"", skinName); + + parser->skinnumbers[parser->foundskins] = skinnum; + parser->foundskins++; + } - frameFrame = R_Char2Frame(frameChar[0]); Z_Free(sprinfoToken); +} + +static void copy_to_skin (struct ParseSpriteInfoState *parser, INT32 skinnum) +{ + skin_t *skin = skins[skinnum]; + spriteinfo_t *sprinfo = skin->sprinfo; + + if (parser->any) + { + playersprite_t spr2num; + + for (spr2num = 0; spr2num < NUMPLAYERSPRITES; ++spr2num) + { + M_Memcpy(&sprinfo[spr2num], parser->info, sizeof(spriteinfo_t)); + } + } + else + { + M_Memcpy(&sprinfo[parser->spr2num], parser->info, sizeof(spriteinfo_t)); + } +} + +struct ParsedSpriteInfoFrame { + INT32 pivotX; + INT32 pivotY; +}; + +static boolean define_spriteinfo_frame(struct ParsedSpriteInfoFrame *frame, spriteinfoframe_t *dest) +{ + boolean defined = false; + + if (frame->pivotX != INT32_MAX) + { + dest->pivot.x = frame->pivotX; + dest->pivot.available = true; + defined = true; + } + if (frame->pivotY != INT32_MAX) + { + dest->pivot.y = frame->pivotY; + dest->pivot.available = true; + defined = true; + } + + return defined; +} + +static void R_ParseSpriteInfoFrame(struct ParseSpriteInfoState *parser, boolean all) +{ + char *sprinfoToken; + UINT16 frameID = 0; + UINT16 frameEndID = UINT16_MAX; + + boolean usingDeprecatedPivot = false; + + struct ParsedSpriteInfoFrame frame = { + .pivotX = INT32_MAX, + .pivotY = INT32_MAX + }; + + if (all) + { + frameID = SPRINFO_DEFAULT_FRAME; + } + else + { + // Sprite identifier + char *frameToken = NULL; + char *startRange = NULL; + char *endRange = NULL; + sprinfoToken = M_GetToken(NULL); + if (sprinfoToken == NULL) + { + I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite frame should be"); + } + + // Parse range + frameToken = Z_StrDup(sprinfoToken); + startRange = frameToken; + endRange = strstr(frameToken, ".."); + if (endRange != NULL) + { + *endRange = '\0'; + endRange += 2; + if (strstr(endRange, ".")) + I_Error("Error parsing SPRTINFO lump: Invalid range \"%s\"",sprinfoToken); + } + + int parseStartFrameID = -1; + if (!M_StringToNumber(startRange, &parseStartFrameID)) + { + if (strlen(startRange) != 1) + I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",startRange); + parseStartFrameID = R_Char2Frame(startRange[0]); + if (parseStartFrameID == 255) + I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",startRange); + } + if (parseStartFrameID < 0 || parseStartFrameID >= MAXFRAMENUM) + I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",startRange); + frameID = (UINT16)parseStartFrameID; + + // Parse range ID + if (endRange != NULL) + { + int parseEndFrameID = -1; + if (!M_StringToNumber(endRange, &parseEndFrameID)) + { + if (strlen(endRange) != 1) + I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",endRange); + parseEndFrameID = R_Char2Frame(endRange[0]); + if (parseEndFrameID == 255) + I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",endRange); + } + if (parseEndFrameID < 0 || parseEndFrameID >= MAXFRAMENUM) + I_Error("Error parsing SPRTINFO lump: Invalid frame \"%s\"",endRange); + frameEndID = (UINT16)parseEndFrameID; + } + + Z_Free(frameToken); + + // Validate the range + if (frameEndID != UINT16_MAX && frameID >= frameEndID) + { + I_Error("Error parsing SPRTINFO lump: Invalid range \"%s\"",sprinfoToken); + } + + Z_Free(sprinfoToken); + } // Left Curly Brace sprinfoToken = M_GetToken(NULL); @@ -1526,18 +1675,35 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info) } while (strcmp(sprinfoToken,"}")!=0) { - if (stricmp(sprinfoToken, "XPIVOT")==0) + if (stricmp(sprinfoToken, "ROTATIONXPIVOT")==0) + { + Z_Free(sprinfoToken); + sprinfoToken = M_GetToken(NULL); + frame.pivotX = atoi(sprinfoToken); + } + else if (stricmp(sprinfoToken, "ROTATIONYPIVOT")==0) + { + Z_Free(sprinfoToken); + sprinfoToken = M_GetToken(NULL); + frame.pivotY = atoi(sprinfoToken); + } + // TODO: 2.3: Delete + else if (stricmp(sprinfoToken, "XPIVOT")==0) { Z_Free(sprinfoToken); sprinfoToken = M_GetToken(NULL); - frameXPivot = atoi(sprinfoToken); + frame.pivotX = atoi(sprinfoToken); + usingDeprecatedPivot = true; } + // TODO: 2.3: Delete else if (stricmp(sprinfoToken, "YPIVOT")==0) { Z_Free(sprinfoToken); sprinfoToken = M_GetToken(NULL); - frameYPivot = atoi(sprinfoToken); + frame.pivotY = atoi(sprinfoToken); + usingDeprecatedPivot = true; } + // TODO: 2.3: Delete else if (stricmp(sprinfoToken, "ROTAXIS")==0) { Z_Free(sprinfoToken); @@ -1555,9 +1721,70 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info) Z_Free(sprinfoToken); } - // set fields - info->pivot[frameFrame].x = frameXPivot; - info->pivot[frameFrame].y = frameYPivot; + // Apply to the specified range of frames + if (frameEndID != UINT16_MAX) + { + for (UINT16 frameIter = frameID; frameIter <= frameEndID; frameIter++) + { + if (define_spriteinfo_frame(&frame, &parser->info->frames[frameIter])) + { + set_bit_array(parser->info->available, frameIter); + } + } + } + else + { + if (define_spriteinfo_frame(&frame, &parser->info->frames[frameID])) + { + set_bit_array(parser->info->available, frameID); + } + } + + // TODO: 2.3: Delete + if (usingDeprecatedPivot) + { + parser->info->frames[SPRINFO_DEFAULT_FRAME].pivot.available = true; + set_bit_array(parser->info->available, SPRINFO_DEFAULT_FRAME); + } + + if (parser->spr2) + { + INT32 i; + + if (!parser->foundskins) + I_Error("Error parsing SPRTINFO lump: No skins specified in this sprite2 definition"); + + if (parser->foundskins < 0) + { + for (i = 0; i < numskins; i++) + { + copy_to_skin(parser, i); + } + } + else + { + for (i = 0; i < parser->foundskins; i++) + { + copy_to_skin(parser, parser->skinnumbers[i]); + } + } + } + else + { + if (parser->any) + { + spritenum_t sprnum; + + for (sprnum = 0; sprnum < NUMSPRITES; ++sprnum) + { + M_Memcpy(&spriteinfo[sprnum], parser->info, sizeof(spriteinfo_t)); + } + } + else + { + M_Memcpy(&spriteinfo[parser->sprnum], parser->info, sizeof(spriteinfo_t)); + } + } } // @@ -1567,15 +1794,19 @@ static void R_ParseSpriteInfoFrame(spriteinfo_t *info) // static void R_ParseSpriteInfo(boolean spr2) { - spriteinfo_t *info; char *sprinfoToken; size_t sprinfoTokenLength; char newSpriteName[MAXSPRITENAME + 1]; // no longer dynamically allocated - spritenum_t sprnum = NUMSPRITES; - playersprite_t spr2num = NUMPLAYERSPRITES; + + struct ParseSpriteInfoState parser = { + .spr2 = spr2, + .sprnum = NUMSPRITES, + .spr2num = NUMPLAYERSPRITES, + .any = false, + .foundskins = 0, + }; + INT32 i; - UINT8 *skinnumbers = NULL; - INT32 foundskins = 0; // Sprite name sprinfoToken = M_GetToken(NULL); @@ -1583,17 +1814,28 @@ static void R_ParseSpriteInfo(boolean spr2) { I_Error("Error parsing SPRTINFO lump: Unexpected end of file where sprite name should be"); } - sprinfoTokenLength = strlen(sprinfoToken); - if (sprinfoTokenLength > MAXSPRITENAME) - I_Error("Error parsing SPRTINFO lump: Sprite name \"%s\" is longer than %d characters", sprinfoToken, MAXSPRITENAME); - strcpy(newSpriteName, sprinfoToken); - strupr(newSpriteName); // Just do this now so we don't have to worry about it + + if (!strcmp(sprinfoToken, "*")) // All sprites + { + parser.any = true; + } + else + { + sprinfoTokenLength = strlen(sprinfoToken); + if (sprinfoTokenLength > MAXSPRITENAME) + I_Error("Error parsing SPRTINFO lump: Sprite name \"%s\" is longer than %d characters", sprinfoToken, MAXSPRITENAME); + strcpy(newSpriteName, sprinfoToken); + strupr(newSpriteName); // Just do this now so we don't have to worry about it + } + Z_Free(sprinfoToken); - if (!spr2) + if (parser.any) + ; + else if (!spr2) { - sprnum = R_GetSpriteNumByName(newSpriteName); - if (sprnum == NUMSPRITES) + parser.sprnum = R_GetSpriteNumByName(newSpriteName); + if (parser.sprnum == NUMSPRITES) I_Error("Error parsing SPRTINFO lump: Unknown sprite name \"%s\"", newSpriteName); } else @@ -1604,15 +1846,14 @@ static void R_ParseSpriteInfo(boolean spr2) I_Error("Error parsing SPRTINFO lump: Unknown sprite2 name \"%s\"", newSpriteName); if (!memcmp(newSpriteName,spr2names[i],4)) { - spr2num = i; + parser.spr2num = i; break; } } } // allocate a spriteinfo - info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL); - info->available = true; + parser.info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL); // Left Curly Brace sprinfoToken = M_GetToken(NULL); @@ -1632,54 +1873,22 @@ static void R_ParseSpriteInfo(boolean spr2) { if (stricmp(sprinfoToken, "SKIN")==0) { - INT32 skinnum; - char *skinName = NULL; if (!spr2) I_Error("Error parsing SPRTINFO lump: \"SKIN\" token found outside of a sprite2 definition"); Z_Free(sprinfoToken); - // Skin name - sprinfoToken = M_GetToken(NULL); - if (sprinfoToken == NULL) - { - I_Error("Error parsing SPRTINFO lump: Unexpected end of file where skin frame should be"); - } - - // copy skin name yada yada - sprinfoTokenLength = strlen(sprinfoToken); - skinName = (char *)Z_Malloc((sprinfoTokenLength+1)*sizeof(char),PU_STATIC,NULL); - M_Memcpy(skinName,sprinfoToken,sprinfoTokenLength*sizeof(char)); - skinName[sprinfoTokenLength] = '\0'; - strlwr(skinName); - Z_Free(sprinfoToken); - - skinnum = R_SkinAvailable(skinName); - if (skinnum == -1) - I_Error("Error parsing SPRTINFO lump: Unknown skin \"%s\"", skinName); - - if (skinnumbers == NULL) - skinnumbers = Z_Malloc(sizeof(UINT8) * numskins, PU_STATIC, NULL); - skinnumbers[foundskins] = (UINT8)skinnum; - foundskins++; + R_ParseSpriteInfoSkin(&parser); } else if (stricmp(sprinfoToken, "FRAME")==0) { - R_ParseSpriteInfoFrame(info); Z_Free(sprinfoToken); - if (spr2) - { - if (!foundskins) - I_Error("Error parsing SPRTINFO lump: No skins specified in this sprite2 definition"); - for (i = 0; i < foundskins; i++) - { - skin_t *skin = skins[skinnumbers[i]]; - spriteinfo_t *sprinfo = skin->sprinfo; - M_Memcpy(&sprinfo[spr2num], info, sizeof(spriteinfo_t)); - } - } - else - M_Memcpy(&spriteinfo[sprnum], info, sizeof(spriteinfo_t)); + R_ParseSpriteInfoFrame(&parser, PARSER_FRAME); + } + else if (stricmp(sprinfoToken, "DEFAULT")==0) + { + Z_Free(sprinfoToken); + R_ParseSpriteInfoFrame(&parser, PARSER_DEFAULT); } else { @@ -1699,9 +1908,7 @@ static void R_ParseSpriteInfo(boolean spr2) } Z_Free(sprinfoToken); - Z_Free(info); - if (skinnumbers) - Z_Free(skinnumbers); + Z_Free(parser.info); } // @@ -1767,3 +1974,8 @@ void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps) R_ParseSPRTINFOLump(wadnum, i); } } + +boolean R_IsSpriteInfoAvailable(spriteinfo_t *info, UINT16 frame) +{ + return info && frame <= SPRINFO_DEFAULT_FRAME && in_bit_array(info->available, frame); +} diff --git a/src/r_picformats.h b/src/r_picformats.h index 123dda976c204787505fb1a90bad859fe39b836b..9c6d788b22d8ed68422168f2d8f988374a0ae65b 100644 --- a/src/r_picformats.h +++ b/src/r_picformats.h @@ -1,8 +1,8 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. -// Copyright (C) 2018-2024 by Lactozilla. -// Copyright (C) 2019-2024 by Sonic Team Junior. +// Copyright (C) 2018-2025 by Lactozilla. +// Copyright (C) 2019-2025 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -96,12 +96,20 @@ typedef enum typedef struct { INT32 x, y; + boolean available; } spriteframepivot_t; typedef struct { - spriteframepivot_t pivot[MAXFRAMENUM]; - boolean available; + spriteframepivot_t pivot; +} spriteinfoframe_t; + +#define SPRINFO_DEFAULT_FRAME (MAXFRAMENUM) + +typedef struct +{ + UINT8 available[BIT_ARRAY_SIZE(MAXFRAMENUM + 1)]; // 1 extra for default_frame + spriteinfoframe_t frames[MAXFRAMENUM + 1]; } spriteinfo_t; // PNG support @@ -126,4 +134,6 @@ extern spriteinfo_t spriteinfo[NUMSPRITES]; void R_LoadSpriteInfoLumps(UINT16 wadnum, UINT16 numlumps); void R_ParseSPRTINFOLump(UINT16 wadNum, UINT16 lumpNum); +boolean R_IsSpriteInfoAvailable(spriteinfo_t *info, UINT16 frame); + #endif // __R_PICFORMATS__