diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 81c871de6..cc9a5acac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -144,6 +144,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 k_vote.c k_serverstats.c k_zvote.c + k_mapuser.c ) if(SRB2_CONFIG_ENABLE_WEBM_MOVIES) diff --git a/src/acs/call-funcs.cpp b/src/acs/call-funcs.cpp index 154a5c37a..1f9904ccd 100644 --- a/src/acs/call-funcs.cpp +++ b/src/acs/call-funcs.cpp @@ -41,6 +41,7 @@ #include "../r_skins.h" #include "../k_battle.h" #include "../k_podium.h" +#include "../z_zone.h" #include "call-funcs.hpp" @@ -135,6 +136,173 @@ static bool ACS_GetSFXFromString(const char *word, sfxenum_t *type) return false; } +/*-------------------------------------------------- + static bool ACS_GetSpriteFromString(const char *word, spritenum_t *type) + + Helper function for CallFunc_Get/SetThingProperty. + Gets a sprite from a string. + + Input Arguments:- + word: The sprite string. + type: Variable to store the result in. + + Return:- + true if successful, otherwise false. +--------------------------------------------------*/ +static bool ACS_GetSpriteFromString(const char *word, spritenum_t *type) +{ + if (fastncmp("SPR_", word, 4)) + { + // take off the SPR_ + word += 4; + } + + for (int i = 0; i < NUMSPRITES; i++) + { + if (fastncmp(word, sprnames[i], 4)) + { + *type = static_cast(i); + return true; + } + } + + return false; +} + +/*-------------------------------------------------- + static bool ACS_GetSprite2FromString(const char *word, playersprite_t *type) + + Helper function for CallFunc_Get/SetThingProperty. + Gets a sprite2 from a string. + + Input Arguments:- + word: The sprite2 string. + type: Variable to store the result in. + + Return:- + true if successful, otherwise false. +--------------------------------------------------*/ +static bool ACS_GetSprite2FromString(const char *word, playersprite_t *type) +{ + if (fastncmp("SPR2_", word, 5)) + { + // take off the SPR2_ + word += 5; + } + + for (int i = 0; i < free_spr2; i++) + { + if (fastcmp(word, spr2names[i])) + { + *type = static_cast(i); + return true; + } + } + + return false; +} + +/*-------------------------------------------------- + static bool ACS_GetStateFromString(const char *word, playersprite_t *type) + + Helper function for CallFunc_Get/SetThingProperty. + Gets a state from a string. + + Input Arguments:- + word: The state string. + type: Variable to store the result in. + + Return:- + true if successful, otherwise false. +--------------------------------------------------*/ +static bool ACS_GetStateFromString(const char *word, statenum_t *type) +{ + if (fastncmp("S_", word, 2)) + { + // take off the S_ + word += 2; + } + + for (int i = 0; i < NUMMOBJFREESLOTS; i++) + { + if (!FREE_STATES[i]) + { + break; + } + + if (fastcmp(word, FREE_STATES[i])) + { + *type = static_cast(static_cast(S_FIRSTFREESLOT) + i); + return true; + } + } + + for (int i = 0; i < S_FIRSTFREESLOT; i++) + { + if (fastcmp(word, STATE_LIST[i] + 2)) + { + *type = static_cast(i); + return true; + } + } + + return false; +} + +/*-------------------------------------------------- + static bool ACS_GetSkinFromString(const char *word, INT32 *type) + + Helper function for CallFunc_Get/SetThingProperty. + Gets a skin from a string. + + Input Arguments:- + word: The skin string. + type: Variable to store the result in. + + Return:- + true if successful, otherwise false. +--------------------------------------------------*/ +static bool ACS_GetSkinFromString(const char *word, INT32 *type) +{ + for (int i = 0; i < numskins; i++) + { + if (fastcmp(word, skins[i].name)) + { + *type = i; + return true; + } + } + + return false; +} + +/*-------------------------------------------------- + static bool ACS_GetColorFromString(const char *word, skincolornum_t *type) + + Helper function for CallFunc_Get/SetThingProperty. + Gets a color from a string. + + Input Arguments:- + word: The color string. + type: Variable to store the result in. + + Return:- + true if successful, otherwise false. +--------------------------------------------------*/ +static bool ACS_GetColorFromString(const char *word, skincolornum_t *type) +{ + for (int i = 0; i < numskincolors; i++) + { + if (fastcmp(word, skins[i].name)) + { + *type = static_cast(i); + return true; + } + } + + return false; +} + /*-------------------------------------------------- static bool ACS_CountThing(mobj_t *mobj, mobjtype_t type) @@ -800,7 +968,17 @@ bool CallFunc_SetLineTexture(ACSVM::Thread *thread, const ACSVM::Word *argV, ACS TAG_ITER_LINES(tag, lineId) { line_t *line = &lines[lineId]; - side_t *side = &sides[line->sidenum[sideId]]; + side_t *side = NULL; + + if (line->sidenum[sideId] != 0xffff) + { + side = &sides[line->sidenum[sideId]]; + } + + if (side == NULL) + { + continue; + } switch (texPos) { @@ -1477,3 +1655,1637 @@ bool CallFunc_SetLineRenderStyle(ACSVM::Thread *thread, const ACSVM::Word *argV, return false; } + +/*-------------------------------------------------- + bool CallFunc_Get/SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Generic line property management. +--------------------------------------------------*/ +enum +{ + LINE_PROP_FLAGS, + LINE_PROP_ALPHA, + LINE_PROP_BLENDMODE, + LINE_PROP_ACTIVATION, + LINE_PROP_ACTION, + LINE_PROP_ARG0, + LINE_PROP_ARG1, + LINE_PROP_ARG2, + LINE_PROP_ARG3, + LINE_PROP_ARG4, + LINE_PROP_ARG5, + LINE_PROP_ARG6, + LINE_PROP_ARG7, + LINE_PROP_ARG8, + LINE_PROP_ARG9, + LINE_PROP_ARG0STR, + LINE_PROP_ARG1STR, + LINE_PROP__MAX +}; + +static INT32 NextLine(mtag_t tag, size_t *iterate, INT32 activatorID) +{ + size_t i = *iterate; + *iterate = *iterate + 1; + + if (tag == 0) + { + // 0 grabs the activator. + + if (i != 0) + { + // Don't do more than once. + return -1; + } + + return activatorID; + } + + return Tag_Iterate_Lines(tag, i); +} + +bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + Environment *env = &ACSEnv; + + mtag_t tag = 0; + size_t tagIt = 0; + + INT32 lineID = 0; + INT32 activatorID = -1; + line_t *line = NULL; + + INT32 property = LINE_PROP__MAX; + INT32 value = 0; + + tag = argV[0]; + + if (info != NULL && info->line != NULL) + { + activatorID = info->line - lines; + } + + if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1) + { + line = &lines[ lineID ]; + } + + property = argV[1]; + + if (line != NULL) + { + +#define PROP_INT(x, y) \ + case x: \ + { \ + value = static_cast( line->y ); \ + break; \ + } + +#define PROP_STR(x, y) \ + case x: \ + { \ + value = static_cast( ~env->getString( line->y )->idx ); \ + break; \ + } + + switch (property) + { + PROP_INT(LINE_PROP_FLAGS, flags) + PROP_INT(LINE_PROP_ALPHA, alpha) + PROP_INT(LINE_PROP_BLENDMODE, blendmode) + PROP_INT(LINE_PROP_ACTIVATION, activation) + PROP_INT(LINE_PROP_ACTION, special) + PROP_INT(LINE_PROP_ARG0, args[0]) + PROP_INT(LINE_PROP_ARG1, args[1]) + PROP_INT(LINE_PROP_ARG2, args[2]) + PROP_INT(LINE_PROP_ARG3, args[3]) + PROP_INT(LINE_PROP_ARG4, args[4]) + PROP_INT(LINE_PROP_ARG5, args[5]) + PROP_INT(LINE_PROP_ARG6, args[6]) + PROP_INT(LINE_PROP_ARG7, args[7]) + PROP_INT(LINE_PROP_ARG8, args[8]) + PROP_INT(LINE_PROP_ARG9, args[9]) + PROP_STR(LINE_PROP_ARG0STR, stringargs[0]) + PROP_STR(LINE_PROP_ARG1STR, stringargs[1]) + default: + { + CONS_Alert(CONS_WARNING, "GetLineProperty type %d out of range (expected 0 - %d).\n", property, LINE_PROP__MAX-1); + break; + } + } + +#undef PROP_STR +#undef PROP_INT + + } + + thread->dataStk.push(value); + return false; +} + +bool CallFunc_SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + //Environment *env = &ACSEnv; + + mtag_t tag = 0; + size_t tagIt = 0; + + INT32 lineID = 0; + INT32 activatorID = -1; + line_t *line = NULL; + + INT32 property = LINE_PROP__MAX; + INT32 value = 0; + + tag = argV[0]; + + if (info != NULL && info->line != NULL) + { + activatorID = info->line - lines; + } + + if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1) + { + line = &lines[ lineID ]; + } + + property = argV[1]; + value = argV[2]; + + while (line != NULL) + { + +#define PROP_READONLY(x, y) \ + case x: \ + { \ + CONS_Alert(CONS_WARNING, "SetLineProperty type '%s' cannot be written to.\n", "y"); \ + break; \ + } + +#define PROP_INT(x, y) \ + case x: \ + { \ + line->y = static_cast< decltype(line->y) >(value); \ + break; \ + } + +#define PROP_STR(x, y) \ + case x: \ + { \ + ACSVM::String *str = thread->scopeMap->getString( value ); \ + if (str->len == 0) \ + { \ + Z_Free(line->y); \ + line->y = NULL; \ + } \ + else \ + { \ + line->y = static_cast(Z_Realloc(line->y, str->len + 1, PU_LEVEL, NULL)); \ + M_Memcpy(line->y, str->str, str->len + 1); \ + line->y[str->len] = '\0'; \ + } \ + break; \ + } + + switch (property) + { + PROP_INT(LINE_PROP_FLAGS, flags) + PROP_INT(LINE_PROP_ALPHA, alpha) + PROP_INT(LINE_PROP_BLENDMODE, blendmode) + PROP_INT(LINE_PROP_ACTIVATION, activation) + PROP_INT(LINE_PROP_ACTION, special) + PROP_INT(LINE_PROP_ARG0, args[0]) + PROP_INT(LINE_PROP_ARG1, args[1]) + PROP_INT(LINE_PROP_ARG2, args[2]) + PROP_INT(LINE_PROP_ARG3, args[3]) + PROP_INT(LINE_PROP_ARG4, args[4]) + PROP_INT(LINE_PROP_ARG5, args[5]) + PROP_INT(LINE_PROP_ARG6, args[6]) + PROP_INT(LINE_PROP_ARG7, args[7]) + PROP_INT(LINE_PROP_ARG8, args[8]) + PROP_INT(LINE_PROP_ARG9, args[9]) + PROP_STR(LINE_PROP_ARG0STR, stringargs[0]) + PROP_STR(LINE_PROP_ARG1STR, stringargs[1]) + default: + { + CONS_Alert(CONS_WARNING, "SetLineProperty type %d out of range (expected 0 - %d).\n", property, LINE_PROP__MAX-1); + break; + } + } + + if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1) + { + line = &lines[ lineID ]; + } + else + { + line = NULL; + } + +#undef PROP_STR +#undef PROP_INT +#undef PROP_READONLY + + } + + return false; +} + +/*-------------------------------------------------- + bool CallFunc_Get/SetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Generic side property management. +--------------------------------------------------*/ +enum +{ + SIDE_FRONT = 0, + SIDE_BACK = 1, + SIDE_BOTH, +}; + +enum +{ + SIDE_PROP_XOFFSET, + SIDE_PROP_YOFFSET, + SIDE_PROP_TOPTEXTURE, + SIDE_PROP_BOTTOMTEXTURE, + SIDE_PROP_MIDTEXTURE, + SIDE_PROP_REPEATCOUNT, + SIDE_PROP__MAX +}; + +bool CallFunc_GetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + Environment *env = &ACSEnv; + + mtag_t tag = 0; + size_t tagIt = 0; + + INT32 lineID = 0; + INT32 activatorID = -1; + line_t *line = NULL; + + UINT8 sideID = 0; + side_t *side = NULL; + + INT32 property = SIDE_PROP__MAX; + INT32 value = 0; + + tag = argV[0]; + + if (info != NULL && info->line != NULL) + { + activatorID = info->line - lines; + } + + if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1) + { + line = &lines[ lineID ]; + } + + sideID = argV[1]; + switch (sideID) + { + default: // Activator + case SIDE_BOTH: // Wouldn't make sense for this function. + { + sideID = info->side; + break; + } + case SIDE_FRONT: + case SIDE_BACK: + { + // Keep sideID as is. + break; + } + } + + if (line != NULL && line->sidenum[sideID] != 0xffff) + { + side = &sides[line->sidenum[sideID]]; + } + + property = argV[2]; + + if (side != NULL) + { + +#define PROP_INT(x, y) \ + case x: \ + { \ + value = static_cast( side->y ); \ + break; \ + } + +#define PROP_STR(x, y) \ + case x: \ + { \ + value = static_cast( ~env->getString( side->y )->idx ); \ + break; \ + } + +#define PROP_TEXTURE(x, y) \ + case x: \ + { \ + value = static_cast( ~env->getString( textures[ side->y ]->name )->idx ); \ + break; \ + } + + switch (property) + { + PROP_INT(SIDE_PROP_XOFFSET, textureoffset) + PROP_INT(SIDE_PROP_YOFFSET, rowoffset) + PROP_TEXTURE(SIDE_PROP_TOPTEXTURE, toptexture) + PROP_TEXTURE(SIDE_PROP_BOTTOMTEXTURE, bottomtexture) + PROP_TEXTURE(SIDE_PROP_MIDTEXTURE, midtexture) + PROP_INT(SIDE_PROP_REPEATCOUNT, repeatcnt) + default: + { + CONS_Alert(CONS_WARNING, "GetSideProperty type %d out of range (expected 0 - %d).\n", property, SIDE_PROP__MAX-1); + break; + } + } + +#undef PROP_TEXTURE +#undef PROP_STR +#undef PROP_INT + + } + + thread->dataStk.push(value); + return false; +} + +bool CallFunc_SetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + //Environment *env = &ACSEnv; + + mtag_t tag = 0; + size_t tagIt = 0; + + INT32 lineID = 0; + INT32 activatorID = -1; + line_t *line = NULL; + + UINT8 sideID = 0; + side_t *side = NULL; + boolean tryBoth = false; + + INT32 property = SIDE_PROP__MAX; + INT32 value = 0; + + tag = argV[0]; + + if (info != NULL && info->line != NULL) + { + activatorID = info->line - lines; + } + + if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1) + { + line = &lines[ lineID ]; + } + + sideID = argV[1]; + switch (sideID) + { + default: // Activator + { + sideID = info->side; + break; + } + case SIDE_BOTH: + { + sideID = SIDE_FRONT; + tryBoth = true; + break; + } + case SIDE_FRONT: + case SIDE_BACK: + { + // Keep sideID as is. + break; + } + } + + if (line != NULL && line->sidenum[sideID] != 0xffff) + { + side = &sides[line->sidenum[sideID]]; + } + + property = argV[2]; + value = argV[3]; + + while (line != NULL) + { + if (side != NULL) + { + +#define PROP_READONLY(x, y) \ + case x: \ + { \ + CONS_Alert(CONS_WARNING, "SetSideProperty type '%s' cannot be written to.\n", "y"); \ + break; \ + } + +#define PROP_INT(x, y) \ + case x: \ + { \ + side->y = static_cast< decltype(side->y) >(value); \ + break; \ + } + +#define PROP_STR(x, y) \ + case x: \ + { \ + ACSVM::String *str = thread->scopeMap->getString( value ); \ + if (str->len == 0) \ + { \ + Z_Free(side->y); \ + side->y = NULL; \ + } \ + else \ + { \ + side->y = static_cast(Z_Realloc(side->y, str->len + 1, PU_LEVEL, NULL)); \ + M_Memcpy(side->y, str->str, str->len + 1); \ + side->y[str->len] = '\0'; \ + } \ + break; \ + } + +#define PROP_TEXTURE(x, y) \ + case x: \ + { \ + side->y = R_TextureNumForName( thread->scopeMap->getString( value )->str ); \ + break; \ + } + + switch (property) + { + PROP_INT(SIDE_PROP_XOFFSET, textureoffset) + PROP_INT(SIDE_PROP_YOFFSET, rowoffset) + PROP_TEXTURE(SIDE_PROP_TOPTEXTURE, toptexture) + PROP_TEXTURE(SIDE_PROP_BOTTOMTEXTURE, bottomtexture) + PROP_TEXTURE(SIDE_PROP_MIDTEXTURE, midtexture) + PROP_INT(SIDE_PROP_REPEATCOUNT, repeatcnt) + default: + { + CONS_Alert(CONS_WARNING, "SetSideProperty type %d out of range (expected 0 - %d).\n", property, SIDE_PROP__MAX-1); + break; + } + } + } + + if (tryBoth == true && sideID == SIDE_FRONT) + { + sideID = SIDE_BACK; + + if (line->sidenum[sideID] != 0xffff) + { + side = &sides[line->sidenum[sideID]]; + continue; + } + } + + if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1) + { + line = &lines[ lineID ]; + + if (tryBoth == true) + { + sideID = SIDE_FRONT; + } + } + else + { + line = NULL; + } + + if (line != NULL && line->sidenum[sideID] != 0xffff) + { + side = &sides[line->sidenum[sideID]]; + } + else + { + side = NULL; + } + +#undef PROP_TEXTURE +#undef PROP_STR +#undef PROP_INT +#undef PROP_READONLY + + } + + return false; +} + +/*-------------------------------------------------- + bool CallFunc_Get/SetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Generic sector property management. +--------------------------------------------------*/ +enum +{ + SECTOR_PROP_FLOORHEIGHT, + SECTOR_PROP_CEILINGHEIGHT, + SECTOR_PROP_FLOORPIC, + SECTOR_PROP_CEILINGPIC, + SECTOR_PROP_LIGHTLEVEL, + SECTOR_PROP_FLOORLIGHTLEVEL, + SECTOR_PROP_CEILINGLIGHTLEVEL, + SECTOR_PROP_FLOORLIGHTABSOLUTE, + SECTOR_PROP_CEILINGLIGHTABSOLUTE, + SECTOR_PROP_FLAGS, + SECTOR_PROP_SPECIALFLAGS, + SECTOR_PROP_GRAVITY, + SECTOR_PROP_ACTIVATION, + SECTOR_PROP_ACTION, + SECTOR_PROP_ARG0, + SECTOR_PROP_ARG1, + SECTOR_PROP_ARG2, + SECTOR_PROP_ARG3, + SECTOR_PROP_ARG4, + SECTOR_PROP_ARG5, + SECTOR_PROP_ARG6, + SECTOR_PROP_ARG7, + SECTOR_PROP_ARG8, + SECTOR_PROP_ARG9, + SECTOR_PROP_ARG0STR, + SECTOR_PROP_ARG1STR, + SECTOR_PROP__MAX +}; + +static INT32 NextSector(mtag_t tag, size_t *iterate, INT32 activatorID) +{ + size_t i = *iterate; + *iterate = *iterate + 1; + + if (tag == 0) + { + // 0 grabs the activator. + + if (i != 0) + { + // Don't do more than once. + return -1; + } + + return activatorID; + } + + return Tag_Iterate_Sectors(tag, i); +} + +bool CallFunc_GetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + Environment *env = &ACSEnv; + + mtag_t tag = 0; + size_t tagIt = 0; + + INT32 sectorID = 0; + INT32 activatorID = -1; + sector_t *sector = NULL; + + INT32 property = SECTOR_PROP__MAX; + INT32 value = 0; + + tag = argV[0]; + + if (info != NULL && info->sector != NULL) + { + activatorID = info->sector - sectors; + } + + if ((sectorID = NextSector(tag, &tagIt, activatorID)) != -1) + { + sector = §ors[ sectorID ]; + } + + property = argV[1]; + + if (sector != NULL) + { + +#define PROP_INT(x, y) \ + case x: \ + { \ + value = static_cast( sector->y ); \ + break; \ + } + +#define PROP_STR(x, y) \ + case x: \ + { \ + value = static_cast( ~env->getString( sector->y )->idx ); \ + break; \ + } + +#define PROP_FLAT(x, y) \ + case x: \ + { \ + value = static_cast( ~env->getString( levelflats[ sector->y ].name )->idx ); \ + break; \ + } + + switch (property) + { + PROP_INT(SECTOR_PROP_FLOORHEIGHT, floorheight) + PROP_INT(SECTOR_PROP_CEILINGHEIGHT, ceilingheight) + PROP_FLAT(SECTOR_PROP_FLOORPIC, floorpic) + PROP_FLAT(SECTOR_PROP_CEILINGPIC, ceilingpic) + PROP_INT(SECTOR_PROP_LIGHTLEVEL, lightlevel) + PROP_INT(SECTOR_PROP_FLOORLIGHTLEVEL, floorlightlevel) + PROP_INT(SECTOR_PROP_CEILINGLIGHTLEVEL, ceilinglightlevel) + PROP_INT(SECTOR_PROP_FLOORLIGHTABSOLUTE, floorlightabsolute) + PROP_INT(SECTOR_PROP_CEILINGLIGHTABSOLUTE, ceilinglightabsolute) + PROP_INT(SECTOR_PROP_FLAGS, flags) + PROP_INT(SECTOR_PROP_SPECIALFLAGS, specialflags) + PROP_INT(SECTOR_PROP_GRAVITY, gravity) + PROP_INT(SECTOR_PROP_ACTIVATION, activation) + PROP_INT(SECTOR_PROP_ACTION, action) + PROP_INT(SECTOR_PROP_ARG0, args[0]) + PROP_INT(SECTOR_PROP_ARG1, args[1]) + PROP_INT(SECTOR_PROP_ARG2, args[2]) + PROP_INT(SECTOR_PROP_ARG3, args[3]) + PROP_INT(SECTOR_PROP_ARG4, args[4]) + PROP_INT(SECTOR_PROP_ARG5, args[5]) + PROP_INT(SECTOR_PROP_ARG6, args[6]) + PROP_INT(SECTOR_PROP_ARG7, args[7]) + PROP_INT(SECTOR_PROP_ARG8, args[8]) + PROP_INT(SECTOR_PROP_ARG9, args[9]) + PROP_STR(SECTOR_PROP_ARG0STR, stringargs[0]) + PROP_STR(SECTOR_PROP_ARG1STR, stringargs[1]) + default: + { + CONS_Alert(CONS_WARNING, "GetSectorProperty type %d out of range (expected 0 - %d).\n", property, SECTOR_PROP__MAX-1); + break; + } + } + +#undef PROP_FLAT +#undef PROP_STR +#undef PROP_INT + + } + + thread->dataStk.push(value); + return false; +} + +bool CallFunc_SetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + //Environment *env = &ACSEnv; + + mtag_t tag = 0; + size_t tagIt = 0; + + INT32 sectorID = 0; + INT32 activatorID = -1; + sector_t *sector = NULL; + + INT32 property = SECTOR_PROP__MAX; + INT32 value = 0; + + tag = argV[0]; + + if (info != NULL && info->sector != NULL) + { + activatorID = info->sector - sectors; + } + + if ((sectorID = NextSector(tag, &tagIt, activatorID)) != -1) + { + sector = §ors[ sectorID ]; + } + + property = argV[1]; + value = argV[2]; + + while (sector != NULL) + { + +#define PROP_READONLY(x, y) \ + case x: \ + { \ + CONS_Alert(CONS_WARNING, "SetSectorProperty type '%s' cannot be written to.\n", "y"); \ + break; \ + } + +#define PROP_INT(x, y) \ + case x: \ + { \ + sector->y = static_cast< decltype(sector->y) >(value); \ + break; \ + } + +#define PROP_STR(x, y) \ + case x: \ + { \ + ACSVM::String *str = thread->scopeMap->getString( value ); \ + if (str->len == 0) \ + { \ + Z_Free(sector->y); \ + sector->y = NULL; \ + } \ + else \ + { \ + sector->y = static_cast(Z_Realloc(sector->y, str->len + 1, PU_LEVEL, NULL)); \ + M_Memcpy(sector->y, str->str, str->len + 1); \ + sector->y[str->len] = '\0'; \ + } \ + break; \ + } + +#define PROP_FLAT(x, y) \ + case x: \ + { \ + sector->y = P_AddLevelFlatRuntime( thread->scopeMap->getString( value )->str ); \ + break; \ + } + + switch (property) + { + PROP_INT(SECTOR_PROP_FLOORHEIGHT, floorheight) + PROP_INT(SECTOR_PROP_CEILINGHEIGHT, ceilingheight) + PROP_FLAT(SECTOR_PROP_FLOORPIC, floorpic) + PROP_FLAT(SECTOR_PROP_CEILINGPIC, ceilingpic) + PROP_INT(SECTOR_PROP_LIGHTLEVEL, lightlevel) + PROP_INT(SECTOR_PROP_FLOORLIGHTLEVEL, floorlightlevel) + PROP_INT(SECTOR_PROP_CEILINGLIGHTLEVEL, ceilinglightlevel) + PROP_INT(SECTOR_PROP_FLOORLIGHTABSOLUTE, floorlightabsolute) + PROP_INT(SECTOR_PROP_CEILINGLIGHTABSOLUTE, ceilinglightabsolute) + PROP_INT(SECTOR_PROP_FLAGS, flags) + PROP_INT(SECTOR_PROP_SPECIALFLAGS, specialflags) + PROP_INT(SECTOR_PROP_GRAVITY, gravity) + PROP_INT(SECTOR_PROP_ACTIVATION, activation) + PROP_INT(SECTOR_PROP_ACTION, action) + PROP_INT(SECTOR_PROP_ARG0, args[0]) + PROP_INT(SECTOR_PROP_ARG1, args[1]) + PROP_INT(SECTOR_PROP_ARG2, args[2]) + PROP_INT(SECTOR_PROP_ARG3, args[3]) + PROP_INT(SECTOR_PROP_ARG4, args[4]) + PROP_INT(SECTOR_PROP_ARG5, args[5]) + PROP_INT(SECTOR_PROP_ARG6, args[6]) + PROP_INT(SECTOR_PROP_ARG7, args[7]) + PROP_INT(SECTOR_PROP_ARG8, args[8]) + PROP_INT(SECTOR_PROP_ARG9, args[9]) + PROP_STR(SECTOR_PROP_ARG0STR, stringargs[0]) + PROP_STR(SECTOR_PROP_ARG1STR, stringargs[1]) + default: + { + CONS_Alert(CONS_WARNING, "SetSectorProperty type %d out of range (expected 0 - %d).\n", property, SECTOR_PROP__MAX-1); + break; + } + } + + if ((sectorID = NextSector(tag, &tagIt, activatorID)) != -1) + { + sector = §ors[ sectorID ]; + } + else + { + sector = NULL; + } + +#undef PROP_FLAT +#undef PROP_STR +#undef PROP_INT +#undef PROP_READONLY + + } + + return false; +} + +/*-------------------------------------------------- + bool CallFunc_Get/SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + Generic thing property management. +--------------------------------------------------*/ +enum +{ + THING_PROP_X, + THING_PROP_Y, + THING_PROP_Z, + THING_PROP_TYPE, + THING_PROP_ANGLE, + THING_PROP_PITCH, + THING_PROP_ROLL, + THING_PROP_SPRITEROLL, + THING_PROP_FRAME, + THING_PROP_SPRITE, + THING_PROP_SPRITE2, + THING_PROP_RENDERFLAGS, + THING_PROP_SPRITEXSCALE, + THING_PROP_SPRITEYSCALE, + THING_PROP_SPRITEXOFFSET, + THING_PROP_SPRITEYOFFSET, + THING_PROP_FLOORZ, + THING_PROP_CEILINGZ, + THING_PROP_RADIUS, + THING_PROP_HEIGHT, + THING_PROP_MOMX, + THING_PROP_MOMY, + THING_PROP_MOMZ, + THING_PROP_TICS, + THING_PROP_STATE, + THING_PROP_FLAGS, + THING_PROP_FLAGS2, + THING_PROP_EFLAGS, + THING_PROP_SKIN, + THING_PROP_COLOR, + THING_PROP_HEALTH, + THING_PROP_MOVEDIR, + THING_PROP_MOVECOUNT, + THING_PROP_REACTIONTIME, + THING_PROP_THRESHOLD, + THING_PROP_LASTLOOK, + THING_PROP_FRICTION, + THING_PROP_MOVEFACTOR, + THING_PROP_FUSE, + THING_PROP_WATERTOP, + THING_PROP_WATERBOTTOM, + THING_PROP_SCALE, + THING_PROP_DESTSCALE, + THING_PROP_SCALESPEED, + THING_PROP_EXTRAVALUE1, + THING_PROP_EXTRAVALUE2, + THING_PROP_CUSVAL, + THING_PROP_CVMEM, + THING_PROP_COLORIZED, + THING_PROP_MIRRORED, + THING_PROP_SHADOWSCALE, + THING_PROP_WHITESHADOW, + THING_PROP_WORLDXOFFSET, + THING_PROP_WORLDYOFFSET, + THING_PROP_WORLDZOFFSET, + THING_PROP_HITLAG, + THING_PROP_WATERSKIP, + THING_PROP_DISPOFFSET, + THING_PROP_TARGET, + THING_PROP_TRACER, + THING_PROP_HNEXT, + THING_PROP_HPREV, + THING_PROP_ITNEXT, + THING_PROP__MAX +}; + +bool CallFunc_GetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + Environment *env = &ACSEnv; + + mtag_t tag = 0; + mobj_t *mobj = NULL; + + INT32 property = SECTOR_PROP__MAX; + INT32 value = 0; + + tag = argV[0]; + mobj = P_FindMobjFromTID(tag, mobj, info->mo); + + property = argV[1]; + + if (mobj != NULL) + { + +#define PROP_INT(x, y) \ + case x: \ + { \ + value = static_cast( mobj->y ); \ + break; \ + } + +#define PROP_STR(x, y) \ + case x: \ + { \ + value = static_cast( ~env->getString( mobj->y )->idx ); \ + break; \ + } + +#define PROP_ANGLE(x, y) \ + case x: \ + { \ + value = static_cast( AngleFixed( mobj->y ) ); \ + break; \ + } + +#define PROP_TYPE(x, y) \ + case x: \ + { \ + if (mobj->y >= MT_FIRSTFREESLOT) \ + { \ + std::string prefix = "MT_"; \ + std::string full = prefix + FREE_MOBJS[mobj->y - MT_FIRSTFREESLOT]; \ + value = static_cast( ~env->getString( full.c_str() )->idx ); \ + } \ + else \ + { \ + value = static_cast( ~env->getString( MOBJTYPE_LIST[ mobj->y ] )->idx ); \ + } \ + break; \ + } + +#define PROP_SPR(x, y) \ + case x: \ + { \ + char crunched[5] = {0}; \ + strncpy(crunched, sprnames[ mobj->y ], 4); \ + value = static_cast( ~env->getString( crunched )->idx ); \ + break; \ + } + +#define PROP_SPR2(x, y) \ + case x: \ + { \ + value = static_cast( ~env->getString( spr2names[ mobj->y ] )->idx ); \ + break; \ + } + +#define PROP_STATE(x, y) \ + case x: \ + { \ + statenum_t stateID = static_cast(mobj->y - states); \ + if (stateID >= S_FIRSTFREESLOT) \ + { \ + std::string prefix = "S_"; \ + std::string full = prefix + FREE_STATES[stateID - S_FIRSTFREESLOT]; \ + value = static_cast( ~env->getString( full.c_str() )->idx ); \ + } \ + else \ + { \ + value = static_cast( ~env->getString( STATE_LIST[ stateID ] )->idx ); \ + } \ + break; \ + } + +#define PROP_SKIN(x, y) \ + case x: \ + { \ + if (mobj->y != NULL) \ + { \ + skin_t *skin = static_cast(mobj->y); \ + value = static_cast( ~env->getString( skin->name )->idx ); \ + } \ + break; \ + } + +#define PROP_COLOR(x, y) \ + case x: \ + { \ + value = static_cast( ~env->getString( skincolors[ mobj->y ].name )->idx ); \ + break; \ + } + +#define PROP_MOBJ(x, y) \ + case x: \ + { \ + if (P_MobjWasRemoved(mobj->y) == false) \ + { \ + value = static_cast( mobj->y->tid ); \ + } \ + break; \ + } + + switch (property) + { + PROP_INT(THING_PROP_X, x) + PROP_INT(THING_PROP_Y, y) + PROP_INT(THING_PROP_Z, z) + PROP_TYPE(THING_PROP_TYPE, type) + PROP_ANGLE(THING_PROP_ANGLE, angle) + PROP_ANGLE(THING_PROP_PITCH, pitch) + PROP_ANGLE(THING_PROP_ROLL, roll) + PROP_ANGLE(THING_PROP_SPRITEROLL, rollangle) + PROP_INT(THING_PROP_FRAME, frame) + PROP_SPR(THING_PROP_SPRITE, sprite) + PROP_SPR2(THING_PROP_SPRITE2, sprite2) + PROP_INT(THING_PROP_RENDERFLAGS, renderflags) + PROP_INT(THING_PROP_SPRITEXSCALE, spritexscale) + PROP_INT(THING_PROP_SPRITEYSCALE, spriteyscale) + PROP_INT(THING_PROP_SPRITEXOFFSET, spritexoffset) + PROP_INT(THING_PROP_SPRITEYOFFSET, spriteyoffset) + PROP_INT(THING_PROP_FLOORZ, floorz) + PROP_INT(THING_PROP_CEILINGZ, ceilingz) + PROP_INT(THING_PROP_RADIUS, radius) + PROP_INT(THING_PROP_HEIGHT, height) + PROP_INT(THING_PROP_MOMX, momx) + PROP_INT(THING_PROP_MOMY, momy) + PROP_INT(THING_PROP_MOMZ, momz) + PROP_INT(THING_PROP_TICS, tics) + PROP_STATE(THING_PROP_STATE, state) + PROP_INT(THING_PROP_FLAGS, flags) + PROP_INT(THING_PROP_FLAGS2, flags2) + PROP_INT(THING_PROP_EFLAGS, eflags) + PROP_SKIN(THING_PROP_SKIN, skin) + PROP_COLOR(THING_PROP_COLOR, color) + PROP_INT(THING_PROP_HEALTH, health) + PROP_INT(THING_PROP_MOVEDIR, movedir) + PROP_INT(THING_PROP_MOVECOUNT, movecount) + PROP_INT(THING_PROP_REACTIONTIME, reactiontime) + PROP_INT(THING_PROP_THRESHOLD, threshold) + PROP_INT(THING_PROP_LASTLOOK, lastlook) + PROP_INT(THING_PROP_FRICTION, friction) + PROP_INT(THING_PROP_MOVEFACTOR, movefactor) + PROP_INT(THING_PROP_FUSE, fuse) + PROP_INT(THING_PROP_WATERTOP, watertop) + PROP_INT(THING_PROP_WATERBOTTOM, waterbottom) + PROP_INT(THING_PROP_SCALE, scale) + PROP_INT(THING_PROP_DESTSCALE, destscale) + PROP_INT(THING_PROP_SCALESPEED, scalespeed) + PROP_INT(THING_PROP_EXTRAVALUE1, extravalue1) + PROP_INT(THING_PROP_EXTRAVALUE2, extravalue2) + PROP_INT(THING_PROP_CUSVAL, cusval) + PROP_INT(THING_PROP_CVMEM, cvmem) + PROP_INT(THING_PROP_COLORIZED, colorized) + PROP_INT(THING_PROP_MIRRORED, mirrored) + PROP_INT(THING_PROP_SHADOWSCALE, shadowscale) + PROP_INT(THING_PROP_WHITESHADOW, whiteshadow) + PROP_INT(THING_PROP_WORLDXOFFSET, sprxoff) + PROP_INT(THING_PROP_WORLDYOFFSET, spryoff) + PROP_INT(THING_PROP_WORLDZOFFSET, sprzoff) + PROP_INT(THING_PROP_HITLAG, hitlag) + PROP_INT(THING_PROP_WATERSKIP, waterskip) + PROP_INT(THING_PROP_DISPOFFSET, dispoffset) + PROP_MOBJ(THING_PROP_TARGET, target) + PROP_MOBJ(THING_PROP_TRACER, tracer) + PROP_MOBJ(THING_PROP_HNEXT, hnext) + PROP_MOBJ(THING_PROP_HPREV, hprev) + PROP_MOBJ(THING_PROP_ITNEXT, itnext) + default: + { + CONS_Alert(CONS_WARNING, "GetThingProperty type %d out of range (expected 0 - %d).\n", property, THING_PROP__MAX-1); + break; + } + } + +#undef PROP_MOBJ +#undef PROP_COLOR +#undef PROP_SKIN +#undef PROP_STATE +#undef PROP_SPR2 +#undef PROP_SPR +#undef PROP_TYPE +#undef PROP_ANGLE +#undef PROP_STR +#undef PROP_INT + + } + + thread->dataStk.push(value); + return false; +} + +bool CallFunc_SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + //Environment *env = &ACSEnv; + + mtag_t tag = 0; + mobj_t *mobj = NULL; + + INT32 property = SECTOR_PROP__MAX; + INT32 value = 0; + + tag = argV[0]; + mobj = P_FindMobjFromTID(tag, mobj, info->mo); + + property = argV[1]; + value = argV[2]; + + while (mobj != NULL) + { + +#define PROP_READONLY(x, y) \ + case x: \ + { \ + CONS_Alert(CONS_WARNING, "SetThingProperty type '%s' cannot be written to.\n", "y"); \ + break; \ + } + +#define PROP_INT(x, y) \ + case x: \ + { \ + mobj->y = static_cast< decltype(mobj->y) >(value); \ + break; \ + } + +#define PROP_STR(x, y) \ + case x: \ + { \ + ACSVM::String *str = thread->scopeMap->getString( value ); \ + if (str->len == 0) \ + { \ + Z_Free(mobj->y); \ + mobj->y = NULL; \ + } \ + else \ + { \ + mobj->y = static_cast(Z_Realloc(mobj->y, str->len + 1, PU_LEVEL, NULL)); \ + M_Memcpy(mobj->y, str->str, str->len + 1); \ + mobj->y[str->len] = '\0'; \ + } \ + break; \ + } + +#define PROP_ANGLE(x, y) \ + case x: \ + { \ + mobj->y = static_cast( FixedAngle(value) ); \ + break; \ + } + +#define PROP_TYPE(x, y) \ + case x: \ + { \ + if (mobj->player == NULL) \ + { \ + mobjtype_t newType = mobj->y; \ + bool success = ACS_GetMobjTypeFromString(thread->scopeMap->getString( value )->str, &newType); \ + if (success == true) \ + { \ + mobj->y = newType; \ + mobj->info = &mobjinfo[newType]; \ + P_SetScale(mobj, mobj->scale); \ + } \ + } \ + break; \ + } + +#define PROP_SPR(x, y) \ + case x: \ + { \ + spritenum_t newSprite = mobj->y; \ + bool success = ACS_GetSpriteFromString(thread->scopeMap->getString( value )->str, &newSprite); \ + if (success == true) \ + { \ + mobj->y = newSprite; \ + } \ + break; \ + } + +#define PROP_SPR2(x, y) \ + case x: \ + { \ + playersprite_t newSprite2 = static_cast(mobj->y); \ + bool success = ACS_GetSprite2FromString(thread->scopeMap->getString( value )->str, &newSprite2); \ + if (success == true) \ + { \ + mobj->y = static_cast< decltype(mobj->y) >(newSprite2); \ + } \ + break; \ + } + +#define PROP_STATE(x, y) \ + case x: \ + { \ + statenum_t newState = static_cast(mobj->y - states); \ + bool success = ACS_GetStateFromString(thread->scopeMap->getString( value )->str, &newState); \ + if (success == true) \ + { \ + if (mobj->player != NULL) \ + { \ + P_SetPlayerMobjState(mobj, newState); \ + } \ + else \ + { \ + P_SetMobjState(mobj, newState); \ + } \ + } \ + break; \ + } + +#define PROP_SKIN(x, y) \ + case x: \ + { \ + INT32 newSkin = (mobj->skin != NULL) ? (static_cast(mobj->skin)) - skins : -1; \ + bool success = ACS_GetSkinFromString(thread->scopeMap->getString( value )->str, &newSkin); \ + if (success == true) \ + { \ + mobj->y = (newSkin >= 0 && newSkin < numskins) ? &skins[ newSkin ] : NULL; \ + } \ + break; \ + } + +#define PROP_COLOR(x, y) \ + case x: \ + { \ + skincolornum_t newColor = static_cast(mobj->y); \ + bool success = ACS_GetColorFromString(thread->scopeMap->getString( value )->str, &newColor); \ + if (success == true) \ + { \ + mobj->y = static_cast< decltype(mobj->y) >(newColor); \ + } \ + break; \ + } + +#define PROP_MOBJ(x, y) \ + case x: \ + { \ + mobj_t *newTarget = P_FindMobjFromTID(value, NULL, NULL); \ + P_SetTarget(&mobj->y, newTarget); \ + break; \ + } + +#define PROP_SCALE(x, y) \ + case x: \ + { \ + P_SetScale(mobj, value); \ + break; \ + } + +#define PROP_FLAGS(x, y) \ + case x: \ + { \ + if ((value & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (mobj->y & (MF_NOBLOCKMAP|MF_NOSECTOR))) \ + { \ + P_UnsetThingPosition(mobj); \ + mobj->y = value; \ + if ((value & MF_NOSECTOR) && sector_list) \ + { \ + P_DelSeclist(sector_list); \ + sector_list = NULL; \ + } \ + mobj->snext = NULL, mobj->sprev = NULL; \ + mobj->bnext = NULL, mobj->bprev = NULL; \ + P_SetThingPosition(mobj); \ + } \ + else \ + { \ + mobj->y = value; \ + } \ + break; \ + } + + switch (property) + { + PROP_READONLY(THING_PROP_X, x) + PROP_READONLY(THING_PROP_Y, y) + PROP_READONLY(THING_PROP_Z, z) + PROP_TYPE(THING_PROP_TYPE, type) + PROP_ANGLE(THING_PROP_ANGLE, angle) + PROP_ANGLE(THING_PROP_PITCH, pitch) + PROP_ANGLE(THING_PROP_ROLL, roll) + PROP_ANGLE(THING_PROP_SPRITEROLL, rollangle) + PROP_INT(THING_PROP_FRAME, frame) + PROP_SPR(THING_PROP_SPRITE, sprite) + PROP_SPR2(THING_PROP_SPRITE2, sprite2) + PROP_INT(THING_PROP_RENDERFLAGS, renderflags) + PROP_INT(THING_PROP_SPRITEXSCALE, spritexscale) + PROP_INT(THING_PROP_SPRITEYSCALE, spriteyscale) + PROP_INT(THING_PROP_SPRITEXOFFSET, spritexoffset) + PROP_INT(THING_PROP_SPRITEYOFFSET, spriteyoffset) + PROP_INT(THING_PROP_FLOORZ, floorz) + PROP_INT(THING_PROP_CEILINGZ, ceilingz) + PROP_READONLY(THING_PROP_RADIUS, radius) + PROP_READONLY(THING_PROP_HEIGHT, height) + PROP_INT(THING_PROP_MOMX, momx) + PROP_INT(THING_PROP_MOMY, momy) + PROP_INT(THING_PROP_MOMZ, momz) + PROP_INT(THING_PROP_TICS, tics) + PROP_STATE(THING_PROP_STATE, state) + PROP_FLAGS(THING_PROP_FLAGS, flags) + PROP_INT(THING_PROP_FLAGS2, flags2) + PROP_INT(THING_PROP_EFLAGS, eflags) + PROP_SKIN(THING_PROP_SKIN, skin) + PROP_COLOR(THING_PROP_COLOR, color) + PROP_INT(THING_PROP_HEALTH, health) + PROP_INT(THING_PROP_MOVEDIR, movedir) + PROP_INT(THING_PROP_MOVECOUNT, movecount) + PROP_INT(THING_PROP_REACTIONTIME, reactiontime) + PROP_INT(THING_PROP_THRESHOLD, threshold) + PROP_INT(THING_PROP_LASTLOOK, lastlook) + PROP_INT(THING_PROP_FRICTION, friction) + PROP_INT(THING_PROP_MOVEFACTOR, movefactor) + PROP_INT(THING_PROP_FUSE, fuse) + PROP_INT(THING_PROP_WATERTOP, watertop) + PROP_INT(THING_PROP_WATERBOTTOM, waterbottom) + PROP_SCALE(THING_PROP_SCALE, scale) + PROP_INT(THING_PROP_DESTSCALE, destscale) + PROP_INT(THING_PROP_SCALESPEED, scalespeed) + PROP_INT(THING_PROP_EXTRAVALUE1, extravalue1) + PROP_INT(THING_PROP_EXTRAVALUE2, extravalue2) + PROP_INT(THING_PROP_CUSVAL, cusval) + PROP_INT(THING_PROP_CVMEM, cvmem) + PROP_INT(THING_PROP_COLORIZED, colorized) + PROP_INT(THING_PROP_MIRRORED, mirrored) + PROP_INT(THING_PROP_SHADOWSCALE, shadowscale) + PROP_INT(THING_PROP_WHITESHADOW, whiteshadow) + PROP_INT(THING_PROP_WORLDXOFFSET, sprxoff) + PROP_INT(THING_PROP_WORLDYOFFSET, spryoff) + PROP_INT(THING_PROP_WORLDZOFFSET, sprzoff) + PROP_INT(THING_PROP_HITLAG, hitlag) + PROP_INT(THING_PROP_WATERSKIP, waterskip) + PROP_INT(THING_PROP_DISPOFFSET, dispoffset) + PROP_MOBJ(THING_PROP_TARGET, target) + PROP_MOBJ(THING_PROP_TRACER, tracer) + PROP_MOBJ(THING_PROP_HNEXT, hnext) + PROP_MOBJ(THING_PROP_HPREV, hprev) + PROP_MOBJ(THING_PROP_ITNEXT, itnext) + default: + { + CONS_Alert(CONS_WARNING, "SetThingProperty type %d out of range (expected 0 - %d).\n", property, THING_PROP__MAX-1); + break; + } + } + + mobj = P_FindMobjFromTID(tag, mobj, info->mo); + +#undef PROP_FLAGS +#undef PROP_SCALE +#undef PROP_MOBJ +#undef PROP_COLOR +#undef PROP_SKIN +#undef PROP_STATE +#undef PROP_SPR2 +#undef PROP_SPR +#undef PROP_TYPE +#undef PROP_ANGLE +#undef PROP_STR +#undef PROP_INT +#undef PROP_READONLY + + } + + return false; +} + +/*-------------------------------------------------- + bool CallFunc_Get[x]UserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) + + User-defined property management. +--------------------------------------------------*/ +bool CallFunc_GetLineUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + Environment *env = &ACSEnv; + + mtag_t tag = 0; + size_t tagIt = 0; + + INT32 lineID = 0; + INT32 activatorID = -1; + line_t *line = NULL; + + const char *key = NULL; + + mapUserProperty_t *prop = NULL; + INT32 ret = 0; + + tag = argV[0]; + key = thread->scopeMap->getString(argV[1])->str; + + if (info != NULL && info->line != NULL) + { + activatorID = info->line - lines; + } + + if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1) + { + line = &lines[ lineID ]; + } + + if (line != NULL) + { + prop = K_UserPropertyFind(&line->user, key); + } + + if (prop != NULL) + { + switch (prop->type) + { + case USER_PROP_BOOL: + { + ret = static_cast(prop->valueBool); + break; + } + case USER_PROP_INT: + { + ret = prop->valueInt; + break; + } + case USER_PROP_FIXED: + { + ret = static_cast(prop->valueFixed); + break; + } + case USER_PROP_STR: + { + ret = static_cast( ~env->getString( prop->valueStr )->idx ); + break; + } + } + } + + thread->dataStk.push(ret); + return false; +} + +bool CallFunc_GetSideUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + Environment *env = &ACSEnv; + + mtag_t tag = 0; + size_t tagIt = 0; + + INT32 lineID = 0; + INT32 activatorID = -1; + line_t *line = NULL; + + UINT8 sideID = 0; + side_t *side = NULL; + + const char *key = NULL; + + mapUserProperty_t *prop = NULL; + INT32 ret = 0; + + tag = argV[0]; + sideID = argV[1]; + key = thread->scopeMap->getString(argV[2])->str; + + if (info != NULL && info->line != NULL) + { + activatorID = info->line - lines; + } + + if ((lineID = NextLine(tag, &tagIt, activatorID)) != -1) + { + line = &lines[ lineID ]; + } + + if (sideID < 0 || sideID > 1) + { + sideID = info->side; + } + + if (line != NULL && line->sidenum[sideID] != 0xffff) + { + side = &sides[line->sidenum[sideID]]; + } + + if (side != NULL) + { + prop = K_UserPropertyFind(&side->user, key); + } + + if (prop != NULL) + { + switch (prop->type) + { + case USER_PROP_BOOL: + { + ret = static_cast(prop->valueBool); + break; + } + case USER_PROP_INT: + { + ret = prop->valueInt; + break; + } + case USER_PROP_FIXED: + { + ret = static_cast(prop->valueFixed); + break; + } + case USER_PROP_STR: + { + ret = static_cast( ~env->getString( prop->valueStr )->idx ); + break; + } + } + } + + thread->dataStk.push(ret); + return false; +} + +bool CallFunc_GetSectorUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + Environment *env = &ACSEnv; + + mtag_t tag = 0; + size_t tagIt = 0; + + INT32 sectorID = 0; + INT32 activatorID = -1; + sector_t *sector = NULL; + + const char *key = NULL; + + mapUserProperty_t *prop = NULL; + INT32 ret = 0; + + tag = argV[0]; + key = thread->scopeMap->getString(argV[1])->str; + + if (info != NULL && info->sector != NULL) + { + activatorID = info->sector - sectors; + } + + if ((sectorID = NextSector(tag, &tagIt, activatorID)) != -1) + { + sector = §ors[ sectorID ]; + } + + if (sector != NULL) + { + prop = K_UserPropertyFind(§or->user, key); + } + + if (prop != NULL) + { + switch (prop->type) + { + case USER_PROP_BOOL: + { + ret = static_cast(prop->valueBool); + break; + } + case USER_PROP_INT: + { + ret = prop->valueInt; + break; + } + case USER_PROP_FIXED: + { + ret = static_cast(prop->valueFixed); + break; + } + case USER_PROP_STR: + { + ret = static_cast( ~env->getString( prop->valueStr )->idx ); + break; + } + } + } + + thread->dataStk.push(ret); + return false; +} + +bool CallFunc_GetThingUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC) +{ + auto info = &static_cast(thread)->info; + Environment *env = &ACSEnv; + + mtag_t tag = 0; + mobj_t *mobj = NULL; + + const char *key = NULL; + + mapUserProperty_t *prop = NULL; + INT32 ret = 0; + + tag = argV[0]; + key = thread->scopeMap->getString(argV[1])->str; + + mobj = P_FindMobjFromTID(tag, mobj, info->mo); + + if (mobj != NULL && mobj->spawnpoint != NULL) + { + prop = K_UserPropertyFind(&mobj->spawnpoint->user, key); + } + + if (prop != NULL) + { + switch (prop->type) + { + case USER_PROP_BOOL: + { + ret = static_cast(prop->valueBool); + break; + } + case USER_PROP_INT: + { + ret = prop->valueInt; + break; + } + case USER_PROP_FIXED: + { + ret = static_cast(prop->valueFixed); + break; + } + case USER_PROP_STR: + { + ret = static_cast( ~env->getString( prop->valueStr )->idx ); + break; + } + } + } + + thread->dataStk.push(ret); + return false; +} diff --git a/src/acs/call-funcs.hpp b/src/acs/call-funcs.hpp index b5136832f..e2c43e945 100644 --- a/src/acs/call-funcs.hpp +++ b/src/acs/call-funcs.hpp @@ -83,4 +83,18 @@ bool CallFunc_PodiumFinish(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM bool CallFunc_SetLineRenderStyle(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_GetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_SetLineProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_GetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_SetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_GetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_SetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_GetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_SetThingProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); + +bool CallFunc_GetLineUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_GetSideUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_GetSectorUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); +bool CallFunc_GetThingUserProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, ACSVM::Word argC); + #endif // __SRB2_ACS_CALL_FUNCS_HPP__ diff --git a/src/acs/environment.cpp b/src/acs/environment.cpp index 0b4932d56..e90c34cb6 100644 --- a/src/acs/environment.cpp +++ b/src/acs/environment.cpp @@ -128,14 +128,22 @@ Environment::Environment() // This style is preferred for added functions // that aren't mimicing one from Hexen's or ZDoom's // ACS implementations. - //addFuncDataACS0( 1, addCallFunc(CallFunc_GetLineUDMFInt)); - //addFuncDataACS0( 2, addCallFunc(CallFunc_GetLineUDMFFixed)); - //addFuncDataACS0( 3, addCallFunc(CallFunc_GetThingUDMFInt)); - //addFuncDataACS0( 4, addCallFunc(CallFunc_GetThingUDMFFixed)); - //addFuncDataACS0( 5, addCallFunc(CallFunc_GetSectorUDMFInt)); - //addFuncDataACS0( 6, addCallFunc(CallFunc_GetSectorUDMFFixed)); - //addFuncDataACS0( 7, addCallFunc(CallFunc_GetSideUDMFInt)); - //addFuncDataACS0( 8, addCallFunc(CallFunc_GetSideUDMFFixed)); + addFuncDataACS0( 1, addCallFunc(CallFunc_GetLineProperty)); + addFuncDataACS0( 2, addCallFunc(CallFunc_SetLineProperty)); + addFuncDataACS0( 3, addCallFunc(CallFunc_GetLineUserProperty)); + addFuncDataACS0( 4, addCallFunc(CallFunc_GetSectorProperty)); + addFuncDataACS0( 5, addCallFunc(CallFunc_SetSectorProperty)); + addFuncDataACS0( 6, addCallFunc(CallFunc_GetSectorUserProperty)); + addFuncDataACS0( 7, addCallFunc(CallFunc_GetSideProperty)); + addFuncDataACS0( 8, addCallFunc(CallFunc_SetSideProperty)); + addFuncDataACS0( 9, addCallFunc(CallFunc_GetSideUserProperty)); + addFuncDataACS0( 10, addCallFunc(CallFunc_GetThingProperty)); + addFuncDataACS0( 11, addCallFunc(CallFunc_SetThingProperty)); + addFuncDataACS0( 12, addCallFunc(CallFunc_GetThingUserProperty)); + //addFuncDataACS0( 13, addCallFunc(CallFunc_GetPlayerProperty)); + //addFuncDataACS0( 14, addCallFunc(CallFunc_SetPlayerProperty)); + //addFuncDataACS0( 15, addCallFunc(CallFunc_GetPolyobjProperty)); + //addFuncDataACS0( 16, addCallFunc(CallFunc_SetPolyobjProperty)); addFuncDataACS0( 100, addCallFunc(CallFunc_strcmp)); addFuncDataACS0( 101, addCallFunc(CallFunc_strcasecmp)); diff --git a/src/doomdata.h b/src/doomdata.h index c7395727f..f5e617a0f 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -233,6 +233,21 @@ struct mapnode_t #pragma pack() #endif +typedef enum +{ + USER_PROP_BOOL, + USER_PROP_INT, + USER_PROP_FIXED, + USER_PROP_STR +} mapUserPropertyType_e; + +struct mapUserProperties_t +{ + mapUserProperty_t *properties; + size_t length; + size_t capacity; +}; + #define NUMMAPTHINGARGS 10 #define NUMMAPTHINGSTRINGARGS 2 @@ -252,6 +267,7 @@ struct mapthing_t INT32 args[NUMMAPTHINGARGS]; char *stringargs[NUMMAPTHINGSTRINGARGS]; UINT8 layer; // FOF layer to spawn on, see P_GetMobjSpawnHeight + mapUserProperties_t user; // UDMF user-defined custom properties. mobj_t *mobj; }; diff --git a/src/doomdef.h b/src/doomdef.h index 99b79c03a..890ace89a 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -540,6 +540,7 @@ void M_TokenizerClose(void); const char *M_TokenizerRead(UINT32 i); UINT32 M_TokenizerGetEndPos(void); void M_TokenizerSetEndPos(UINT32 newPos); +boolean M_TokenizerJustReadString(void); char *sizeu1(size_t num); char *sizeu2(size_t num); diff --git a/src/k_mapuser.c b/src/k_mapuser.c new file mode 100644 index 000000000..9253e1eaa --- /dev/null +++ b/src/k_mapuser.c @@ -0,0 +1,180 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) by Sally "TehRealSalt" Cochenour +// Copyright (C) by Kart Krew +// +// 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 k_mapuser.c +/// \brief UDMF: Custom user properties + +#include "k_mapuser.h" + +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" +#include "doomtype.h" +#include "z_zone.h" +#include "m_misc.h" +#include "m_fixed.h" + +/*-------------------------------------------------- + void K_UserPropertyPush(mapUserProperties_t *user, const char *key, mapUserPropertyType_e type, void *value) + + See header file for description. +--------------------------------------------------*/ +void K_UserPropertyPush(mapUserProperties_t *user, const char *key, mapUserPropertyType_e type, void *value) +{ + const size_t keyLength = strlen(key); + mapUserProperty_t *prop = NULL; + + I_Assert(keyLength > 0); + + if (user->length >= user->capacity) + { + if (user->capacity == 0) + { + user->capacity = 8; + } + else + { + user->capacity *= 2; + } + + user->properties = (mapUserProperty_t *)Z_ReallocAlign( + user->properties, + sizeof(mapUserProperty_t) * user->capacity, + PU_LEVEL, + NULL, + sizeof(mapUserProperty_t) * 8 + ); + } + + prop = &user->properties[ user->length ]; + + prop->key = Z_Malloc(keyLength + 1, PU_LEVEL, NULL); + M_Memcpy(prop->key, key, keyLength + 1); + prop->key[keyLength] = '\0'; + + prop->hash = quickncasehash(prop->key, keyLength); + + prop->type = type; + switch (type) + { + case USER_PROP_BOOL: + { + prop->valueBool = *(boolean *)value; + break; + } + case USER_PROP_INT: + { + prop->valueInt = *(INT32 *)value; + break; + } + case USER_PROP_FIXED: + { + prop->valueFixed = *(fixed_t *)value; + break; + } + case USER_PROP_STR: + { + const char *string = *(const char **)value; + const size_t stringLength = strlen(string); + + prop->valueStr = Z_Malloc(stringLength + 1, PU_LEVEL, NULL); + M_Memcpy(prop->valueStr, string, stringLength + 1); + prop->valueStr[stringLength] = '\0'; + break; + } + } + + user->length++; +} + +/*-------------------------------------------------- + mapUserProperty_t *K_UserPropertyFind(mapUserProperties_t *user, const char *key) + + See header file for description. +--------------------------------------------------*/ +mapUserProperty_t *K_UserPropertyFind(mapUserProperties_t *user, const char *key) +{ + const size_t keyLength = strlen(key); + const UINT32 hash = quickncasehash(key, keyLength); + size_t i; + + if (user->length == 0) + { + return NULL; + } + + for (i = 0; i < user->length; i++) + { + mapUserProperty_t *const prop = &user->properties[ i ]; + + if (hash != prop->hash) + { + continue; + } + + if (strcasecmp(key, prop->key)) + { + continue; + } + + return prop; + } + + return NULL; +} + +/*-------------------------------------------------- + static void K_UserPropertyFree(mapUserProperty_t *prop) + + Frees the memory of a single user property. + + Input Arguments:- + prop - User property memory structure to free. + + Return:- + N/A +--------------------------------------------------*/ +static void K_UserPropertyFree(mapUserProperty_t *prop) +{ + if (prop->key != NULL) + { + Z_Free(prop->key); + prop->key = NULL; + } + + if (prop->valueStr != NULL) + { + Z_Free(prop->valueStr); + prop->valueStr = NULL; + } +} + +/*-------------------------------------------------- + void K_UserPropertiesClear(mapUserProperties_t *user) + + See header file for description. +--------------------------------------------------*/ +void K_UserPropertiesClear(mapUserProperties_t *user) +{ + size_t i; + + if (user->properties == NULL) + { + return; + } + + for (i = 0; i < user->length; i++) + { + K_UserPropertyFree(&user->properties[i]); + } + + Z_Free(user->properties); + user->properties = NULL; + user->length = user->capacity = 0; +} diff --git a/src/k_mapuser.h b/src/k_mapuser.h new file mode 100644 index 000000000..6bbb6e516 --- /dev/null +++ b/src/k_mapuser.h @@ -0,0 +1,94 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) by Sally "TehRealSalt" Cochenour +// Copyright (C) by Kart Krew +// +// 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 k_mapuser.h +/// \brief UDMF: Custom user properties + +#ifndef __K_MAPUSER__ +#define __K_MAPUSER__ + +#include "doomdef.h" +#include "doomstat.h" +#include "doomdata.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct mapUserProperty_t +{ + UINT32 hash; + char *key; + + mapUserPropertyType_e type; + + boolean valueBool; + INT32 valueInt; + fixed_t valueFixed; + char *valueStr; +}; + +// mapUserProperties_t has to be defined in doomdata.h instead +// because of circular dependency issues + +/*-------------------------------------------------- + void K_UserPropertyPush(mapUserProperties_t *user, const char *key, mapUserPropertyType_e type, void *value); + + Adds a new key value to the user properties struct. + + Input Arguments:- + user - User properties memory structure. + key - The key to add. + type - Enum representing the type of the value to add. + value - The value to add, as a void pointer. + + Return:- + N/A +--------------------------------------------------*/ + +void K_UserPropertyPush(mapUserProperties_t *user, const char *key, mapUserPropertyType_e type, void *value); + + +/*-------------------------------------------------- + mapUserProperty_t *K_UserPropertyFind(mapUserProperties_t *user, const char *key); + + Tries to find if the key value already exists in this + user properties struct. + + Input Arguments:- + user - User properties memory structure. + key - The key to find. + + Return:- + The struct of the property we just got, or NULL if the key didn't exist already. +--------------------------------------------------*/ + +mapUserProperty_t *K_UserPropertyFind(mapUserProperties_t *user, const char *key); + + +/*-------------------------------------------------- + void K_UserPropertiesClear(mapUserProperties_t *user); + + Frees an entire user properties struct. + + Input Arguments:- + user - User properties memory structure to free. + + Return:- + N/A +--------------------------------------------------*/ + +void K_UserPropertiesClear(mapUserProperties_t *user); + + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif // __K_MAPUSER__ diff --git a/src/m_misc.cpp b/src/m_misc.cpp index f5b1594ba..226128fc1 100644 --- a/src/m_misc.cpp +++ b/src/m_misc.cpp @@ -2295,6 +2295,7 @@ static UINT32 tokenizerStartPos = 0; static UINT32 tokenizerEndPos = 0; static UINT32 tokenizerInputLength = 0; static UINT8 tokenizerInComment = 0; // 0 = not in comment, 1 = // Single-line, 2 = /* Multi-line */ +static boolean tokenizerIsString = false; // did we strip quotes from this token? void M_TokenizerOpen(const char *inputString) { @@ -2319,6 +2320,7 @@ void M_TokenizerClose(void) tokenizerStartPos = 0; tokenizerEndPos = 0; tokenizerInComment = 0; + tokenizerIsString = false; } static void M_DetectComment(UINT32 *pos) @@ -2349,8 +2351,10 @@ static void M_ReadTokenString(UINT32 i) // Assign the memory. Don't forget an extra byte for the end of the string! tokenizerToken[i] = (char *)Z_Malloc(tokenCapacity[i] * sizeof(char), PU_STATIC, NULL); } + // Copy the string. M_Memcpy(tokenizerToken[i], tokenizerInput + tokenizerStartPos, (size_t)tokenLength); + // Make the final character NUL. tokenizerToken[i][tokenLength] = '\0'; } @@ -2362,6 +2366,9 @@ const char *M_TokenizerRead(UINT32 i) tokenizerStartPos = tokenizerEndPos; + // Reset string flag + tokenizerIsString = false; + // Try to detect comments now, in case we're pointing right at one M_DetectComment(&tokenizerStartPos); @@ -2416,6 +2423,10 @@ const char *M_TokenizerRead(UINT32 i) M_ReadTokenString(i); tokenizerEndPos++; + + // Tell us the the token was a string. + tokenizerIsString = true; + return tokenizerToken[i]; } @@ -2451,6 +2462,11 @@ void M_TokenizerSetEndPos(UINT32 newPos) tokenizerEndPos = newPos; } +boolean M_TokenizerJustReadString(void) +{ + return tokenizerIsString; +} + /** Count bits in a number. */ UINT8 M_CountBits(UINT32 num, UINT8 size) diff --git a/src/p_setup.c b/src/p_setup.c index 27f523e25..4153f889a 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -103,6 +103,7 @@ #include "doomstat.h" // MAXMUSNAMES #include "k_podium.h" #include "k_rank.h" +#include "k_mapuser.h" // Replay names have time #if !defined (UNDER_CE) @@ -1388,6 +1389,82 @@ static boolean TextmapCount(size_t size) return true; } +enum +{ + PROP_NUM_TYPE_NA, + PROP_NUM_TYPE_INT, + PROP_NUM_TYPE_FLOAT +}; + +static void ParseUserProperty(mapUserProperties_t *user, const char *param, const char *val) +{ + if (fastncmp(param, "user_", 5) && strlen(param) > 5) + { + const boolean valIsString = M_TokenizerJustReadString(); + const char *key = param + 5; + const size_t valLen = strlen(val); + UINT8 numberType = PROP_NUM_TYPE_INT; + size_t i = 0; + + if (valIsString == true) + { + // Value is a string. Upload directly! + K_UserPropertyPush(user, key, USER_PROP_STR, &val); + return; + } + + for (i = 0; i < valLen; i++) + { + if (val[i] == '.') + { + numberType = PROP_NUM_TYPE_FLOAT; + } + else if (val[i] < '0' || val[i] > '9') + { + numberType = PROP_NUM_TYPE_NA; + break; + } + } + + switch (numberType) + { + case PROP_NUM_TYPE_INT: + { + // Value is an integer. + INT32 vInt = atol(val); + K_UserPropertyPush(user, key, USER_PROP_INT, &vInt); + break; + } + case PROP_NUM_TYPE_FLOAT: + { + // Value is a float. Convert to fixed. + fixed_t vFixed = FLOAT_TO_FIXED(atof(val)); + K_UserPropertyPush(user, key, USER_PROP_FIXED, &vFixed); + break; + } + case PROP_NUM_TYPE_NA: + default: + { + // Value is some other kind of type. + // Currently we just support bool. + + boolean vBool = fastcmp("true", val); + if (vBool == true || fastcmp("false", val)) + { + // Value is a boolean. + K_UserPropertyPush(user, key, USER_PROP_BOOL, &vBool); + } + else + { + // Value is invalid. + CONS_Alert(CONS_WARNING, "Could not interpret user property \"%s\" value (%s)\n", param, val); + } + break; + } + } + } +} + static void ParseTextmapVertexParameter(UINT32 i, const char *param, const char *val) { if (fastcmp(param, "x")) @@ -1657,6 +1734,8 @@ static void ParseTextmapSectorParameter(UINT32 i, const char *param, const char sectors[i].activation |= SECSPAC_FLOORMISSILE; else if (fastcmp(param, "missileceiling") && fastcmp("true", val)) sectors[i].activation |= SECSPAC_CEILINGMISSILE; + else + ParseUserProperty(§ors[i].user, param, val); } static void ParseTextmapSidedefParameter(UINT32 i, const char *param, const char *val) @@ -1675,6 +1754,8 @@ static void ParseTextmapSidedefParameter(UINT32 i, const char *param, const char P_SetSidedefSector(i, atol(val)); else if (fastcmp(param, "repeatcnt")) sides[i].repeatcnt = atol(val); + else + ParseUserProperty(&sides[i].user, param, val); } static void ParseTextmapLinedefParameter(UINT32 i, const char *param, const char *val) @@ -1782,6 +1863,8 @@ static void ParseTextmapLinedefParameter(UINT32 i, const char *param, const char lines[i].activation |= SPAC_PUSHMONSTER; else if (fastcmp(param, "impact") && fastcmp("true", val)) lines[i].activation |= SPAC_IMPACT; + else + ParseUserProperty(&lines[i].user, param, val); } static void ParseTextmapThingParameter(UINT32 i, const char *param, const char *val) @@ -1839,6 +1922,8 @@ static void ParseTextmapThingParameter(UINT32 i, const char *param, const char * return; mapthings[i].args[argnum] = atol(val); } + else + ParseUserProperty(&mapthings[i].user, param, val); } /** From a given position table, run a specified parser function through a {}-encapsuled text. @@ -1977,13 +2062,35 @@ static void P_WriteTextmap(void) memcpy(wsides, sides, numsides * sizeof(*sides)); for (i = 0; i < nummapthings; i++) + { if (mapthings[i].tags.count) wmapthings[i].tags.tags = memcpy(Z_Malloc(mapthings[i].tags.count * sizeof(mtag_t), PU_LEVEL, NULL), mapthings[i].tags.tags, mapthings[i].tags.count * sizeof(mtag_t)); + if (mapthings[i].user.length) + { + wmapthings[i].user.properties = memcpy( + Z_Malloc(mapthings[i].user.length * sizeof(mapUserProperty_t), PU_LEVEL, NULL), + mapthings[i].user.properties, + mapthings[i].user.length * sizeof(mapUserProperty_t) + ); + } + } + for (i = 0; i < numsectors; i++) + { if (sectors[i].tags.count) wsectors[i].tags.tags = memcpy(Z_Malloc(sectors[i].tags.count*sizeof(mtag_t), PU_LEVEL, NULL), sectors[i].tags.tags, sectors[i].tags.count*sizeof(mtag_t)); + if (sectors[i].user.length) + { + wsectors[i].user.properties = memcpy( + Z_Malloc(sectors[i].user.length * sizeof(mapUserProperty_t), PU_LEVEL, NULL), + sectors[i].user.properties, + sectors[i].user.length * sizeof(mapUserProperty_t) + ); + } + } + for (i = 0; i < numlines; i++) { size_t v; @@ -1991,6 +2098,15 @@ static void P_WriteTextmap(void) if (lines[i].tags.count) wlines[i].tags.tags = memcpy(Z_Malloc(lines[i].tags.count * sizeof(mtag_t), PU_LEVEL, NULL), lines[i].tags.tags, lines[i].tags.count * sizeof(mtag_t)); + if (lines[i].user.length) + { + wlines[i].user.properties = memcpy( + Z_Malloc(lines[i].user.length * sizeof(mapUserProperty_t), PU_LEVEL, NULL), + lines[i].user.properties, + lines[i].user.length * sizeof(mapUserProperty_t) + ); + } + v = lines[i].v1 - vertexes; wusedvertexes[v] = true; @@ -1998,6 +2114,18 @@ static void P_WriteTextmap(void) wusedvertexes[v] = true; } + for (i = 0; i < numsides; i++) + { + if (sides[i].user.length) + { + wsides[i].user.properties = memcpy( + Z_Malloc(sides[i].user.length * sizeof(mapUserProperty_t), PU_LEVEL, NULL), + sides[i].user.properties, + sides[i].user.length * sizeof(mapUserProperty_t) + ); + } + } + freetag = Tag_NextUnused(0); for (i = 0; i < nummapthings; i++) @@ -2279,6 +2407,28 @@ static void P_WriteTextmap(void) for (j = 0; j < NUMMAPTHINGSTRINGARGS; j++) if (mapthings[i].stringargs[j]) fprintf(f, "stringarg%s = \"%s\";\n", sizeu1(j), mapthings[i].stringargs[j]); + if (wmapthings[i].user.length > 0) + { + for (j = 0; j < wmapthings[i].user.length; j++) + { + mapUserProperty_t *const prop = &wmapthings[i].user.properties[j]; + switch (prop->type) + { + case USER_PROP_BOOL: + fprintf(f, "user_%s = %s;\n", prop->key, (prop->valueBool == true) ? "true" : "false"); + break; + case USER_PROP_INT: + fprintf(f, "user_%s = %d;\n", prop->key, prop->valueInt); + break; + case USER_PROP_FIXED: + fprintf(f, "user_%s = %f;\n", prop->key, FIXED_TO_FLOAT(prop->valueFixed)); + break; + case USER_PROP_STR: + fprintf(f, "user_%s = \"%s\";\n", prop->key, prop->valueStr); + break; + } + } + } fprintf(f, "}\n"); fprintf(f, "\n"); } @@ -2405,6 +2555,28 @@ static void P_WriteTextmap(void) fprintf(f, "monsterpush = true;\n"); if (wlines[i].activation & SPAC_IMPACT) fprintf(f, "impact = true;\n"); + if (wlines[i].user.length > 0) + { + for (j = 0; j < wlines[i].user.length; j++) + { + mapUserProperty_t *const prop = &wlines[i].user.properties[j]; + switch (prop->type) + { + case USER_PROP_BOOL: + fprintf(f, "user_%s = %s;\n", prop->key, (prop->valueBool == true) ? "true" : "false"); + break; + case USER_PROP_INT: + fprintf(f, "user_%s = %d;\n", prop->key, prop->valueInt); + break; + case USER_PROP_FIXED: + fprintf(f, "user_%s = %f;\n", prop->key, FIXED_TO_FLOAT(prop->valueFixed)); + break; + case USER_PROP_STR: + fprintf(f, "user_%s = \"%s\";\n", prop->key, prop->valueStr); + break; + } + } + } fprintf(f, "}\n"); fprintf(f, "\n"); } @@ -2426,6 +2598,28 @@ static void P_WriteTextmap(void) fprintf(f, "texturemiddle = \"%.*s\";\n", 8, textures[wsides[i].midtexture]->name); if (wsides[i].repeatcnt != 0) fprintf(f, "repeatcnt = %d;\n", wsides[i].repeatcnt); + if (wsides[i].user.length > 0) + { + for (j = 0; j < wsides[i].user.length; j++) + { + mapUserProperty_t *const prop = &wsides[i].user.properties[j]; + switch (prop->type) + { + case USER_PROP_BOOL: + fprintf(f, "user_%s = %s;\n", prop->key, (prop->valueBool == true) ? "true" : "false"); + break; + case USER_PROP_INT: + fprintf(f, "user_%s = %d;\n", prop->key, prop->valueInt); + break; + case USER_PROP_FIXED: + fprintf(f, "user_%s = %f;\n", prop->key, FIXED_TO_FLOAT(prop->valueFixed)); + break; + case USER_PROP_STR: + fprintf(f, "user_%s = \"%s\";\n", prop->key, prop->valueStr); + break; + } + } + } fprintf(f, "}\n"); fprintf(f, "\n"); } @@ -2609,6 +2803,28 @@ static void P_WriteTextmap(void) fprintf(f, "missilefloor = true;\n"); if (wsectors[i].activation & SECSPAC_CEILINGMISSILE) fprintf(f, "missileceiling = true;\n"); + if (wsectors[i].user.length > 0) + { + for (j = 0; j < wsectors[i].user.length; j++) + { + mapUserProperty_t *const prop = &wsectors[i].user.properties[j]; + switch (prop->type) + { + case USER_PROP_BOOL: + fprintf(f, "user_%s = %s;\n", prop->key, (prop->valueBool == true) ? "true" : "false"); + break; + case USER_PROP_INT: + fprintf(f, "user_%s = %d;\n", prop->key, prop->valueInt); + break; + case USER_PROP_FIXED: + fprintf(f, "user_%s = %f;\n", prop->key, FIXED_TO_FLOAT(prop->valueFixed)); + break; + case USER_PROP_STR: + fprintf(f, "user_%s = \"%s\";\n", prop->key, prop->valueStr); + break; + } + } + } fprintf(f, "}\n"); fprintf(f, "\n"); } @@ -2616,17 +2832,38 @@ static void P_WriteTextmap(void) fclose(f); for (i = 0; i < nummapthings; i++) + { if (wmapthings[i].tags.count) Z_Free(wmapthings[i].tags.tags); + if (wmapthings[i].user.length) + Z_Free(wmapthings[i].user.properties); + } + for (i = 0; i < numsectors; i++) + { if (wsectors[i].tags.count) Z_Free(wsectors[i].tags.tags); + if (wsectors[i].user.length) + Z_Free(wsectors[i].user.properties); + } + for (i = 0; i < numlines; i++) + { if (wlines[i].tags.count) Z_Free(wlines[i].tags.tags); + if (wlines[i].user.length) + Z_Free(wlines[i].user.properties); + } + + for (i = 0; i < numsides; i++) + { + if (wsides[i].user.length) + Z_Free(wsides[i].user.properties); + } + Z_Free(wmapthings); Z_Free(wvertexes); Z_Free(wsectors); @@ -2709,6 +2946,8 @@ static void P_LoadTextmap(void) memset(sc->stringargs, 0x00, NUMSECTORSTRINGARGS*sizeof(*sc->stringargs)); sc->activation = 0; + K_UserPropertiesClear(&sc->user); + textmap_colormap.used = false; textmap_colormap.lightcolor = 0; textmap_colormap.lightalpha = 25; @@ -2762,6 +3001,7 @@ static void P_LoadTextmap(void) ld->sidenum[1] = 0xffff; ld->activation = 0; + K_UserPropertiesClear(&ld->user); TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter); @@ -2786,6 +3026,8 @@ static void P_LoadTextmap(void) sd->sector = NULL; sd->repeatcnt = 0; + K_UserPropertiesClear(&sd->user); + TextmapParse(sidesPos[i], i, ParseTextmapSidedefParameter); if (!sd->sector) @@ -2811,6 +3053,8 @@ static void P_LoadTextmap(void) mt->layer = 0; mt->mobj = NULL; + K_UserPropertiesClear(&mt->user); + TextmapParse(mapthingsPos[i], i, ParseTextmapThingParameter); } } diff --git a/src/r_defs.h b/src/r_defs.h index 72892fc9e..ec4371e75 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -30,6 +30,8 @@ #include "taglist.h" +#include "k_mapuser.h" + #ifdef __cplusplus extern "C" { #endif @@ -551,6 +553,9 @@ struct sector_t INT32 args[NUMSECTORARGS]; char *stringargs[NUMSECTORSTRINGARGS]; sectoractionflags_t activation; + + // UDMF user-defined custom properties. + mapUserProperties_t user; }; // @@ -609,6 +614,9 @@ struct line_t char *text; // a concatenation of all front and back texture names, for linedef specials that require a string. INT16 callcount; // no. of calls left before triggering, for the "X calls" linedef specials, defaults to 0 + + // UDMF user-defined custom properties. + mapUserProperties_t user; }; struct side_t @@ -635,6 +643,9 @@ struct side_t char *text; // a concatenation of all top, bottom, and mid texture names, for linedef specials that require a string. extracolormap_t *colormap_data; // storage for colormaps; not applied to sectors. + + // UDMF user-defined custom properties. + mapUserProperties_t user; }; // diff --git a/src/typedef.h b/src/typedef.h index 25e2910e1..ebe816913 100644 --- a/src/typedef.h +++ b/src/typedef.h @@ -217,6 +217,10 @@ TYPEDEF (gpRank_t); TYPEDEF (midVote_t); TYPEDEF (midVoteGUI_t); +// k_mapuser.h +TYPEDEF (mapUserProperty_t); +TYPEDEF (mapUserProperties_t); + // lua_hudlib_drawlist.h typedef struct huddrawlist_s *huddrawlist_h;