diff --git a/src/deh_lua.c b/src/deh_lua.c index 6e892d5e2..944a8b67a 100644 --- a/src/deh_lua.c +++ b/src/deh_lua.c @@ -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; diff --git a/src/deh_soc.c b/src/deh_soc.c index 617857609..8617ad3bb 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -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; } diff --git a/src/deh_soc.h b/src/deh_soc.h index e2319ab14..7381e548c 100644 --- a/src/deh_soc.h +++ b/src/deh_soc.h @@ -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 diff --git a/src/deh_tables.c b/src/deh_tables.c index b01e3ff4b..e421d2b4b 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -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} }; diff --git a/src/dehacked.c b/src/dehacked.c index 7288ac374..970f84b9a 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -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])) diff --git a/src/doomstat.h b/src/doomstat.h index 4b3f75d13..5a2478b48 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -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; diff --git a/src/g_game.c b/src/g_game.c index 896ea00ea..2939dee2e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -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 diff --git a/src/lua_baselib.c b/src/lua_baselib.c index a461ac80b..301665e45 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -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"}, diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 880496770..88cb32ed9 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -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"); diff --git a/src/lua_libs.h b/src/lua_libs.h index a6580622b..bc8e4141e 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -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" diff --git a/src/p_setup.c b/src/p_setup.c index a47077ba5..372cfa91b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -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; diff --git a/src/p_spec.c b/src/p_spec.c index 53d3b5370..56e1456ea 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -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. diff --git a/src/p_spec.h b/src/p_spec.h index cbcbf366d..6f84c3aac 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -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);