diff --git a/src/deh_soc.c b/src/deh_soc.c index 001c6d2896f1109ada545b3dd0625bd92479c79c..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->frames[frame].pivot.x = value; + available = true; + } else if (fastcmp(word, "YPIVOT")) + { 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); } @@ -1074,6 +1085,11 @@ 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 44ced92e941b1578f78bd17591c384ee3d1b275c..d241dca3cd7260b4e63275e43c1dc27def209037 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -236,12 +236,12 @@ static int lib_spr2namelen(lua_State *L) // SPRITE INFO // ///////////////// -struct PivotFrame { +struct SpriteInfoFrame { spriteinfo_t *sprinfo; UINT16 frame; }; -static UINT16 GetSpriteInfoFrame(lua_State *L, int idx) +static UINT16 GetSpriteInfoFrameIndex(lua_State *L, int idx) { if (lua_type(L, idx) == LUA_TSTRING) { @@ -295,89 +295,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(spriteinfo_t *info, 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)) - } - // Set it - if (ikey == 1 || (key && fastcmp(key, "x"))) - info->frames[idx].pivot.x = (INT32)value; - else if (ikey == 2 || (key && fastcmp(key, "y"))) - info->frames[idx].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); + 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; 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: + 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: - idx = GetSpriteInfoFrame(L, stk+1); + value = lua_tonumber(L, stk+2); + break; + case LUA_TBOOLEAN: + value = (UINT8)lua_toboolean(L, stk+2); break; default: - TYPEERROR("pivot frame", LUA_TNUMBER, lua_type(L, stk+1)); + 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)); } + // the values in pivot[] are also tables - if (PopPivotSubTable(info, L, stk+2, idx)) + 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); } @@ -423,6 +495,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); } @@ -448,9 +528,9 @@ 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; luaL_getmetatable(L, META_PIVOTLIST); @@ -459,6 +539,15 @@ static int spriteinfo_get(lua_State *L) // 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); @@ -492,7 +581,27 @@ static int spriteinfo_set(lua_State *L) else if (lua_isuserdata(L, 1)) { spriteinfo_t *copyinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_PIVOTLIST)); - memcpy(sprinfo, copyinfo, sizeof(spriteinfo_t)); + 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 @@ -512,14 +621,138 @@ static int spriteinfo_num(lua_State *L) return 1; } +// 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) +{ + struct SpriteInfoFrame *container = luaL_checkudata(L, 1, META_SPRITEINFOFRAME); + spriteinfoframe_t *frame = &container->sprinfo->frames[container->frame]; + const char *field = luaL_checkstring(L, 2); + + I_Assert(frame != NULL); + + 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; +} + // framepivot_t static int pivotlist_get(lua_State *L) { - struct PivotFrame *container; + struct SpriteInfoFrame *container; spriteinfo_t *sprinfo = *((spriteinfo_t **)luaL_checkudata(L, 1, META_PIVOTLIST)); - UINT16 frame = GetSpriteInfoFrame(L, 2); + UINT16 frame = GetSpriteInfoFrameIndex(L, 2); - // bypass LUA_PushUserdata container = lua_newuserdata(L, sizeof *container); container->sprinfo = sprinfo; container->frame = frame; @@ -543,15 +776,15 @@ 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 = GetSpriteInfoFrame(L, 2); + frame = GetSpriteInfoFrameIndex(L, 2); // pivot[] is a table if (lua_istable(L, 3)) - okcool = PopPivotSubTable(sprinfo, L, 3, frame); + okcool = PopPivotSubTable(&sprinfo->frames[frame], L, 3); // pivot[] is userdata else if (lua_isuserdata(L, 3)) { - struct PivotFrame *container = luaL_checkudata(L, 3, META_FRAMEPIVOT); + struct SpriteInfoFrame *container = luaL_checkudata(L, 3, META_FRAMEPIVOT); memcpy(&sprinfo->frames[frame].pivot, &container->sprinfo->frames[container->frame].pivot, sizeof(spriteframepivot_t)); @@ -572,7 +805,7 @@ static int pivotlist_num(lua_State *L) static int framepivot_get(lua_State *L) { - struct PivotFrame *container = 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); @@ -596,7 +829,7 @@ static int framepivot_get(lua_State *L) static int framepivot_set(lua_State *L) { - struct PivotFrame *container = 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); @@ -1941,6 +2174,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 a84f0ad4a41e3d356012a0c9ef8801d8874abf04..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,21 +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 (R_IsSpriteInfoAvailable(sprinfo, frame)) - { - xpivot = sprinfo->frames[frame].pivot.x; - ypivot = sprinfo->frames[frame].pivot.y; - } - else if (R_IsSpriteInfoAvailable(sprinfo, SPRINFO_DEFAULT_FRAME)) + pivot = GetSpriteInfoRotationPivot(sprinfo, frame); + if (pivot == NULL) + pivot = GetSpriteInfoRotationPivot(sprinfo, SPRINFO_DEFAULT_FRAME); + + if (pivot != NULL) { - xpivot = sprinfo->frames[SPRINFO_DEFAULT_FRAME].pivot.x; - ypivot = sprinfo->frames[SPRINFO_DEFAULT_FRAME].pivot.y; + xpivot = pivot->x; + ypivot = pivot->y; } else { diff --git a/src/r_picformats.c b/src/r_picformats.c index 84fc980a3a76f822569a85785b32b1bf08fad1e9..645b5c1e6e860704b1139b820259e730a1bd1876 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -1563,11 +1563,13 @@ static boolean define_spriteinfo_frame(struct ParsedSpriteInfoFrame *frame, spri 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; } @@ -1682,6 +1684,7 @@ static void R_ParseSpriteInfoFrame(struct ParseSpriteInfoState *parser, boolean sprinfoToken = M_GetToken(NULL); frame.pivotY = atoi(sprinfoToken); } + // TODO: 2.3: Delete else if (stricmp(sprinfoToken, "ROTAXIS")==0) { Z_Free(sprinfoToken); diff --git a/src/r_picformats.h b/src/r_picformats.h index b07b6a9485830198eafe1bd9db181850159e0e20..9c6d788b22d8ed68422168f2d8f988374a0ae65b 100644 --- a/src/r_picformats.h +++ b/src/r_picformats.h @@ -96,6 +96,7 @@ typedef enum typedef struct { INT32 x, y; + boolean available; } spriteframepivot_t; typedef struct