diff --git a/src/deh_lua.c b/src/deh_lua.c index 6e892d5e2..f4c3bcf41 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; @@ -475,6 +496,21 @@ static inline int lib_getenum(lua_State *L) if (mathlib) return luaL_error(L, "menutype '%s' could not be found.\n", word); return 0; } + else if (fastncmp("PRECIP_",word,7)) { + p = word+7; + for (i = 0; i < MAXPRECIP; i++) + { + if (precipprops[i].name == NULL) + break; + + if (fastcmp(p, precipprops[i].name)) + { + lua_pushinteger(L, PRECIP_NONE + i); + return 1; + } + } + return luaL_error(L, "weather type '%s' does not exist.\n", word); + } else if (!mathlib && fastncmp("A_",word,2)) { char *caps; // Try to get a Lua action first. 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..9a733141f 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -6741,6 +6741,11 @@ 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}, + {"PRECIPFX_WATERPARTICLES",PRECIPFX_WATERPARTICLES}, + {NULL,0} }; diff --git a/src/dehacked.c b/src/dehacked.c index 7288ac374..b813106c4 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -576,6 +576,18 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) readcupheader(f, cup); } + else 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..4e782e59c 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,31 +63,44 @@ 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; typedef enum { PRECIPFX_THUNDER = 1, - PRECIPFX_LIGHTNING = 1<<1 + PRECIPFX_LIGHTNING = 1<<1, + PRECIPFX_WATERPARTICLES = 1<<2 } precipeffect_t; 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/info.c b/src/info.c index 754530837..78181c2e7 100644 --- a/src/info.c +++ b/src/info.c @@ -18246,7 +18246,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_SPLASH1, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound - -72*FRACUNIT, // speed + 72*FRACUNIT, // speed 1*FRACUNIT, // radius 8*FRACUNIT, // height 0, // display offset @@ -18273,7 +18273,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound - -2*FRACUNIT, // speed + 2*FRACUNIT, // speed 4*FRACUNIT, // radius 4*FRACUNIT, // height 0, // display offset @@ -18300,7 +18300,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // deathstate S_NULL, // xdeathstate sfx_None, // deathsound - -24*FRACUNIT, // speed + 24*FRACUNIT, // speed 4*FRACUNIT, // radius 4*FRACUNIT, // height 0, // display offset 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..1aaa207de 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -1758,6 +1758,151 @@ 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 + // make sure we do not clear the name + memset(props + sizeof(props->name), 0, sizeof(precipprops_t) - sizeof(props->name)); + + 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 +2006,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 +2117,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_local.h b/src/p_local.h index 82b1d445c..bb0d02fb2 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -277,6 +277,7 @@ void P_RespawnSpecials(void); mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type); +void P_CalculatePrecipFloor(precipmobj_t *mobj); void P_RecalcPrecipInSector(sector_t *sector); void P_PrecipitationEffects(void); diff --git a/src/p_mobj.c b/src/p_mobj.c index 5219fb707..ea20da99e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3760,19 +3760,25 @@ animonly: P_CyclePlayerMobjState(mobj); } -static void CalculatePrecipFloor(precipmobj_t *mobj) +void P_CalculatePrecipFloor(precipmobj_t *mobj) { // recalculate floorz each time const sector_t *mobjsecsubsec; + boolean setWater = false; + if (mobj && mobj->subsector && mobj->subsector->sector) mobjsecsubsec = mobj->subsector->sector; else return; + + mobj->precipflags &= ~PCF_INVISIBLE; mobj->floorz = P_GetSectorFloorZAt(mobjsecsubsec, mobj->x, mobj->y); + mobj->ceilingz = P_GetSectorCeilingZAt(mobjsecsubsec, mobj->x, mobj->y); + if (mobjsecsubsec->ffloors) { ffloor_t *rover; - fixed_t topheight; + fixed_t height; for (rover = mobjsecsubsec->ffloors; rover; rover = rover->next) { @@ -3780,14 +3786,44 @@ static void CalculatePrecipFloor(precipmobj_t *mobj) if (!(rover->flags & FF_EXISTS)) continue; - if (!(rover->flags & FF_BLOCKOTHERS) && !(rover->flags & FF_SWIMMABLE)) - continue; + if (precipprops[curWeather].effects & PRECIPFX_WATERPARTICLES) + { + if (!(rover->flags & FF_SWIMMABLE)) + continue; - topheight = P_GetFFloorTopZAt(rover, mobj->x, mobj->y); - if (topheight > mobj->floorz) - mobj->floorz = topheight; + if (setWater == false) + { + mobj->ceilingz = P_GetFFloorTopZAt(rover, mobj->x, mobj->y); + mobj->floorz = P_GetFFloorBottomZAt(rover, mobj->x, mobj->y); + setWater = true; + } + else + { + height = P_GetFFloorTopZAt(rover, mobj->x, mobj->y); + if (height > mobj->ceilingz) + mobj->ceilingz = height; + + height = P_GetFFloorBottomZAt(rover, mobj->x, mobj->y); + if (height < mobj->floorz) + mobj->floorz = height; + } + } + else + { + if (!(rover->flags & FF_BLOCKOTHERS) && !(rover->flags & FF_SWIMMABLE)) + continue; + + height = P_GetFFloorTopZAt(rover, mobj->x, mobj->y); + if (height > mobj->floorz) + mobj->floorz = height; + } } } + + if ((precipprops[curWeather].effects & PRECIPFX_WATERPARTICLES) && setWater == false) + { + mobj->precipflags |= PCF_INVISIBLE; + } } void P_RecalcPrecipInSector(sector_t *sector) @@ -3800,7 +3836,7 @@ void P_RecalcPrecipInSector(sector_t *sector) sector->moved = true; // Recalc lighting and things too, maybe for (psecnode = sector->touching_preciplist; psecnode; psecnode = psecnode->m_thinglist_next) - CalculatePrecipFloor(psecnode->m_thing); + P_CalculatePrecipFloor(psecnode->m_thing); } // @@ -3812,11 +3848,12 @@ void P_NullPrecipThinker(precipmobj_t *mobj) { //(void)mobj; mobj->precipflags &= ~PCF_THUNK; + R_ResetPrecipitationMobjInterpolationState(mobj); } void P_PrecipThinker(precipmobj_t *mobj) { - R_ResetPrecipitationMobjInterpolationState(mobj); + boolean flip = (mobj->precipflags & PCF_FLIP); P_CycleStateAnimation((mobj_t *)mobj); @@ -3824,9 +3861,10 @@ void P_PrecipThinker(precipmobj_t *mobj) { // Reset to ceiling! P_SetPrecipMobjState(mobj, mobj->info->spawnstate); - mobj->z = mobj->ceilingz; - mobj->momz = mobj->info->speed; + mobj->z = (flip) ? (mobj->floorz) : (mobj->ceilingz); + mobj->momz = -mobj->info->speed; mobj->precipflags &= ~PCF_SPLASH; + R_ResetPrecipitationMobjInterpolationState(mobj); } if (mobj->tics != -1) @@ -3858,18 +3896,20 @@ void P_PrecipThinker(precipmobj_t *mobj) if (mobj->precipflags & PCF_SPLASH) return; + mobj->z += mobj->momz; + // adjust height - if ((mobj->z += mobj->momz) <= mobj->floorz) + if ((flip) ? (mobj->z >= mobj->ceilingz) : (mobj->z <= mobj->floorz)) { if ((mobj->info->deathstate == S_NULL) || (mobj->precipflags & PCF_PIT)) // no splashes on sky or bottomless pits { - mobj->z = mobj->ceilingz; + mobj->z = (flip) ? (mobj->floorz) : (mobj->ceilingz); R_ResetPrecipitationMobjInterpolationState(mobj); } else { P_SetPrecipMobjState(mobj, mobj->info->deathstate); - mobj->z = mobj->floorz; + mobj->z = (flip) ? (mobj->ceilingz) : (mobj->floorz); mobj->precipflags |= PCF_SPLASH; R_ResetPrecipitationMobjInterpolationState(mobj); } @@ -10341,8 +10381,8 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype { const mobjinfo_t *info = &mobjinfo[type]; state_t *st; + fixed_t start_z = INT32_MIN; precipmobj_t *mobj = Z_Calloc(sizeof (*mobj), PU_LEVEL, NULL); - fixed_t starting_floorz; mobj->type = type; mobj->info = info; @@ -10364,26 +10404,43 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype // set subsector and/or block links P_SetPrecipitationThingPosition(mobj); - mobj->floorz = starting_floorz = P_GetSectorFloorZAt (mobj->subsector->sector, x, y); - mobj->ceilingz = P_GetSectorCeilingZAt(mobj->subsector->sector, x, y); + mobj->floorz = P_GetSectorFloorZAt (mobj->subsector->sector, x, y); + mobj->ceilingz = P_GetSectorCeilingZAt(mobj->subsector->sector, x, y); mobj->floorrover = NULL; mobj->ceilingrover = NULL; mobj->z = z; - mobj->momz = info->speed; + mobj->momz = -info->speed; + + if (info->speed < 0) + { + mobj->precipflags |= PCF_FLIP; + } + + start_z = mobj->floorz; mobj->thinker.function.acp1 = (actionf_p1)P_NullPrecipThinker; P_AddThinker(THINK_PRECIP, &mobj->thinker); - CalculatePrecipFloor(mobj); + P_CalculatePrecipFloor(mobj); - if (mobj->floorz != starting_floorz) - mobj->precipflags |= PCF_FOF; - else if (GETSECSPECIAL(mobj->subsector->sector->special, 1) == 7 - || GETSECSPECIAL(mobj->subsector->sector->special, 1) == 6 - || mobj->subsector->sector->floorpic == skyflatnum) - mobj->precipflags |= PCF_PIT; + if (mobj->floorz != start_z) + { + ; //mobj->precipflags |= PCF_FOF; + } + else + { + INT32 special = GETSECSPECIAL(mobj->subsector->sector->special, 1); + boolean sFlag = (mobj->precipflags & PCF_FLIP) ? (mobj->subsector->sector->flags & SF_FLIPSPECIAL_CEILING) : (mobj->subsector->sector->flags & SF_FLIPSPECIAL_FLOOR); + boolean pitFloor = ((special == 6 || special == 7) && sFlag); + boolean skyFloor = (mobj->precipflags & PCF_FLIP) ? (mobj->subsector->sector->ceilingpic == skyflatnum) : (mobj->subsector->sector->floorpic == skyflatnum); + + if (pitFloor || skyFloor) + { + mobj->precipflags |= PCF_PIT; + } + } R_ResetPrecipitationMobjInterpolationState(mobj); @@ -10588,13 +10645,19 @@ consvar_t cv_itemrespawn = CVAR_INIT ("respawnitem", "On", CV_NETVAR, CV_OnOff, void P_SpawnPrecipitation(void) { INT32 i, j, k; - mobjtype_t type = precipprops[curWeather].type; - UINT8 randomstates = (UINT8)mobjinfo[type].damage; + + const mobjtype_t type = precipprops[curWeather].type; + const UINT8 randomstates = (UINT8)mobjinfo[type].damage; + const boolean flip = (mobjinfo[type].speed < 0); + fixed_t basex, basey, x, y, z, height; + UINT16 numparticles = 0; + boolean condition = false; + subsector_t *precipsector = NULL; precipmobj_t *rainmo = NULL; - if (dedicated || !cv_drawdist_precip.value || curWeather == PRECIP_NONE) // SRB2Kart + if (dedicated || !cv_drawdist_precip.value || type == MT_NULL) return; // Use the blockmap to narrow down our placing patterns @@ -10603,58 +10666,87 @@ void P_SpawnPrecipitation(void) basex = bmaporgx + (i % bmapwidth) * MAPBLOCKSIZE; basey = bmaporgy + (i / bmapwidth) * MAPBLOCKSIZE; + x = basex + ((M_RandomKey(MAPBLOCKUNITS << 3) << FRACBITS) >> 3); + y = basey + ((M_RandomKey(MAPBLOCKUNITS << 3) << FRACBITS) >> 3); + + precipsector = R_PointInSubsectorOrNull(x, y); + + // No sector? Stop wasting time, + // move on to the next entry in the blockmap + if (!precipsector) + continue; + + // Not in a sector with visible sky? + if (precipprops[curWeather].effects & PRECIPFX_WATERPARTICLES) { - UINT16 numparticles = 0; + condition = false; - x = basex + ((M_RandomKey(MAPBLOCKUNITS<<3)<>3); - y = basey + ((M_RandomKey(MAPBLOCKUNITS<<3)<>3); - - precipsector = R_PointInSubsectorOrNull(x, y); - - // No sector? Stop wasting time, - // move on to the next entry in the blockmap - if (!precipsector) - continue; - - // Not in a sector with visible sky? - if (precipsector->sector->ceilingpic != skyflatnum) - continue; - - height = precipsector->sector->ceilingheight - precipsector->sector->floorheight; - - // Exists, but is too small for reasonable precipitation. - if (height < 64<sector->ceilingheight; - - for (j = 0; j < numparticles; j++) + if (precipsector->sector->ffloors) { - rainmo = P_SpawnPrecipMobj(x, y, z, type); + ffloor_t *rover; - if (randomstates > 0) + for (rover = precipsector->sector->ffloors; rover; rover = rover->next) { - UINT8 mrand = M_RandomByte(); - UINT8 threshold = UINT8_MAX / (randomstates + 1); - statenum_t st = mobjinfo[type].spawnstate; + if (!(rover->flags & FF_EXISTS)) + continue; - for (k = 0; k < randomstates; k++) + if (!(rover->flags & FF_SWIMMABLE)) + continue; + + condition = true; + break; + } + } + } + else + { + condition = (precipsector->sector->ceilingpic == skyflatnum); + } + + if (precipsector->sector->flags & SF_INVERTPRECIP) + { + condition = !condition; + } + + if (!condition) + { + continue; + } + + height = precipsector->sector->ceilingheight - precipsector->sector->floorheight; + + // Exists, but is too small for reasonable precipitation. + if (height < 64<sector->floorheight) : (precipsector->sector->ceilingheight); + + for (j = 0; j < numparticles; j++) + { + rainmo = P_SpawnPrecipMobj(x, y, z, type); + + if (randomstates > 0) + { + UINT8 mrand = M_RandomByte(); + UINT8 threshold = UINT8_MAX / (randomstates + 1); + statenum_t st = mobjinfo[type].spawnstate; + + for (k = 0; k < randomstates; k++) + { + if (mrand < (threshold * (k+1))) { - if (mrand < (threshold * (k+1))) - { - P_SetPrecipMobjState(rainmo, st+k+1); - break; - } + P_SetPrecipMobjState(rainmo, st+k+1); + break; } } - - // Randomly assign a height, now that floorz is set. - rainmo->z = M_RandomRange(rainmo->floorz>>FRACBITS, rainmo->ceilingz>>FRACBITS)<z = M_RandomRange(rainmo->floorz >> FRACBITS, rainmo->ceilingz >> FRACBITS) << FRACBITS; } } } diff --git a/src/p_mobj.h b/src/p_mobj.h index 09f1c9a87..0f05d7c90 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -262,12 +262,11 @@ typedef enum // PRECIPITATION flags ?! ?! ?! // typedef enum { - PCF_INVISIBLE = 1, // Don't draw. - PCF_PIT = 1<<1, // Above pit. - PCF_FOF = 1<<2, // Above FOF. - PCF_MOVINGFOF = 1<<3, // Above MOVING FOF (this means we need to keep floorz up to date...) - PCF_SPLASH = 1<<4, // Splashed on the ground, return to the ceiling after the animation's over - PCF_THUNK = 1<<5, // Ran the thinker this tic. + PCF_THUNK = 1, // Ran the thinker this tic. + PCF_SPLASH = 1<<1, // Splashed on the ground, return to the ceiling after the animation's over + PCF_INVISIBLE = 1<<2, // Don't draw. + PCF_PIT = 1<<3, // Above pit. + PCF_FLIP = 1<<4, // Spawning from floor, moving upwards. } precipflag_t; // Map Object definition. @@ -462,6 +461,8 @@ typedef struct precipmobj_s fixed_t ceilingz; // Nearest ceiling above. struct ffloor_s *floorrover; // FOF referred by floorz struct ffloor_s *ceilingrover; // FOF referred by ceilingz + fixed_t floordrop; + fixed_t ceilingdrop; // For movement checking. fixed_t radius; // Fixed at 2*FRACUNIT @@ -473,7 +474,7 @@ typedef struct precipmobj_s INT32 tics; // state tic counter state_t *state; - INT32 flags; // flags from mobjinfo tables + UINT32 flags; // flags from mobjinfo tables } precipmobj_t; typedef struct actioncache_s diff --git a/src/p_setup.c b/src/p_setup.c index e6b966824..a9701511b 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 d850f2a3f..2ca329052 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -1769,10 +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; + INT32 oldEffects = precipprops[curWeather].effects; + + 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) { @@ -1790,6 +1798,8 @@ void P_SwitchWeather(UINT8 newWeather) } } + curWeather = newWeather; + if (purge == true) { thinker_t *think; @@ -1844,14 +1854,22 @@ void P_SwitchWeather(UINT8 newWeather) precipmobj->sprite = precipmobj->state->sprite; precipmobj->frame = precipmobj->state->frame; - precipmobj->momz = mobjinfo[swap].speed; - precipmobj->precipflags &= ~PCF_INVISIBLE; + precipmobj->momz = -mobjinfo[swap].speed; + precipmobj->precipflags &= ~(PCF_INVISIBLE|PCF_FLIP); + + if (precipmobj->momz > 0) + { + precipmobj->precipflags |= PCF_FLIP; + } + + if ((oldEffects & PRECIPFX_WATERPARTICLES) != (precipprops[curWeather].effects & PRECIPFX_WATERPARTICLES)) + { + P_CalculatePrecipFloor(precipmobj); + } } } - curWeather = newWeather; - - if (swap == MT_NULL && precipprops[newWeather].type != MT_NULL) + if (swap == MT_NULL && precipprops[curWeather].type != MT_NULL) P_SpawnPrecipitation(); } diff --git a/src/p_spec.h b/src/p_spec.h index d1af15ec6..40b9d75ff 100644 --- a/src/p_spec.h +++ b/src/p_spec.h @@ -66,7 +66,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); diff --git a/src/r_things.c b/src/r_things.c index 804287fca..05276ea8e 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2188,6 +2188,9 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) //SoM: 3/17/2000 fixed_t gz, gzt; + UINT32 blendmode; + UINT32 trans; + // uncapped/interpolation interpmobjstate_t interp = {0}; @@ -2281,6 +2284,17 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) goto weatherthink; } + // Determine the blendmode and translucency value + { + blendmode = (thing->frame & FF_BLENDMASK) >> FF_BLENDSHIFT; + if (blendmode) + blendmode++; // realign to constants + + trans = (thing->frame & FF_TRANSMASK) >> FF_TRANSSHIFT; + if (trans >= NUMTRANSMAPS) + goto weatherthink; // cap + } + // store information in a vissprite vis = R_NewVisSprite(); vis->scale = vis->sortscale = yscale; //<patch = W_CachePatchNum(sprframe->lumppat[0], PU_SPRITE); - // specific translucency - // (no draw flags) - if (thing->frame & FF_TRANSMASK) - vis->transmap = ((thing->frame & FF_TRANSMASK) - FF_TRANS10) + transtables; - else - vis->transmap = NULL; + vis->transmap = R_GetBlendTable(blendmode, trans); vis->mobj = (mobj_t *)thing; vis->mobjflags = 0;