Allow freesloting & editing precipprops

Allows for custom weather types.

In SOC:
```Freeslot
PRECIP_GROOVY

Weather PRECIP_GROOVY
Type = MT_PARTICLE
Effects = PRECIPFX_THUNDER|PRECIPFX_LIGHTNING```

In Lua:
```freeslot("PRECIP_GROOVY")

precipprops[PRECIP_GROOVY] = {
    type = MT_PARTICLE,
    effects = PRECIPFX_THUNDER|PRECIPFX_LIGHTNING
}```

Then in level header, simply set `Weather = PRECIP_GROOVY`.

Other properties are part of the object itself:
- Falling speed is set as the object's speed
- Sound ID is set via the object's SeeSound
- Sound frequency is set by the object's Mass.
This commit is contained in:
Sally Coolatta 2022-05-31 09:03:06 -04:00
parent 30f60585be
commit 4c3f89cdf7
13 changed files with 338 additions and 15 deletions

View file

@ -186,6 +186,27 @@ static inline int lib_freeslot(lua_State *L)
}
}
}
else if (fastcmp(type, "PRECIP"))
{
// Search if we already have a PRECIP by that name...
preciptype_t i;
for (i = PRECIP_FIRSTFREESLOT; i < precip_freeslot; i++)
if (fastcmp(word, precipprops[i].name))
break;
// We don't, so allocate a new one.
if (i >= precip_freeslot) {
if (precip_freeslot < MAXPRECIP)
{
CONS_Printf("Weather PRECIP_%s allocated.\n",word);
precipprops[i].name = Z_StrDup(word);
lua_pushinteger(L, precip_freeslot);
r++;
precip_freeslot++;
} else
CONS_Alert(CONS_WARNING, "Ran out of free PRECIP slots!\n");
}
}
Z_Free(s);
lua_remove(L, 1);
continue;

View file

@ -481,6 +481,25 @@ void readfreeslots(MYFILE *f)
lastcustomtol <<= 1;
}
}
else if (fastcmp(type, "PRECIP"))
{
// Search if we already have a PRECIP by that name...
for (i = PRECIP_FIRSTFREESLOT; i < (int)precip_freeslot; i++)
if (fastcmp(word, precipprops[i].name))
break;
// We found it? Then don't allocate another one.
if (i < (int)precip_freeslot)
continue;
// We don't, so allocate a new one.
if (precip_freeslot < MAXPRECIP)
{
precipprops[i].name = Z_StrDup(word);
precip_freeslot++;
} else
deh_warning("Ran out of free PRECIP slots!\n");
}
else
deh_warning("Freeslots: unknown enum class '%s' for '%s_%s'", type, type, word);
}
@ -1528,7 +1547,7 @@ void readlevelheader(MYFILE *f, INT32 num)
strlwr(mapheaderinfo[num-1]->forcecharacter); // skin names are lowercase
}
else if (fastcmp(word, "WEATHER"))
mapheaderinfo[num-1]->weather = (UINT8)get_number(word2);
mapheaderinfo[num-1]->weather = get_precip(word2);
else if (fastcmp(word, "SKYTEXTURE"))
deh_strlcpy(mapheaderinfo[num-1]->skytexture, word2,
sizeof(mapheaderinfo[num-1]->skytexture), va("Level header %d: sky texture", num));
@ -4086,6 +4105,61 @@ if (!followers[numfollowers].field) \
Z_Free(s);
}
void readweather(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word;
char *word2;
char *tmp;
do
{
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
// First remove trailing newline, if there is one
tmp = strchr(s, '\n');
if (tmp)
*tmp = '\0';
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
if (s == tmp)
continue; // Skip comment lines, but don't break.
// Set / reset word
word = s;
// Get the part before the " = "
tmp = strchr(s, '=');
if (tmp)
*(tmp-1) = '\0';
else
break;
strupr(word);
// Now get the part after
word2 = tmp += 2;
if (fastcmp(word, "TYPE"))
{
precipprops[num].type = get_mobjtype(word2);
}
else if (fastcmp(word, "EFFECTS"))
{
precipprops[num].effects = get_number(word2);
}
else
deh_warning("Weather %d : unknown word '%s'", num, word);
}
} while (!myfeof(f));
Z_Free(s);
}
//
//
//
@ -4222,6 +4296,24 @@ menutype_t get_menutype(const char *word)
return GT_COOP;
}*/
preciptype_t get_precip(const char *word)
{ // Returns the value of PRECIP_ enumerations
preciptype_t i;
if (*word >= '0' && *word <= '9')
return atoi(word);
if (fastncmp("PRECIP_",word,4))
word += 7; // take off the PRECIP_
for (i = 0; i < MAXPRECIP; i++)
{
if (precipprops[i].name == NULL)
break;
if (fasticmp(word, precipprops[i].name))
return i;
}
deh_warning("Couldn't find weather type named 'PRECIP_%s'",word);
return PRECIP_RAIN;
}
/// \todo Make ANY of this completely over-the-top math craziness obey the order of operations.
static fixed_t op_mul(fixed_t a, fixed_t b) { return a*b; }
static fixed_t op_div(fixed_t a, fixed_t b) { return a/b; }

View file

@ -85,5 +85,7 @@ void clear_conditionsets(void);
void readcupheader(MYFILE *f, cupheader_t *cup);
void readfollower(MYFILE *f);
preciptype_t get_precip(const char *word);
void readweather(MYFILE *f, INT32 num);
#endif

View file

@ -6741,6 +6741,10 @@ struct int_const_s const INT_CONST[] = {
{"SPOT_WEAK",SPOT_WEAK},
{"SPOT_BUMP",SPOT_BUMP},
// precipeffect_t
{"PRECIPFX_THUNDER",PRECIPFX_THUNDER},
{"PRECIPFX_LIGHTNING",PRECIPFX_LIGHTNING},
{NULL,0}
};

View file

@ -576,6 +576,18 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
readcupheader(f, cup);
}
if (fastcmp(word, "WEATHER") || fastcmp(word, "PRECIP") || fastcmp(word, "PRECIPITATION"))
{
if (i == 0 && word2[0] != '0') // If word2 isn't a number
i = get_precip(word2); // find a weather type by name
if (i < MAXPRECIP && i > 0)
readweather(f, i);
else
{
deh_warning("Weather number %d out of range (1 - %d)", i, MAXPRECIP-1);
ignorelines(f);
}
}
else if (fastcmp(word, "RINGRACERS"))
{
if (isdigit(word2[0]))

View file

@ -41,8 +41,7 @@ extern UINT32 mapmusresume;
// Use other bits if necessary.
extern UINT32 maptol;
extern UINT8 globalweather;
extern UINT8 curWeather;
extern INT32 cursaveslot;
//extern INT16 lastmapsaved;
extern INT16 lastmaploaded;
@ -64,15 +63,22 @@ extern tic_t marathontime;
extern UINT8 numgameovers;
extern SINT8 startinglivesbalance[maxgameovers+1];
#define NUMPRECIPFREESLOTS 64
typedef enum
{
PRECIP_NONE = 0,
PRECIP_RAIN,
PRECIP_SNOW,
PRECIP_BLIZZARD,
PRECIP_STORM,
PRECIP_STORM_NORAIN,
PRECIP_STORM_NOSTRIKES,
PRECIP_FIRSTFREESLOT,
PRECIP_LASTFREESLOT = PRECIP_FIRSTFREESLOT + NUMPRECIPFREESLOTS - 1,
MAXPRECIP
} preciptype_t;
@ -84,11 +90,16 @@ typedef enum
typedef struct
{
const char *name;
mobjtype_t type;
precipeffect_t effects;
} precipprops_t;
extern precipprops_t precipprops[MAXPRECIP];
extern preciptype_t precip_freeslot;
extern preciptype_t globalweather;
extern preciptype_t curWeather;
// Set if homebrew PWAD stuff has been added.
extern boolean modifiedgame;

View file

@ -93,20 +93,22 @@ UINT32 mapmusresume;
INT16 gamemap = 1;
UINT32 maptol;
UINT8 globalweather = PRECIP_NONE;
UINT8 curWeather = PRECIP_NONE;
preciptype_t globalweather = PRECIP_NONE;
preciptype_t curWeather = PRECIP_NONE;
precipprops_t precipprops[MAXPRECIP] =
{
{MT_NULL, 0}, // PRECIP_NONE
{MT_RAIN, 0}, // PRECIP_RAIN
{MT_SNOWFLAKE, 0}, // PRECIP_SNOW
{MT_BLIZZARDSNOW, 0}, // PRECIP_BLIZZARD
{MT_RAIN, PRECIPFX_THUNDER|PRECIPFX_LIGHTNING}, // PRECIP_STORM
{MT_NULL, PRECIPFX_THUNDER|PRECIPFX_LIGHTNING}, // PRECIP_STORM_NORAIN
{MT_RAIN, PRECIPFX_THUNDER} // PRECIP_STORM_NOSTRIKES
{"NONE", MT_NULL, 0}, // PRECIP_NONE
{"RAIN", MT_RAIN, 0}, // PRECIP_RAIN
{"SNOW", MT_SNOWFLAKE, 0}, // PRECIP_SNOW
{"BLIZZARD", MT_BLIZZARDSNOW, 0}, // PRECIP_BLIZZARD
{"STORM", MT_RAIN, PRECIPFX_THUNDER|PRECIPFX_LIGHTNING}, // PRECIP_STORM
{"STORM_NORAIN", MT_NULL, PRECIPFX_THUNDER|PRECIPFX_LIGHTNING}, // PRECIP_STORM_NORAIN
{"STORM_NOSTRIKES", MT_RAIN, PRECIPFX_THUNDER} // PRECIP_STORM_NOSTRIKES
};
preciptype_t precip_freeslot = PRECIP_FIRSTFREESLOT;
INT32 cursaveslot = 0; // Auto-save 1p savegame slot
//INT16 lastmapsaved = 0; // Last map we auto-saved at
INT16 lastmaploaded = 0; // Last map the game loaded

View file

@ -163,6 +163,7 @@ static const struct {
{META_SPRITEINFO, "spriteinfo_t"},
{META_PIVOTLIST, "spriteframepivot_t[]"},
{META_FRAMEPIVOT, "spriteframepivot_t"},
{META_PRECIPPROPS, "precipprops_t"},
{META_TAGLIST, "taglist"},

View file

@ -1758,6 +1758,152 @@ static int colorramp_len(lua_State *L)
return 1;
}
//////////////////
// PRECIP PROPS //
//////////////////
// Arbitrary precipprops[] table index -> precipprops_t *
static int lib_getPrecipProps(lua_State *L)
{
INT32 i;
lua_remove(L, 1);
i = luaL_checkinteger(L, 1);
if (i <= 0 || i >= MAXPRECIP)
return luaL_error(L, "precipprops[] index %d out of range (1 - %d)", i, MAXPRECIP-1);
LUA_PushUserdata(L, &precipprops[i], META_PRECIPPROPS);
return 1;
}
// Lua table full of data -> precipprops[]
static int lib_setPrecipProps(lua_State *L)
{
precipprops_t *props;
lua_remove(L, 1); // don't care about precipprops[] userdata.
{
INT32 i = luaL_checkinteger(L, 1);
if (i <= 0 || i >= MAXPRECIP)
return luaL_error(L, "precipprops[] index %d out of range (1 - %d)", i, MAXPRECIP-1);
props = &precipprops[i]; // get the precipprops to assign to.
}
luaL_checktype(L, 2, LUA_TTABLE); // check that we've been passed a table.
lua_remove(L, 1); // pop preciptype num, don't need it any more.
lua_settop(L, 1); // cut the stack here. the only thing left now is the table of data we're assigning to the precipprops.
if (hud_running)
return luaL_error(L, "Do not alter precipprops in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter precipprops in CMD building code!");
// clear the precipprops to start with, in case of missing table elements
// done manually because we do not want to clear name
props->type = MT_NULL;
props->effects = 0;
lua_pushnil(L);
while (lua_next(L, 1)) {
lua_Integer i = 0;
const char *str = NULL;
lua_Integer value;
if (lua_isnumber(L, 2))
i = lua_tointeger(L, 2);
else
str = luaL_checkstring(L, 2);
if (i == 1 || (str && fastcmp(str, "type")))
{
value = luaL_checkinteger(L, 3);
if (value < MT_NULL || value >= NUMMOBJTYPES)
return luaL_error(L, "type number %d is invalid.", value);
props->type = luaL_checkinteger(L, 3);
}
else if (i == 2 || (str && fastcmp(str, "effects")))
{
props->effects = luaL_checkinteger(L, 3);
}
lua_pop(L, 1);
}
return 0;
}
// #precipprops -> MAXPRECIP
static int lib_precippropslen(lua_State *L)
{
lua_pushinteger(L, MAXPRECIP);
return 1;
}
// precipprops_t *, field -> number
static int precipprops_get(lua_State *L)
{
precipprops_t *props = *((precipprops_t **)luaL_checkudata(L, 1, META_PRECIPPROPS));
const char *field = luaL_checkstring(L, 2);
I_Assert(props != NULL);
I_Assert(props >= precipprops);
if (fastcmp(field, "type"))
{
lua_pushinteger(L, props->type);
}
else if (fastcmp(field,"effects"))
{
lua_pushinteger(L, props->effects);
}
else
{
CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "precipprops_t", field);
return 0;
}
return 1;
}
// precipprops_t *, field, number -> precipprops[]
static int precipprops_set(lua_State *L)
{
precipprops_t *props = *((precipprops_t **)luaL_checkudata(L, 1, META_PRECIPPROPS));
const char *field = luaL_checkstring(L, 2);
if (hud_running)
return luaL_error(L, "Do not alter precipprops in HUD rendering code!");
if (hook_cmd_running)
return luaL_error(L, "Do not alter precipprops in CMD building code!");
I_Assert(props != NULL);
I_Assert(props >= precipprops);
if (fastcmp(field, "type"))
{
props->type = luaL_checkinteger(L, 3);
}
else if (fastcmp(field, "effects"))
{
props->effects = luaL_checkinteger(L, 3);
}
else
{
return luaL_error(L, LUA_QL("precipprops_t") " has no field named " LUA_QS, field);
}
return 0;
}
// precipprops_t * -> PRECIP_*
static int precipprops_num(lua_State *L)
{
precipprops_t *props = *((precipprops_t **)luaL_checkudata(L, 1, META_PRECIPPROPS));
I_Assert(props != NULL);
I_Assert(props >= precipprops);
lua_pushinteger(L, props - precipprops);
return 1;
}
//////////////////////////////
//
// Now push all these functions into the Lua state!
@ -1861,6 +2007,17 @@ int LUA_InfoLib(lua_State *L)
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
luaL_newmetatable(L, META_PRECIPPROPS);
lua_pushcfunction(L, precipprops_get);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, precipprops_set);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, precipprops_num);
lua_setfield(L, -2, "__len");
lua_pop(L, 1);
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getSprname);
@ -1961,6 +2118,19 @@ int LUA_InfoLib(lua_State *L)
lua_setmetatable(L, -2);
lua_setglobal(L, "spriteinfo");
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getPrecipProps);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_setPrecipProps);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, lib_precippropslen);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_setglobal(L, "precipprops");
luaL_newmetatable(L, META_LUABANKS);
lua_pushcfunction(L, lib_getluabanks);
lua_setfield(L, -2, "__index");

View file

@ -28,6 +28,7 @@ extern lua_State *gL;
#define META_SPRITEINFO "SPRITEINFO_T*"
#define META_PIVOTLIST "SPRITEFRAMEPIVOT_T[]"
#define META_FRAMEPIVOT "SPRITEFRAMEPIVOT_T*"
#define META_PRECIPPROPS "PRECIPPROPS_T*"
#define META_TAGLIST "TAGLIST"

View file

@ -390,7 +390,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
mapheaderinfo[num]->muspostbossfadein = 0;
mapheaderinfo[num]->musforcereset = -1;
mapheaderinfo[num]->forcecharacter[0] = '\0';
mapheaderinfo[num]->weather = 0;
mapheaderinfo[num]->weather = PRECIP_NONE;
snprintf(mapheaderinfo[num]->skytexture, 5, "SKY1");
mapheaderinfo[num]->skytexture[4] = 0;
mapheaderinfo[num]->skybox_scalex = 16;

View file

@ -1769,11 +1769,18 @@ void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller)
//
// Switches the weather!
//
void P_SwitchWeather(UINT8 newWeather)
void P_SwitchWeather(preciptype_t newWeather)
{
boolean purge = false;
mobjtype_t swap = MT_NULL;
if (newWeather >= precip_freeslot)
{
// Weather type invalid, set to no weather.
CONS_Debug(DBG_SETUP, "Weather ID %d out of bounds\n", newWeather);
newWeather = PRECIP_NONE;
}
if (precipprops[newWeather].type == MT_NULL)
{
// New type is null, we want to purge the weather.

View file

@ -67,7 +67,7 @@ void P_CrossSpecialLine(line_t *ld, INT32 side, mobj_t *thing);
void P_SetupSignExit(player_t *player);
boolean P_IsFlagAtBase(mobjtype_t flag);
void P_SwitchWeather(UINT8 newWeather);
void P_SwitchWeather(preciptype_t newWeather);
boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller);
void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller);