ACS: UDMF user properties

Lines, sides, sectors, and things now all support the "Custom" tab properly. Label a property as `user_[whatever you want]` in this tab, and it will be added to the structure. ACS will then be able to retrieve it using the `Get[x]UserProperty()` function.
This commit is contained in:
Sally Coolatta 2023-04-26 09:47:39 -04:00
parent a366948c62
commit d9382f2100
10 changed files with 822 additions and 4 deletions

View file

@ -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)

View file

@ -2226,3 +2226,268 @@ bool CallFunc_SetSectorProperty(ACSVM::Thread *thread, const ACSVM::Word *argV,
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 *>(thread)->info;
Environment *env = &ACSEnv;
mtag_t tag = 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, lineID, 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<INT32>(prop->valueBool);
break;
}
case USER_PROP_INT:
{
ret = prop->valueInt;
break;
}
case USER_PROP_FIXED:
{
ret = static_cast<INT32>(prop->valueFixed);
break;
}
case USER_PROP_STR:
{
ret = static_cast<INT32>( ~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 *>(thread)->info;
Environment *env = &ACSEnv;
mtag_t tag = 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, lineID, 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<INT32>(prop->valueBool);
break;
}
case USER_PROP_INT:
{
ret = prop->valueInt;
break;
}
case USER_PROP_FIXED:
{
ret = static_cast<INT32>(prop->valueFixed);
break;
}
case USER_PROP_STR:
{
ret = static_cast<INT32>( ~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 *>(thread)->info;
Environment *env = &ACSEnv;
mtag_t tag = 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, sectorID, activatorID)) != -1)
{
sector = &sectors[ sectorID ];
}
if (sector != NULL)
{
prop = K_UserPropertyFind(&sector->user, key);
}
if (prop != NULL)
{
switch (prop->type)
{
case USER_PROP_BOOL:
{
ret = static_cast<INT32>(prop->valueBool);
break;
}
case USER_PROP_INT:
{
ret = prop->valueInt;
break;
}
case USER_PROP_FIXED:
{
ret = static_cast<INT32>(prop->valueFixed);
break;
}
case USER_PROP_STR:
{
ret = static_cast<INT32>( ~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 *>(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<INT32>(prop->valueBool);
break;
}
case USER_PROP_INT:
{
ret = prop->valueInt;
break;
}
case USER_PROP_FIXED:
{
ret = static_cast<INT32>(prop->valueFixed);
break;
}
case USER_PROP_STR:
{
ret = static_cast<INT32>( ~env->getString( prop->valueStr )->idx );
break;
}
}
}
thread->dataStk.push(ret);
return false;
}

View file

@ -90,4 +90,9 @@ bool CallFunc_SetSideProperty(ACSVM::Thread *thread, const ACSVM::Word *argV, AC
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_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__

View file

@ -130,16 +130,16 @@ Environment::Environment()
// ACS implementations.
addFuncDataACS0( 1, addCallFunc(CallFunc_GetLineProperty));
addFuncDataACS0( 2, addCallFunc(CallFunc_SetLineProperty));
//addFuncDataACS0( 3, addCallFunc(CallFunc_GetLineUserProperty));
addFuncDataACS0( 3, addCallFunc(CallFunc_GetLineUserProperty));
addFuncDataACS0( 4, addCallFunc(CallFunc_GetSectorProperty));
addFuncDataACS0( 5, addCallFunc(CallFunc_SetSectorProperty));
//addFuncDataACS0( 6, addCallFunc(CallFunc_GetSectorUserProperty));
addFuncDataACS0( 6, addCallFunc(CallFunc_GetSectorUserProperty));
addFuncDataACS0( 7, addCallFunc(CallFunc_GetSideProperty));
addFuncDataACS0( 8, addCallFunc(CallFunc_SetSideProperty));
//addFuncDataACS0( 9, addCallFunc(CallFunc_GetSideUserProperty));
addFuncDataACS0( 9, addCallFunc(CallFunc_GetSideUserProperty));
//addFuncDataACS0( 10, addCallFunc(CallFunc_GetThingProperty));
//addFuncDataACS0( 11, addCallFunc(CallFunc_SetThingProperty));
//addFuncDataACS0( 12, addCallFunc(CallFunc_GetThingUserProperty));
addFuncDataACS0( 12, addCallFunc(CallFunc_GetThingUserProperty));
//addFuncDataACS0( 13, addCallFunc(CallFunc_GetPlayerProperty));
//addFuncDataACS0( 14, addCallFunc(CallFunc_SetPlayerProperty));
//addFuncDataACS0( 15, addCallFunc(CallFunc_GetPolyobjProperty));

View file

@ -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;
};

180
src/k_mapuser.c Normal file
View file

@ -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_STATIC, // PU_LEVEL
NULL,
sizeof(mapUserProperty_t) * 8
);
}
prop = &user->properties[ user->length ];
prop->key = Z_Malloc(keyLength + 1, PU_STATIC, 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_STATIC, 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;
}

94
src/k_mapuser.h Normal file
View file

@ -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__

View file

@ -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,80 @@ 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 char *key = param + 5;
const size_t valLen = strlen(val);
UINT8 numberType = PROP_NUM_TYPE_INT;
size_t i = 0;
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_NA:
default:
{
// Value is a boolean or a string.
// Unfortunately, our UDMF parser discards the quotation marks,
// so we can't differentiate strings from booleans properly.
// Don't feel like tearing it apart to fix that, so we just
// turn true & false into booleans, even if they were actually
// supposed to be text.
boolean vBool = fastcmp("true", val);
if (vBool == true || fastcmp("false", val))
{
// Value is *probably* a boolean.
K_UserPropertyPush(user, key, USER_PROP_BOOL, &vBool);
}
else
{
// Value is a string.
K_UserPropertyPush(user, key, USER_PROP_STR, &val);
}
break;
}
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;
}
}
}
}
static void ParseTextmapVertexParameter(UINT32 i, const char *param, const char *val)
{
if (fastcmp(param, "x"))
@ -1657,6 +1732,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(&sectors[i].user, param, val);
}
static void ParseTextmapSidedefParameter(UINT32 i, const char *param, const char *val)
@ -1675,6 +1752,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 +1861,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 +1920,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 +2060,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 +2096,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 +2112,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 +2405,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 +2553,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 +2596,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 +2801,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 +2830,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 +2944,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 +2999,7 @@ static void P_LoadTextmap(void)
ld->sidenum[1] = 0xffff;
ld->activation = 0;
K_UserPropertiesClear(&ld->user);
TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter);
@ -2786,6 +3024,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 +3051,8 @@ static void P_LoadTextmap(void)
mt->layer = 0;
mt->mobj = NULL;
K_UserPropertiesClear(&mt->user);
TextmapParse(mapthingsPos[i], i, ParseTextmapThingParameter);
}
}

View file

@ -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;
};
//

View file

@ -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;