diff --git a/src/Sourcefile b/src/Sourcefile index e142ebcc1..91851dc62 100644 --- a/src/Sourcefile +++ b/src/Sourcefile @@ -111,4 +111,5 @@ k_botitem.c k_botsearch.c k_grandprix.c k_hud.c +k_terrain.c k_brightmap.c diff --git a/src/k_kart.c b/src/k_kart.c index ca32c6f6d..53426531a 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -33,6 +33,7 @@ #include "k_waypoint.h" #include "k_bot.h" #include "k_hud.h" +#include "k_terrain.h" // SOME IMPORTANT VARIABLES DEFINED IN DOOMDEF.H: // gamespeed is cc (0 for easy, 1 for normal, 2 for hard) @@ -1605,6 +1606,7 @@ static UINT8 K_CheckOffroadCollide(mobj_t *mo) } } + return 0; // couldn't find any offroad } @@ -1616,7 +1618,17 @@ static UINT8 K_CheckOffroadCollide(mobj_t *mo) */ static void K_UpdateOffroad(player_t *player) { - fixed_t offroadstrength = (K_CheckOffroadCollide(player->mo) << FRACBITS); + terrain_t *terrain = player->mo->terrain; + fixed_t offroadstrength = 0; + + if (terrain != NULL && terrain->offroad > 0) + { + offroadstrength = (terrain->offroad << FRACBITS); + } + else + { + offroadstrength = (K_CheckOffroadCollide(player->mo) << FRACBITS); + } // If you are in offroad, a timer starts. if (offroadstrength) @@ -4345,7 +4357,7 @@ void K_SpawnSparkleTrail(mobj_t *mo) sparkle->color = mo->color; } -void K_SpawnWipeoutTrail(mobj_t *mo, boolean offroad) +void K_SpawnWipeoutTrail(mobj_t *mo) { mobj_t *dust; angle_t aoff; @@ -4372,13 +4384,6 @@ void K_SpawnWipeoutTrail(mobj_t *mo, boolean offroad) dust->destscale = mo->scale; P_SetScale(dust, mo->scale); K_FlipFromObject(dust, mo); - - if (offroad) // offroad effect - { - dust->momx = mo->momx/2; - dust->momy = mo->momy/2; - dust->momz = P_GetMobjZMovement(mo)/2; - } } void K_SpawnDraftDust(mobj_t *mo) @@ -5074,7 +5079,7 @@ void K_DoSneaker(player_t *player, INT32 type) { const fixed_t intendedboost = FRACUNIT/2; - if (!player->floorboost || player->floorboost == 3) + if (player->floorboost == 0 || player->floorboost == 3) { const sfxenum_t normalsfx = sfx_cdfm01; const sfxenum_t smallsfx = sfx_cdfm40; @@ -5097,7 +5102,7 @@ void K_DoSneaker(player_t *player, INT32 type) player->numsneakers++; } - if (!player->sneakertimer) + if (player->sneakertimer == 0) { if (type == 2) { @@ -5131,13 +5136,12 @@ void K_DoSneaker(player_t *player, INT32 type) { player->pflags |= PF_ATTACKDOWN; K_PlayBoostTaunt(player->mo); - } player->sneakertimer = sneakertime; // set angle for spun out players: - player->boostangle = (INT32)player->mo->angle; + player->boostangle = player->mo->angle; } static void K_DoShrink(player_t *user) @@ -5219,6 +5223,7 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) return; mo->standingslope = NULL; + mo->terrain = NULL; mo->eflags |= MFE_SPRUNG; @@ -6662,7 +6667,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) // update boost angle if not spun out if (!player->spinouttimer && !player->wipeoutslow) - player->boostangle = (INT32)player->mo->angle; + player->boostangle = player->mo->angle; K_GetKartBoostPower(player); @@ -6710,16 +6715,11 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) ghost->renderflags |= RF_DONTDRAW; } + // Could probably be moved somewhere else. + K_HandleFootstepParticles(player->mo); + if (P_IsObjectOnGround(player->mo)) { - // Offroad dust - if (player->boostpower < FRACUNIT) - { - K_SpawnWipeoutTrail(player->mo, true); - if (leveltime % 6 == 0) - S_StartSound(player->mo, sfx_cdfm70); - } - // Draft dust if (player->draftpower >= FRACUNIT) { @@ -6935,7 +6935,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->sneakertimer && player->wipeoutslow > 0 && player->wipeoutslow < wipeoutslowtime+1) player->wipeoutslow = wipeoutslowtime+1; - if (player->floorboost) + if (player->floorboost > 0) player->floorboost--; if (player->driftboost) diff --git a/src/k_kart.h b/src/k_kart.h index dffc1cedf..04d80fd05 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -76,7 +76,7 @@ void K_RunFinishLineBeam(void); UINT16 K_DriftSparkColor(player_t *player, INT32 charge); void K_SpawnBoostTrail(player_t *player); void K_SpawnSparkleTrail(mobj_t *mo); -void K_SpawnWipeoutTrail(mobj_t *mo, boolean offroad); +void K_SpawnWipeoutTrail(mobj_t *mo); void K_SpawnDraftDust(mobj_t *mo); void K_DriftDustHandling(mobj_t *spawner); void K_Squish(mobj_t *mo); diff --git a/src/k_terrain.c b/src/k_terrain.c new file mode 100644 index 000000000..e03ee2059 --- /dev/null +++ b/src/k_terrain.c @@ -0,0 +1,1532 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2021 by ZDoom + GZDoom teams, and contributors +// Copyright (C) 2021 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2021 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_terrain.c +/// \brief Implementation of TERRAIN lump from GZDoom codebase for DRRR. + +#include "k_terrain.h" + +#include "dehacked.h" // get_number +#include "deh_soc.h" // get_mobjtype +#include "doomdata.h" +#include "doomdef.h" +#include "doomtype.h" +#include "fastcmp.h" +#include "m_fixed.h" +#include "m_random.h" +#include "p_local.h" +#include "p_mobj.h" +#include "r_textures.h" +#include "w_wad.h" +#include "z_zone.h" + +#include "k_kart.h" // on the chopping block... + +static t_splash_t *splashDefs = NULL; +static size_t numSplashDefs = 0; + +static t_footstep_t *footstepDefs = NULL; +static size_t numFootstepDefs = 0; + +static terrain_t *terrainDefs = NULL; +static size_t numTerrainDefs = 0; + +static t_floor_t *terrainFloorDefs = NULL; +static size_t numTerrainFloorDefs = 0; + +static size_t defaultTerrain = SIZE_MAX; +static size_t defaultOffroadFootstep = SIZE_MAX; + +/*-------------------------------------------------- + size_t K_GetSplashHeapIndex(t_splash_t *splash) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetSplashHeapIndex(t_splash_t *splash) +{ + if (splash == NULL) + { + return SIZE_MAX; + } + + return (splash - splashDefs); +} + +/*-------------------------------------------------- + size_t K_GetNumSplashDefs(void) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetNumSplashDefs(void) +{ + return numSplashDefs; +} + +/*-------------------------------------------------- + t_splash_t *K_GetSplashByIndex(size_t checkIndex) + + See header file for description. +--------------------------------------------------*/ +t_splash_t *K_GetSplashByIndex(size_t checkIndex) +{ + if (checkIndex >= numSplashDefs) + { + return NULL; + } + + return &splashDefs[checkIndex]; +} + +/*-------------------------------------------------- + t_splash_t *K_GetSplashByName(const char *checkName) + + See header file for description. +--------------------------------------------------*/ +t_splash_t *K_GetSplashByName(const char *checkName) +{ + size_t i; + + if (numSplashDefs == 0) + { + return NULL; + } + + for (i = 0; i < numSplashDefs; i++) + { + t_splash_t *s = &splashDefs[i]; + + if (stricmp(checkName, s->name) == 0) + { + // Name matches. + return s; + } + } + + return NULL; +} + +/*-------------------------------------------------- + size_t K_GetFootstepHeapIndex(t_footstep_t *footstep) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetFootstepHeapIndex(t_footstep_t *footstep) +{ + if (footstep == NULL) + { + return SIZE_MAX; + } + + return (footstep - footstepDefs); +} + +/*-------------------------------------------------- + size_t K_GetNumFootstepDefs(void) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetNumFootstepDefs(void) +{ + return numFootstepDefs; +} + +/*-------------------------------------------------- + t_footstep_t *K_GetFootstepByIndex(size_t checkIndex) + + See header file for description. +--------------------------------------------------*/ +t_footstep_t *K_GetFootstepByIndex(size_t checkIndex) +{ + if (checkIndex >= numFootstepDefs) + { + return NULL; + } + + return &footstepDefs[checkIndex]; +} + +/*-------------------------------------------------- + t_footstep_t *K_GetFootstepByName(const char *checkName) + + See header file for description. +--------------------------------------------------*/ +t_footstep_t *K_GetFootstepByName(const char *checkName) +{ + size_t i; + + if (numFootstepDefs == 0) + { + return NULL; + } + + for (i = 0; i < numFootstepDefs; i++) + { + t_footstep_t *fs = &footstepDefs[i]; + + if (stricmp(checkName, fs->name) == 0) + { + // Name matches. + return fs; + } + } + + return NULL; +} + +/*-------------------------------------------------- + size_t K_GetTerrainHeapIndex(terrain_t *terrain) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetTerrainHeapIndex(terrain_t *terrain) +{ + if (terrain == NULL) + { + return SIZE_MAX; + } + + return (terrain - terrainDefs); +} + +/*-------------------------------------------------- + size_t K_GetNumTerrainDefs(void) + + See header file for description. +--------------------------------------------------*/ +size_t K_GetNumTerrainDefs(void) +{ + return numTerrainDefs; +} + +/*-------------------------------------------------- + terrain_t *K_GetTerrainByIndex(size_t checkIndex) + + See header file for description. +--------------------------------------------------*/ +terrain_t *K_GetTerrainByIndex(size_t checkIndex) +{ + if (checkIndex >= numTerrainDefs) + { + return NULL; + } + + return &terrainDefs[checkIndex]; +} + +/*-------------------------------------------------- + terrain_t *K_GetTerrainByName(const char *checkName) + + See header file for description. +--------------------------------------------------*/ +terrain_t *K_GetTerrainByName(const char *checkName) +{ + size_t i; + + if (numTerrainDefs == 0) + { + return NULL; + } + + for (i = 0; i < numTerrainDefs; i++) + { + terrain_t *t = &terrainDefs[i]; + + if (stricmp(checkName, t->name) == 0) + { + // Name matches. + return t; + } + } + + return NULL; +} + +/*-------------------------------------------------- + terrain_t *K_GetDefaultTerrain(void) + + See header file for description. +--------------------------------------------------*/ +terrain_t *K_GetDefaultTerrain(void) +{ + return K_GetTerrainByIndex(defaultTerrain); +} + +/*-------------------------------------------------- + terrain_t *K_GetTerrainForTextureName(const char *checkName) + + See header file for description. +--------------------------------------------------*/ +terrain_t *K_GetTerrainForTextureName(const char *checkName) +{ + size_t i; + + if (numTerrainFloorDefs == 0) + { + return NULL; + } + + for (i = 0; i < numTerrainFloorDefs; i++) + { + t_floor_t *f = &terrainFloorDefs[i]; + + if (strncasecmp(checkName, f->textureName, 8) == 0) + { + return K_GetTerrainByIndex(f->terrainID); + } + } + + // This texture doesn't have a terrain directly applied to it, + // so we fallback to the default terrain. + return K_GetDefaultTerrain(); +} + +/*-------------------------------------------------- + terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) + + See header file for description. +--------------------------------------------------*/ +terrain_t *K_GetTerrainForTextureNum(INT32 textureNum) +{ + texture_t *tex = NULL; + + if (textureNum < 0 || textureNum >= numtextures) + { + return NULL; + } + + tex = textures[textureNum]; + return K_GetTerrainForTextureName(tex->name); +} + +/*-------------------------------------------------- + terrain_t *K_GetTerrainForFlatNum(INT32 flatID) + + See header file for description. +--------------------------------------------------*/ +terrain_t *K_GetTerrainForFlatNum(INT32 flatID) +{ + levelflat_t *levelFlat = NULL; + + if (flatID < 0 || flatID >= (signed)numlevelflats) + { + // Clearly invalid floor... + return NULL; + } + + levelFlat = &levelflats[flatID]; + return K_GetTerrainForTextureName(levelFlat->name); +} + +/*-------------------------------------------------- + void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) + + See header file for description. +--------------------------------------------------*/ +void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID) +{ + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. + return; + } + + if (mo->flags & MF_NOCLIPHEIGHT) + { + // You can't collide with floors anyway! + mo->terrain = NULL; + return; + } + + if (mo->player != NULL && mo->player->spectator == true) + { + // We don't want a terrain pointer for spectators. + mo->terrain = NULL; + return; + } + + // Update the object's terrain pointer. + mo->terrain = K_GetTerrainForFlatNum(flatID); +} + +/*-------------------------------------------------- + void K_ProcessTerrainEffect(mobj_t *mo) + + See header file for description. +--------------------------------------------------*/ +void K_ProcessTerrainEffect(mobj_t *mo) +{ + player_t *player = NULL; + terrain_t *terrain = NULL; + + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. + return; + } + + if (mo->terrain == NULL) + { + // No terrain type. + return; + } + + terrain = mo->terrain; + player = mo->player; + + if (player == NULL) + { + // maybe can support regualar mobjs later? :) + return; + } + + // Damage effects + if (terrain->damageType > 0) + { + UINT8 dmg = (terrain->damageType & 0xFF); + P_DamageMobj(mo, NULL, NULL, 1, dmg); + } + + // Sneaker panel + if (terrain->flags & TRF_SNEAKERPANEL) + { + if (player->floorboost == 0) + player->floorboost = 3; + else + player->floorboost = 2; + + K_DoSneaker(player, 0); + } + + // Trick panel + if (terrain->trickPanel > 0 && !(mo->eflags & MFE_SPRUNG)) + { + const fixed_t hscale = mapobjectscale + (mapobjectscale - mo->scale); + const fixed_t minspeed = 24*hscale; + fixed_t speed = FixedHypot(mo->momx, mo->momy); + fixed_t upwards = 16 * FRACUNIT * terrain->trickPanel; + + player->trickpanel = 1; + player->pflags |= PF_TRICKDELAY; + K_DoPogoSpring(mo, upwards, 1); + + if (speed < minspeed) + { + speed = minspeed; + } + + P_InstaThrust(mo, mo->angle, speed); + } + + // Bumpy floor + if (terrain->flags & TRF_STAIRJANK) + { + /* use a shorter sound if not two tics have passed + * since the last step */ + S_StartSound(mo, player->stairjank + >= 16 ? sfx_s23b : sfx_s268); + + if (player->stairjank == 0) + { + mobj_t *spark = P_SpawnMobjFromMobj(mo, + 0, 0, 0, MT_JANKSPARK); + spark->fuse = 9; + spark->cusval = K_StairJankFlip(ANGLE_90); + P_SetTarget(&spark->target, mo); + } + + player->stairjank = 17; + } + + // (Offroad is handled elsewhere!) +} + +/*-------------------------------------------------- + void K_SetDefaultFriction(mobj_t *mo) + + See header file for description. +--------------------------------------------------*/ +void K_SetDefaultFriction(mobj_t *mo) +{ + boolean isPlayer = false; + + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. + return; + } + + isPlayer = (mo->player != NULL); + + mo->friction = ORIG_FRICTION; + + if (isPlayer == true) + { + mo->movefactor = FRACUNIT; + } + + if (mo->terrain != NULL) + { + fixed_t strength = mo->terrain->friction; + + fixed_t newFriction = INT32_MAX; + fixed_t newMovefactor = INT32_MAX; + + if (strength > 0) // sludge + { + strength = strength * 2; // otherwise, the maximum sludginess value is +967... + } + + // The following might seem odd. At the time of movement, + // the move distance is multiplied by 'friction/0x10000', so a + // higher friction value actually means 'less friction'. + newFriction = ORIG_FRICTION - FixedMul(0x1EB8, strength) / 0x80; // ORIG_FRICTION is 0xE800 + + if (newFriction > FRACUNIT) + { + newFriction = FRACUNIT; + } + + if (newFriction < 0) + { + newFriction = 0; + } + + mo->friction = newFriction; + + if (isPlayer == true) + { + newMovefactor = FixedDiv(ORIG_FRICTION, newFriction); + + if (newMovefactor < FRACUNIT) + { + newMovefactor = 19*newMovefactor - 18*FRACUNIT; + } + else + { + newMovefactor = FRACUNIT; + } + + mo->movefactor = newMovefactor; + } + } +} + +/*-------------------------------------------------- + static void K_SpawnSplashParticles(mobj_t *mo, t_splash_t *s, fixed_t impact) + + See header file for description. +--------------------------------------------------*/ +static void K_SpawnSplashParticles(mobj_t *mo, t_splash_t *s, fixed_t impact) +{ + const UINT8 numParticles = s->numParticles; + const angle_t particleSpread = ANGLE_MAX / numParticles; + size_t i; + + for (i = 0; i < numParticles; i++) + { + mobj_t *dust = NULL; + angle_t pushAngle = (particleSpread * i); + fixed_t momH = INT32_MAX; + fixed_t momV = INT32_MAX; + + if (numParticles == 1) + { + // Random angle. + pushAngle = P_RandomRange(0, ANGLE_MAX); + } + + dust = P_SpawnMobjFromMobj( + mo, + (12 * FINECOSINE(pushAngle >> ANGLETOFINESHIFT)), + (12 * FINESINE(pushAngle >> ANGLETOFINESHIFT)), + 0, s->mobjType + ); + + P_SetTarget(&dust->target, mo); + dust->angle = pushAngle; + + dust->destscale = FixedMul(mo->scale, s->scale); + P_SetScale(dust, dust->destscale); + + dust->momx = mo->momx / 2; + dust->momy = mo->momy / 2; + dust->momz = 0; + + momH = FixedMul(impact, s->pushH); + momV = FixedMul(impact, s->pushV); + + dust->momx += FixedMul(momH, FINECOSINE(pushAngle >> ANGLETOFINESHIFT)); + dust->momy += FixedMul(momH, FINESINE(pushAngle >> ANGLETOFINESHIFT)); + dust->momz += momV * P_MobjFlip(mo); + + if (s->color != SKINCOLOR_NONE) + { + dust->color = s->color; + } + + if (s->sfx != sfx_None) + { + S_StartSound(mo, s->sfx); + } + } +} + +/*-------------------------------------------------- + void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact) + + See header file for description. +--------------------------------------------------*/ +void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact) +{ + const fixed_t minImpact = 4 * mo->scale; + t_splash_t *s = NULL; + + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. + return; + } + + if (mo->terrain == NULL || mo->terrain->splashID == SIZE_MAX) + { + // No impact for this terrain type. + return; + } + else + { + s = K_GetSplashByIndex(mo->terrain->splashID); + } + + if (s == NULL || s->mobjType == MT_NULL || s->numParticles == 0) + { + // No particles to spawn. + return; + } + + if (impact < minImpact) + { + impact = minImpact; + } + + // Idea for later: if different spawning styles are desired, + // we can put a switch case here! + K_SpawnSplashParticles(mo, s, impact); +} + +/*-------------------------------------------------- + static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs) + + See header file for description. +--------------------------------------------------*/ +static void K_SpawnFootstepParticle(mobj_t *mo, t_footstep_t *fs, tic_t timer) +{ + mobj_t *dust = NULL; + angle_t pushAngle = ANGLE_MAX; + angle_t tireAngle = ANGLE_MAX; + fixed_t momentum = INT32_MAX; + fixed_t speedValue = INT32_MAX; + fixed_t momH = INT32_MAX; + fixed_t momV = INT32_MAX; + fixed_t xOff = 0; + fixed_t yOff = 0; + + if (timer % fs->frequency != 0) + { + return; + } + + momentum = P_AproxDistance(mo->momx, mo->momy); + + if (mo->player != NULL) + { + tireAngle = (mo->player->drawangle + ANGLE_180); + speedValue = K_GetKartSpeedFromStat(mo->player->kartspeed); + } + else + { + tireAngle = (mo->angle + ANGLE_180); + speedValue = K_GetKartSpeedFromStat(5); + } + + speedValue = FixedMul(speedValue, mo->scale); + speedValue = FixedMul(speedValue, fs->requiredSpeed); + + if (momentum < speedValue) + { + return; + } + + pushAngle = K_MomentumAngle(mo) + ANGLE_180; + + if (((timer / fs->frequency) / 2) & 1) + { + tireAngle -= ANGLE_45; + tireAngle -= P_RandomRange(0, fs->cone / ANG1) * ANG1; + pushAngle -= P_RandomRange(0, fs->cone / ANG1) * ANG1; + } + else + { + tireAngle += ANGLE_45; + tireAngle += P_RandomRange(0, fs->cone / ANG1) * ANG1; + pushAngle += P_RandomRange(0, fs->cone / ANG1) * ANG1; + } + + if (fs->spread > 0) + { + xOff = P_RandomRange(-fs->spread / FRACUNIT, fs->spread / FRACUNIT) * FRACUNIT; + yOff = P_RandomRange(-fs->spread / FRACUNIT, fs->spread / FRACUNIT) * FRACUNIT; + } + + dust = P_SpawnMobjFromMobj( + mo, + xOff + (24 * FINECOSINE(tireAngle >> ANGLETOFINESHIFT)), + yOff + (24 * FINESINE(tireAngle >> ANGLETOFINESHIFT)), + 0, fs->mobjType + ); + + P_SetTarget(&dust->target, mo); + dust->angle = K_MomentumAngle(mo); + + dust->destscale = FixedMul(mo->scale, fs->scale); + P_SetScale(dust, dust->destscale); + + dust->momx = mo->momx; + dust->momy = mo->momy; + dust->momz = P_GetMobjZMovement(mo) / 2; + + momH = FixedMul(momentum, fs->pushH); + momV = FixedMul(momentum, fs->pushV); + + dust->momx += FixedMul(momH, FINECOSINE(pushAngle >> ANGLETOFINESHIFT)); + dust->momy += FixedMul(momH, FINESINE(pushAngle >> ANGLETOFINESHIFT)); + dust->momz += (momV / 16) * P_MobjFlip(mo); + + if (fs->color != SKINCOLOR_NONE) + { + dust->color = fs->color; + } + + if ((fs->sfx != sfx_None) && (fs->sfxFreq > 0) && (timer % fs->sfxFreq == 0)) + { + S_StartSound(mo, fs->sfx); + } +} + +/*-------------------------------------------------- + void K_HandleFootstepParticles(mobj_t *mo) + + See header file for description. +--------------------------------------------------*/ +void K_HandleFootstepParticles(mobj_t *mo) +{ + tic_t timer = leveltime; + t_footstep_t *fs = NULL; + + if (mo == NULL || P_MobjWasRemoved(mo) == true) + { + // Invalid object. + return; + } + + if (mo->terrain == NULL || mo->terrain->footstepID == SIZE_MAX) + { + // If no terrain, check for offroad. + // If we're in offroad, use the default particle. + + if (mo->player != NULL && mo->player->boostpower < FRACUNIT) + { + fs = K_GetFootstepByIndex(defaultOffroadFootstep); + } + } + else + { + fs = K_GetFootstepByIndex(mo->terrain->footstepID); + } + + if (fs == NULL || fs->mobjType == MT_NULL || fs->frequency <= 0) + { + // No particles to spawn. + return; + } + + if (mo->player != NULL) + { + // Offset timer by player ID. + timer += mo->player - players; + } + + // Idea for later: if different spawning styles are desired, + // we can put a switch case here! + K_SpawnFootstepParticle(mo, fs, timer); +} + +/*-------------------------------------------------- + static void K_FlagBoolean(UINT32 *inputFlags, UINT32 newFlag, char *val) + + Sets a flag to true or false depending on + the string input. + + Input Arguments:- + inputFlags - Pointer to flags value to modify. + newFlag - The flag(s) to set / unset. + val - The string input from the file. + + Return:- + None +--------------------------------------------------*/ +static void K_FlagBoolean(UINT32 *inputFlags, UINT32 newFlag, char *val) +{ + if (stricmp(val, "true") == 0) + { + *inputFlags |= newFlag; + } + else if (stricmp(val, "false") == 0) + { + *inputFlags &= ~newFlag; + } +} + +/*-------------------------------------------------- + static void K_SplashDefaults(t_splash_t *splash) + + Sets the defaults for a new Splash block. + + Input Arguments:- + splash - Terrain Splash structure to default. + + Return:- + None +--------------------------------------------------*/ +static void K_SplashDefaults(t_splash_t *splash) +{ + splash->mobjType = MT_NULL; + splash->sfx = sfx_None; + splash->scale = FRACUNIT; + splash->color = SKINCOLOR_NONE; + + splash->pushH = FRACUNIT/4; + splash->pushV = FRACUNIT/64; + splash->spread = 2*FRACUNIT; + splash->cone = ANGLE_11hh; + + splash->numParticles = 8; +} + +/*-------------------------------------------------- + static void K_NewSplashDefs(void) + + Increases the size of splashDefs by 1, and + sets the new struct's values to their defaults. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ +static void K_NewSplashDefs(void) +{ + numSplashDefs++; + splashDefs = (t_splash_t *)Z_Realloc(splashDefs, sizeof(t_splash_t) * (numSplashDefs + 1), PU_STATIC, NULL); + K_SplashDefaults( &splashDefs[numSplashDefs - 1] ); +} + +/*-------------------------------------------------- + static void K_ParseSplashParameter(size_t i, char *param, char *val) + + Parser function for Splash blocks. + + Input Arguments:- + i - Struct ID + param - Parameter string + val - Value string + + Return:- + None +--------------------------------------------------*/ +static void K_ParseSplashParameter(size_t i, char *param, char *val) +{ + t_splash_t *splash = &splashDefs[i]; + + if (stricmp(param, "mobjType") == 0) + { + splash->mobjType = get_number(val); + } + else if (stricmp(param, "sfx") == 0) + { + splash->sfx = get_number(val); + } + else if (stricmp(param, "scale") == 0) + { + splash->scale = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "color") == 0) + { + splash->color = get_number(val); + } + else if (stricmp(param, "pushH") == 0) + { + splash->pushH = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "pushV") == 0) + { + splash->pushV = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "spread") == 0) + { + splash->spread = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "cone") == 0) + { + splash->cone = FloatToAngle(atof(val)); + } + else if (stricmp(param, "numParticles") == 0) + { + splash->numParticles = (UINT8)atoi(val); + } +} + +/*-------------------------------------------------- + static void K_FootstepDefaults(t_footstep_t *footstep) + + Sets the defaults for a new Footstep block. + + Input Arguments:- + footstep - Terrain Footstep structure to default. + + Return:- + None +--------------------------------------------------*/ +static void K_FootstepDefaults(t_footstep_t *footstep) +{ + footstep->mobjType = MT_NULL; + footstep->sfx = sfx_None; + footstep->scale = FRACUNIT; + footstep->color = SKINCOLOR_NONE; + + footstep->pushH = FRACUNIT/2; + footstep->pushV = FRACUNIT/32; + footstep->spread = 2*FRACUNIT; + footstep->cone = ANGLE_11hh; + + footstep->sfxFreq = 6; + footstep->frequency = 1; + footstep->requiredSpeed = 0; +} + +/*-------------------------------------------------- + static void K_NewFootstepDefs(void) + + Increases the size of footstepDefs by 1, and + sets the new struct's values to their defaults. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ +static void K_NewFootstepDefs(void) +{ + numFootstepDefs++; + footstepDefs = (t_footstep_t *)Z_Realloc(footstepDefs, sizeof(t_footstep_t) * (numFootstepDefs + 1), PU_STATIC, NULL); + K_FootstepDefaults( &footstepDefs[numFootstepDefs - 1] ); +} + +/*-------------------------------------------------- + static void K_ParseFootstepParameter(size_t i, char *param, char *val) + + Parser function for Footstep blocks. + + Input Arguments:- + i - Struct ID + param - Parameter string + val - Value string + + Return:- + None +--------------------------------------------------*/ +static void K_ParseFootstepParameter(size_t i, char *param, char *val) +{ + t_footstep_t *footstep = &footstepDefs[i]; + + if (stricmp(param, "mobjType") == 0) + { + footstep->mobjType = get_number(val); + } + else if (stricmp(param, "sfx") == 0) + { + footstep->sfx = get_number(val); + } + else if (stricmp(param, "scale") == 0) + { + footstep->scale = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "color") == 0) + { + footstep->color = get_number(val); + } + else if (stricmp(param, "pushH") == 0) + { + footstep->pushH = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "pushV") == 0) + { + footstep->pushV = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "spread") == 0) + { + footstep->spread = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "cone") == 0) + { + footstep->cone = FloatToAngle(atof(val)); + } + else if (stricmp(param, "sfxFreq") == 0) + { + footstep->sfxFreq = (tic_t)atoi(val); + } + else if (stricmp(param, "frequency") == 0) + { + footstep->frequency = (tic_t)atoi(val); + } + else if (stricmp(param, "requiredSpeed") == 0) + { + footstep->requiredSpeed = FLOAT_TO_FIXED(atof(val)); + } +} + +/*-------------------------------------------------- + static void K_TerrainDefaults(terrain_t *terrain) + + Sets the defaults for a new Terrain block. + + Input Arguments:- + terrain - Terrain structure to default. + + Return:- + None +--------------------------------------------------*/ +static void K_TerrainDefaults(terrain_t *terrain) +{ + terrain->splashID = SIZE_MAX; + terrain->footstepID = SIZE_MAX; + + terrain->friction = FRACUNIT; + terrain->offroad = 0; + terrain->damageType = -1; + terrain->trickPanel = 0; + terrain->flags = 0; +} + +/*-------------------------------------------------- + static void K_NewTerrainDefs(void) + + Increases the size of terrainDefs by 1, and + sets the new struct's values to their defaults. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ +static void K_NewTerrainDefs(void) +{ + numTerrainDefs++; + terrainDefs = (terrain_t *)Z_Realloc(terrainDefs, sizeof(terrain_t) * (numTerrainDefs + 1), PU_STATIC, NULL); + K_TerrainDefaults( &terrainDefs[numTerrainDefs - 1] ); +} + +/*-------------------------------------------------- + static void K_ParseTerrainParameter(size_t i, char *param, char *val) + + Parser function for Terrain blocks. + + Input Arguments:- + i - Struct ID + param - Parameter string + val - Value string + + Return:- + None +--------------------------------------------------*/ +static void K_ParseTerrainParameter(size_t i, char *param, char *val) +{ + terrain_t *terrain = &terrainDefs[i]; + + if (stricmp(param, "splash") == 0) + { + t_splash_t *splash = K_GetSplashByName(val); + terrain->splashID = K_GetSplashHeapIndex(splash); + } + else if (stricmp(param, "footstep") == 0) + { + t_footstep_t *footstep = K_GetFootstepByName(val); + terrain->footstepID = K_GetFootstepHeapIndex(footstep); + } + else if (stricmp(param, "friction") == 0) + { + terrain->friction = FLOAT_TO_FIXED(atof(val)); + } + else if (stricmp(param, "offroad") == 0) + { + terrain->offroad = (UINT8)get_number(val); // offroad strength enum? + } + else if (stricmp(param, "damageType") == 0) + { + terrain->damageType = (INT16)get_number(val); + } + else if (stricmp(param, "trickPanel") == 0) + { + terrain->trickPanel = (UINT8)get_number(val); // trick panel strength enum? + } + else if (stricmp(param, "liquid") == 0) + { + K_FlagBoolean(&terrain->flags, TRF_LIQUID, val); + } + else if (stricmp(param, "sneakerPanel") == 0) + { + K_FlagBoolean(&terrain->flags, TRF_SNEAKERPANEL, val); + } + else if (stricmp(param, "bumpy") == 0 || stricmp(param, "stairJank") == 0) + { + K_FlagBoolean(&terrain->flags, TRF_STAIRJANK, val); + } + else if (stricmp(param, "tripwire") == 0) + { + K_FlagBoolean(&terrain->flags, TRF_TRIPWIRE, val); + } +} + +/*-------------------------------------------------- + static void K_NewTerrainFloorDefs(void) + + Increases the size of numTerrainFloorDefs by 1. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ +static void K_NewTerrainFloorDefs(void) +{ + numTerrainFloorDefs++; + terrainFloorDefs = (t_floor_t *)Z_Realloc(terrainFloorDefs, sizeof(t_floor_t) * (numTerrainFloorDefs + 1), PU_STATIC, NULL); +} + +/*-------------------------------------------------- + static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(UINT32, char *, char *)) + + Runs another parser function for the TERRAIN + lump, handling the nitty-gritty parts of the + token handling. + + Input Arguments:- + num - Struct ID to modify. Which one it will modify depends on the parser function. + parser - The parser function. Takes three inputs: Struct ID, Parameter String, and Value String. + + Return:- + false if any errors occured, otherwise true. +--------------------------------------------------*/ +static boolean K_DoTERRAINLumpParse(size_t num, void (*parser)(size_t, char *, char *)) +{ + char *param, *val; + + param = M_GetToken(NULL); + + if (!fastcmp(param, "{")) + { + Z_Free(param); + CONS_Alert(CONS_WARNING, "Invalid TERRAIN data capsule!\n"); + return false; + } + + Z_Free(param); + + while (true) + { + param = M_GetToken(NULL); + + if (fastcmp(param, "}")) + { + Z_Free(param); + break; + } + + val = M_GetToken(NULL); + parser(num, param, val); + + Z_Free(param); + Z_Free(val); + } + + return true; +} + +/*-------------------------------------------------- + static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) + + Parses inputted lump data as a TERRAIN lump. + + Input Arguments:- + data - Pointer to lump data. + size - The length of the lump data. + + Return:- + false if any errors occured, otherwise true. +--------------------------------------------------*/ +static boolean K_TERRAINLumpParser(UINT8 *data, size_t size) +{ + char *tkn = M_GetToken((char *)data); + size_t pos = 0; + size_t i; + + while (tkn && (pos = M_GetTokenPos()) < size) + { + boolean valid = true; + + // Avoid anything inside bracketed stuff, only look for external keywords. + if (fastcmp(tkn, "{") || fastcmp(tkn, "}")) + { + CONS_Alert(CONS_ERROR, "Rogue bracket detected in TERRAIN lump.\n"); + valid = false; + } + // Check for valid fields. + else if (stricmp(tkn, "splash") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + t_splash_t *s = NULL; + + for (i = 0; i < numSplashDefs; i++) + { + s = &splashDefs[i]; + + if (stricmp(tkn, s->name) == 0) + { + break; + } + } + + if (i == numSplashDefs) + { + K_NewSplashDefs(); + s = &splashDefs[i]; + + strncpy(s->name, tkn, TERRAIN_NAME_LEN); + CONS_Printf("Created new Splash type '%s'\n", s->name); + } + + valid = K_DoTERRAINLumpParse(i, K_ParseSplashParameter); + } + else + { + CONS_Alert(CONS_ERROR, "No Splash type name.\n"); + valid = false; + } + } + else if (stricmp(tkn, "footstep") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + t_footstep_t *fs = NULL; + + for (i = 0; i < numFootstepDefs; i++) + { + fs = &footstepDefs[i]; + + if (stricmp(tkn, fs->name) == 0) + { + break; + } + } + + if (i == numFootstepDefs) + { + K_NewFootstepDefs(); + fs = &footstepDefs[i]; + + strncpy(fs->name, tkn, TERRAIN_NAME_LEN); + CONS_Printf("Created new Footstep type '%s'\n", fs->name); + } + + valid = K_DoTERRAINLumpParse(i, K_ParseFootstepParameter); + } + else + { + CONS_Alert(CONS_ERROR, "No Footstep type name.\n"); + valid = false; + } + } + else if (stricmp(tkn, "terrain") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + terrain_t *t = NULL; + + for (i = 0; i < numTerrainDefs; i++) + { + t = &terrainDefs[i]; + + if (stricmp(tkn, t->name) == 0) + { + break; + } + } + + if (i == numTerrainDefs) + { + K_NewTerrainDefs(); + t = &terrainDefs[i]; + + strncpy(t->name, tkn, TERRAIN_NAME_LEN); + CONS_Printf("Created new Terrain type '%s'\n", t->name); + } + + valid = K_DoTERRAINLumpParse(i, K_ParseTerrainParameter); + } + else + { + CONS_Alert(CONS_ERROR, "No Terrain type name.\n"); + valid = false; + } + } + else if (stricmp(tkn, "floor") == 0 || stricmp(tkn, "texture") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + if (stricmp(tkn, "optional") == 0) + { + // "optional" is ZDoom syntax + // We don't use it, but we can ignore it. + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + } + + if (tkn && pos < size) + { + t_floor_t *f = NULL; + + for (i = 0; i < numTerrainFloorDefs; i++) + { + f = &terrainFloorDefs[i]; + + if (stricmp(tkn, f->textureName) == 0) + { + break; + } + } + + if (i == numTerrainFloorDefs) + { + K_NewTerrainFloorDefs(); + f = &terrainFloorDefs[i]; + + strncpy(f->textureName, tkn, 9); + } + + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + terrain_t *t = K_GetTerrainByName(tkn); + + if (t == NULL) + { + CONS_Alert(CONS_ERROR, "Invalid Terrain type '%s'.\n", tkn); + valid = false; + } + else + { + f->terrainID = K_GetTerrainHeapIndex(t); + CONS_Printf("Texture '%s' set to Terrain '%s'\n", f->textureName, tkn); + } + } + else + { + CONS_Alert(CONS_ERROR, "No terrain for floor definition.\n"); + valid = false; + } + } + else + { + CONS_Alert(CONS_ERROR, "No texture for floor definition.\n"); + valid = false; + } + } + else + { + CONS_Alert(CONS_ERROR, "No texture for floor definition.\n"); + valid = false; + } + } + else if (stricmp(tkn, "defaultTerrain") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + terrain_t *t = NULL; + + for (i = 0; i < numTerrainDefs; i++) + { + t = &terrainDefs[i]; + + if (stricmp(tkn, t->name) == 0) + { + break; + } + } + + if (i == numTerrainDefs) + { + CONS_Alert(CONS_ERROR, "Invalid DefaultTerrain type.\n"); + valid = false; + } + else + { + defaultTerrain = i; + CONS_Printf("DefaultTerrain set to '%s'\n", tkn); + } + } + else + { + CONS_Alert(CONS_ERROR, "No DefaultTerrain type.\n"); + valid = false; + } + } + else if (stricmp(tkn, "defaultOffroadFootstep") == 0) + { + Z_Free(tkn); + tkn = M_GetToken(NULL); + pos = M_GetTokenPos(); + + if (tkn && pos < size) + { + t_footstep_t *fs = NULL; + + for (i = 0; i < numFootstepDefs; i++) + { + fs = &footstepDefs[i]; + + if (stricmp(tkn, fs->name) == 0) + { + break; + } + } + + if (i == numFootstepDefs) + { + CONS_Alert(CONS_ERROR, "Invalid DefaultOffroadFootstep type.\n"); + valid = false; + } + else + { + defaultOffroadFootstep = i; + CONS_Printf("DefaultOffroadFootstep set to '%s'\n", tkn); + } + } + else + { + CONS_Alert(CONS_ERROR, "No DefaultOffroadFootstep type.\n"); + valid = false; + } + } + else + { + CONS_Alert(CONS_ERROR, "Unknown field '%s' found in TERRAIN lump.\n", tkn); + valid = false; + } + + Z_Free(tkn); + + if (valid == false) + { + return false; + } + + tkn = M_GetToken(NULL); + } + + Z_Free(tkn); + return true; +} + +/*-------------------------------------------------- + void K_InitTerrain(UINT16 wadNum) + + See header file for description. +--------------------------------------------------*/ +void K_InitTerrain(UINT16 wadNum) +{ + UINT16 lumpNum; + lumpinfo_t *lump_p = wadfiles[wadNum]->lumpinfo; + + // Iterate through all lumps and compare the name individually. + // In PK3 files, you can potentially have multiple TERRAIN differentiated by + // their file extension. + for (lumpNum = 0; lumpNum < wadfiles[wadNum]->numlumps; lumpNum++, lump_p++) + { + UINT8 *data; + + if (memcmp(lump_p->name, "TERRAIN", 8) != 0) + { + continue; + } + + data = (UINT8 *)W_CacheLumpNumPwad(wadNum, lumpNum, PU_STATIC); + + // If that didn't exist, we have nothing to do here. + if (data == NULL) + { + continue; + } + else + { + size_t size = W_LumpLengthPwad(wadNum, lumpNum); + + size_t nameLength = strlen(wadfiles[wadNum]->filename) + 1 + strlen(lump_p->fullname); // length of file name, '|', and lump name + char *name = malloc(nameLength + 1); + + sprintf(name, "%s|%s", wadfiles[wadNum]->filename, lump_p->fullname); + name[nameLength] = '\0'; + + size = W_LumpLengthPwad(wadNum, lumpNum); + + CONS_Printf(M_GetText("Loading TERRAIN from %s\n"), name); + K_TERRAINLumpParser(data, size); + + free(name); + } + } +} diff --git a/src/k_terrain.h b/src/k_terrain.h new file mode 100644 index 000000000..924f7a1b1 --- /dev/null +++ b/src/k_terrain.h @@ -0,0 +1,459 @@ +// DR. ROBOTNIK'S RING RACERS +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2021 by ZDoom + GZDoom teams, and contributors +// Copyright (C) 2021 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2021 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_terrain.h +/// \brief Implementation of a TERRAIN-style lump for DRRR, ala GZDoom's codebase. + +#ifndef __K_TERRAIN_H__ +#define __K_TERRAIN_H__ + +#include "doomdata.h" +#include "doomdef.h" +#include "doomtype.h" +#include "m_fixed.h" +#include "p_mobj.h" + +#define TERRAIN_NAME_LEN 32 + +typedef struct t_splash_s +{ + // Splash definition. + // These are particles spawned when hitting the floor. + + char name[TERRAIN_NAME_LEN]; // Lookup name. + + UINT16 mobjType; // Thing type. MT_NULL to not spawn anything. + UINT16 sfx; // Sound to play. + fixed_t scale; // Thing scale multiplier. + UINT16 color; // Colorize effect. SKINCOLOR_NONE has no colorize. + + fixed_t pushH; // Push-out horizontal multiplier. + fixed_t pushV; // Push-out vertical multiplier. + fixed_t spread; // Randomized spread distance. + angle_t cone; // Randomized angle of the push-out. + + UINT8 numParticles; // Number of particles to spawn. +} t_splash_t; + +typedef struct t_footstep_s +{ + // Footstep definition. + // These are particles spawned when moving fast enough on a floor. + + char name[TERRAIN_NAME_LEN]; // Lookup name. + + UINT16 mobjType; // Thing type. MT_NULL to not spawn anything. + UINT16 sfx; // Sound to play. + fixed_t scale; // Thing scale multiplier. + UINT16 color; // Colorize effect. SKINCOLOR_NONE has no colorize. + + fixed_t pushH; // Push-out horizontal multiplier. + fixed_t pushV; // Push-out vertical multiplier. + fixed_t spread; // Randomized spread distance. + angle_t cone; // Randomized angle of the push-out. + + tic_t sfxFreq; // How frequently to play the sound. + tic_t frequency; // How frequently to spawn the particles. + fixed_t requiredSpeed; // Speed percentage you need to be at to trigger the particles. +} t_footstep_t; + +typedef enum +{ + // Terrain flag values. + TRF_LIQUID = 1, // Texture water properties (wavy, slippery, etc) + TRF_SNEAKERPANEL = 1<<1, // Texture is a booster + TRF_STAIRJANK = 1<<2, // Texture is bumpy road + TRF_TRIPWIRE = 1<<3 // Texture is a tripwire when used as a midtexture +} terrain_flags_t; + +typedef struct terrain_s +{ + // Terrain definition. + // These are all of the properties that the floor gets. + + char name[TERRAIN_NAME_LEN]; // Lookup name. + + size_t splashID; // Splash defintion ID. + size_t footstepID; // Footstep defintion ID. + + fixed_t friction; // The default friction of this texture. + UINT8 offroad; // The default offroad level of this texture. + INT16 damageType; // The default damage type of this texture. (Negative means no damage). + UINT8 trickPanel; // Trick panel strength + UINT32 flags; // Flag values (see: terrain_flags_t) +} terrain_t; + +typedef struct t_floor_s +{ + // Terrain floor definition. + // Ties texture names to a . + + // (Could be optimized by using texture IDs instead of names, + // but was concerned because I recall sooomething about those not being netsafe? + // Someone confirm if I just hallucinated that. :V) + + char textureName[9]; // Floor texture name. + size_t terrainID; // Terrain definition ID. +} t_floor_t; + +/*-------------------------------------------------- + size_t K_GetSplashHeapIndex(t_splash_t *splash); + + Returns a splash defintion's index in the + splash definition heap. + + Input Arguments:- + splash - The splash definition to return the index of. + + Return:- + The splash heap index, SIZE_MAX if the splash was invalid. +--------------------------------------------------*/ + +size_t K_GetSplashHeapIndex(t_splash_t *splash); + + +/*-------------------------------------------------- + size_t K_GetNumSplashDefs(void); + + Returns the number of splash definitions. + + Input Arguments:- + None + + Return:- + Length of splashDefs. +--------------------------------------------------*/ + +size_t K_GetNumSplashDefs(void); + + +/*-------------------------------------------------- + t_splash_t *K_GetSplashByIndex(size_t checkIndex); + + Retrieves a splash definition by its heap index. + + Input Arguments:- + checkIndex - The heap index to retrieve. + + Return:- + The splash definition, NULL if it didn't exist. +--------------------------------------------------*/ + +t_splash_t *K_GetSplashByIndex(size_t checkIndex); + + +/*-------------------------------------------------- + t_splash_t *K_GetSplashByName(const char *checkName); + + Retrieves a splash definition by its lookup name. + + Input Arguments:- + checkName - The lookup name to retrieve. + + Return:- + The splash definition, NULL if it didn't exist. +--------------------------------------------------*/ + +t_splash_t *K_GetSplashByName(const char *checkName); + + +/*-------------------------------------------------- + size_t K_GetFootstepHeapIndex(t_footstep_t *footstep); + + Returns a footstep defintion's index in the + footstep definition heap. + + Input Arguments:- + footstep - The footstep definition to return the index of. + + Return:- + The footstep heap index, SIZE_MAX if the footstep was invalid. +--------------------------------------------------*/ + +size_t K_GetFootstepHeapIndex(t_footstep_t *footstep); + + +/*-------------------------------------------------- + size_t K_GetNumFootstepDefs(void); + + Returns the number of footstep definitions. + + Input Arguments:- + None + + Return:- + Length of footstepDefs. +--------------------------------------------------*/ + +size_t K_GetNumFootstepDefs(void); + + +/*-------------------------------------------------- + t_footstep_t *K_GetFootstepByIndex(size_t checkIndex); + + Retrieves a footstep definition by its heap index. + + Input Arguments:- + checkIndex - The heap index to retrieve. + + Return:- + The footstep definition, NULL if it didn't exist. +--------------------------------------------------*/ + +t_footstep_t *K_GetFootstepByIndex(size_t checkIndex); + + +/*-------------------------------------------------- + t_footstep_t *K_GetFootstepByName(const char *checkName); + + Retrieves a footstep definition by its lookup name. + + Input Arguments:- + checkName - The lookup name to retrieve. + + Return:- + The footstep definition, NULL if it didn't exist. +--------------------------------------------------*/ + +t_footstep_t *K_GetFootstepByName(const char *checkName); + + +/*-------------------------------------------------- + size_t K_GetTerrainHeapIndex(terrain_t *terrain); + + Returns a terrain defintion's index in the + terrain definition heap. + + Input Arguments:- + terrain - The terrain definition to return the index of. + + Return:- + The terrain heap index, SIZE_MAX if the terrain was invalid. +--------------------------------------------------*/ + +size_t K_GetTerrainHeapIndex(terrain_t *terrain); + + +/*-------------------------------------------------- + size_t K_GetNumTerrainDefs(void); + + Returns the number of terrain definitions. + + Input Arguments:- + None + + Return:- + Length of terrainDefs. +--------------------------------------------------*/ + +size_t K_GetNumTerrainDefs(void); + + +/*-------------------------------------------------- + terrain_t *K_GetTerrainByIndex(size_t checkIndex); + + Retrieves a terrain definition by its heap index. + + Input Arguments:- + checkIndex - The heap index to retrieve. + + Return:- + The terrain definition, NULL if it didn't exist. +--------------------------------------------------*/ + +terrain_t *K_GetTerrainByIndex(size_t checkIndex); + + +/*-------------------------------------------------- + terrain_t *K_GetTerrainByName(const char *checkName); + + Retrieves a terrain definition by its lookup name. + + Input Arguments:- + checkName - The lookup name to retrieve. + + Return:- + The terrain definition, NULL if it didn't exist. +--------------------------------------------------*/ + +terrain_t *K_GetTerrainByName(const char *checkName); + +/*-------------------------------------------------- + terrain_t *K_GetDefaultTerrain(void); + + Returns the default terrain definition, used + in cases where terrain is not set for a texture. + + Input Arguments:- + None + + Return:- + The default terrain definition, NULL if it didn't exist. +--------------------------------------------------*/ + +terrain_t *K_GetDefaultTerrain(void); + + +/*-------------------------------------------------- + terrain_t *K_GetTerrainForTextureName(const char *checkName); + + Returns the terrain definition applied to + the texture name inputted. + + Input Arguments:- + checkName - The texture's name. + + Return:- + The texture's terrain definition if it exists, + otherwise the default terrain if it exists, + otherwise NULL. +--------------------------------------------------*/ + +terrain_t *K_GetTerrainForTextureName(const char *checkName); + + +/*-------------------------------------------------- + terrain_t *K_GetTerrainForTextureNum(INT32 textureNum); + + Returns the terrain definition applied to + the texture ID inputted. + + Input Arguments:- + textureNum - The texture's ID. + + Return:- + The texture's terrain definition if it exists, + otherwise the default terrain if it exists, + otherwise NULL. +--------------------------------------------------*/ + +terrain_t *K_GetTerrainForTextureNum(INT32 textureNum); + + +/*-------------------------------------------------- + terrain_t *K_GetTerrainForFlatNum(INT32 flatID); + + Returns the terrain definition applied to + the level flat ID. + + Input Arguments:- + flatID - The level flat's ID. + + Return:- + The level flat's terrain definition if it exists, + otherwise the default terrain if it exists, + otherwise NULL. +--------------------------------------------------*/ + +terrain_t *K_GetTerrainForFlatNum(INT32 flatID); + + +/*-------------------------------------------------- + void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID); + + Updates an object's terrain pointer, based on + the level flat ID supplied. Intended to be called + when the object moves to new floors. + + Input Arguments:- + mo - The object to update. + flatID - The level flat ID the object is standing on. + + Return:- + None +--------------------------------------------------*/ + +void K_UpdateMobjTerrain(mobj_t *mo, INT32 flatID); + + +/*-------------------------------------------------- + void K_ProcessTerrainEffect(mobj_t *mo); + + Handles applying terrain effects to the object, + intended to be called in a thinker. + + Currently only intended for players, but + could be modified to be inclusive of all + object types. + + Input Arguments:- + mo - The object to apply effects to. + + Return:- + None +--------------------------------------------------*/ + +void K_ProcessTerrainEffect(mobj_t *mo); + + +/*-------------------------------------------------- + void K_SetDefaultFriction(mobj_t *mo); + + Resets an object to their default friction values. + If they are on terrain with different friction, + they will update to that value. + + Input Arguments:- + mo - The object to reset the friction values of. + + Return:- + None +--------------------------------------------------*/ + +void K_SetDefaultFriction(mobj_t *mo); + + +/*-------------------------------------------------- + void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact); + + Spawns the splash particles for an object's + terrain type. Intended to be called when hitting a floor. + + Input Arguments:- + mo - The object to spawn a splash for. + + Return:- + None +--------------------------------------------------*/ +void K_SpawnSplashForMobj(mobj_t *mo, fixed_t impact); + + +/*-------------------------------------------------- + void K_HandleFootstepParticles(mobj_t *mo); + + Spawns the footstep particles for an object's + terrain type. Intended to be called every tic. + + Input Arguments:- + mo - The object to spawn footsteps for. + + Return:- + None +--------------------------------------------------*/ + +void K_HandleFootstepParticles(mobj_t *mo); + + +/*-------------------------------------------------- + void K_InitTerrain(UINT16 wadNum); + + Finds the TERRAIN lumps in a WAD/PK3, and + processes all of them. + + Input Arguments:- + wadNum - WAD file ID to process. + + Return:- + None +--------------------------------------------------*/ + +void K_InitTerrain(UINT16 wadNum); + +#endif // __K_TERRAIN_H__ diff --git a/src/lua_baselib.c b/src/lua_baselib.c index eec97d2d0..534325283 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3624,11 +3624,10 @@ static int lib_kSpawnSparkleTrail(lua_State *L) static int lib_kSpawnWipeoutTrail(lua_State *L) { mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); - boolean offroad = lua_optboolean(L, 2); NOHUD if (!mo) return LUA_ErrInvalid(L, "mobj_t"); - K_SpawnWipeoutTrail(mo, offroad); + K_SpawnWipeoutTrail(mo); return 0; } diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 8a4a1e7a5..4c2a4f24d 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -494,9 +494,6 @@ static int mobj_set(lua_State *L) if (hook_cmd_running) return luaL_error(L, "Do not alter mobj_t in CMD building code!"); - if (hook_cmd_running) - return luaL_error(L, "Do not alter mobj_t in BuildCMD code!"); - switch(field) { case mobj_valid: diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index f7c20ab64..17e1a39b1 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -281,7 +281,7 @@ static int player_get(lua_State *L) else if (fastcmp(field,"handleboost")) lua_pushinteger(L, plr->handleboost); else if (fastcmp(field,"boostangle")) - lua_pushinteger(L, plr->boostangle); + lua_pushangle(L, plr->boostangle); else if (fastcmp(field,"draftpower")) lua_pushinteger(L, plr->draftpower); else if (fastcmp(field,"draftleeway")) @@ -499,9 +499,6 @@ static int player_set(lua_State *L) if (hook_cmd_running) return luaL_error(L, "Do not alter player_t in CMD building code!"); - if (hook_cmd_running) - return luaL_error(L, "Do not alter player_t in BuildCMD code!"); - if (fastcmp(field,"mo")) { mobj_t *newmo = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); plr->mo->player = NULL; // remove player pointer from old mobj @@ -626,7 +623,7 @@ static int player_set(lua_State *L) else if (fastcmp(field,"handleboost")) plr->handleboost = luaL_checkinteger(L, 3); else if (fastcmp(field,"boostangle")) - plr->boostangle = luaL_checkinteger(L, 3); + plr->boostangle = luaL_checkangle(L, 3); else if (fastcmp(field,"draftpower")) plr->draftpower = luaL_checkinteger(L, 3); else if (fastcmp(field,"draftleeway")) @@ -861,7 +858,7 @@ static int karthud_set(lua_State *L) if (hud_running) return luaL_error(L, "Do not alter player_t in HUD rendering code!"); if (hook_cmd_running) - return luaL_error(L, "Do not alter player_t in BuildCMD code!"); + return luaL_error(L, "Do not alter player_t in CMD building code!"); karthud[ks] = i; return 0; } diff --git a/src/p_inter.c b/src/p_inter.c index 6a352ecec..337d2b84f 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1054,6 +1054,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->flags |= MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY; P_SetThingPosition(target); target->standingslope = NULL; + target->terrain = NULL; target->pmomz = 0; target->player->playerstate = PST_DEAD; diff --git a/src/p_local.h b/src/p_local.h index 51a43b639..2cb291b11 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -168,7 +168,7 @@ boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec); boolean P_IsObjectOnRealGround(mobj_t *mo, sector_t *sec); // SRB2Kart #define P_IsObjectFlipped(o) ((o)->eflags & MFE_VERTICALFLIP) boolean P_InQuicksand(mobj_t *mo); -boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff); +boolean P_PlayerHitFloor(player_t *player, boolean fromAir); void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative); void P_RestoreMusic(player_t *player); @@ -385,6 +385,7 @@ extern camera_t *mapcampointer; extern fixed_t tmx; extern fixed_t tmy; extern pslope_t *tmfloorslope, *tmceilingslope; +extern INT32 tmfloorpic, tmceilingpic; /* cphipps 2004/08/30 */ extern void P_MapStart(void); diff --git a/src/p_map.c b/src/p_map.c index 33c1821d9..8fb58cd0a 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -24,12 +24,13 @@ #include "r_sky.h" #include "s_sound.h" #include "w_wad.h" + #include "k_kart.h" // SRB2kart 011617 #include "k_collide.h" #include "k_respawn.h" - #include "hu_stuff.h" // SRB2kart #include "i_system.h" // SRB2kart +#include "k_terrain.h" #include "r_splats.h" @@ -60,6 +61,7 @@ mobj_t *tmfloorthing; // the thing corresponding to tmfloorz or NULL if tmfloorz mobj_t *tmhitthing; // the solid thing you bumped into (for collisions) ffloor_t *tmfloorrover, *tmceilingrover; pslope_t *tmfloorslope, *tmceilingslope; +INT32 tmfloorpic, tmceilingpic; static fixed_t tmfloorstep; static fixed_t tmceilingstep; @@ -325,6 +327,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) } object->standingslope = NULL; // Okay, now we know it's not going to be relevant - no launching off at silly angles for you. + object->terrain = NULL; object->eflags |= MFE_SPRUNG; // apply this flag asap! spring->flags &= ~(MF_SOLID|MF_SPECIAL); // De-solidify @@ -466,6 +469,7 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) } object->standingslope = NULL; // No launching off at silly angles for you. + object->terrain = NULL; switch (spring->type) { @@ -1452,6 +1456,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmfloorz = thing->z + thing->height; tmfloorrover = NULL; tmfloorslope = NULL; + tmfloorpic = -1; } return true; } @@ -1471,6 +1476,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmfloorz = tmceilingz = topz; // block while in air tmceilingrover = NULL; tmceilingslope = NULL; + tmceilingpic = -1; tmfloorthing = thing; // needed for side collision } else if (topz < tmceilingz && tmthing->z <= thing->z+thing->height) @@ -1478,6 +1484,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmceilingz = topz; tmceilingrover = NULL; tmceilingslope = NULL; + tmceilingpic = -1; tmfloorthing = thing; // thing we may stand on } } @@ -1493,6 +1500,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmceilingz = thing->z; tmceilingrover = NULL; tmceilingslope = NULL; + tmceilingpic = -1; } return true; } @@ -1512,6 +1520,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmfloorz = tmceilingz = topz; // block while in air tmfloorrover = NULL; tmfloorslope = NULL; + tmfloorpic = -1; tmfloorthing = thing; // needed for side collision } else if (topz > tmfloorz && tmthing->z+tmthing->height >= thing->z) @@ -1519,6 +1528,7 @@ static boolean PIT_CheckThing(mobj_t *thing) tmfloorz = topz; tmfloorrover = NULL; tmfloorslope = NULL; + tmfloorpic = -1; tmfloorthing = thing; // thing we may stand on } } @@ -1698,6 +1708,7 @@ static boolean PIT_CheckLine(line_t *ld) ceilingline = ld; tmceilingrover = openceilingrover; tmceilingslope = opentopslope; + tmceilingpic = opentoppic; tmceilingstep = openceilingstep; if (thingtop == tmthing->ceilingz) { @@ -1710,6 +1721,7 @@ static boolean PIT_CheckLine(line_t *ld) tmfloorz = openbottom; tmfloorrover = openfloorrover; tmfloorslope = openbottomslope; + tmfloorpic = openbottompic; tmfloorstep = openfloorstep; if (tmthing->z == tmthing->floorz) { @@ -1806,6 +1818,8 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmceilingrover = NULL; tmfloorslope = newsubsec->sector->f_slope; tmceilingslope = newsubsec->sector->c_slope; + tmfloorpic = newsubsec->sector->floorpic; + tmceilingpic = newsubsec->sector->ceilingpic; tmfloorstep = 0; tmceilingstep = 0; @@ -1859,6 +1873,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmfloorz = topheight - sinklevel; tmfloorrover = rover; tmfloorslope = *rover->t_slope; + tmfloorpic = *rover->toppic; } } else if (thing->eflags & MFE_VERTICALFLIP && thingtop <= bottomheight + sinklevel && thing->momz >= 0) @@ -1867,6 +1882,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmceilingz = bottomheight + sinklevel; tmceilingrover = rover; tmceilingslope = *rover->b_slope; + tmceilingpic = *rover->bottompic; } } } @@ -1890,6 +1906,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmfloorz = thing->z; tmfloorrover = rover; tmfloorslope = NULL; + tmfloorpic = *rover->toppic; } } // Quicksand blocks never change heights otherwise. @@ -1907,6 +1924,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmfloorz = tmdropoffz = topheight; tmfloorrover = rover; tmfloorslope = *rover->t_slope; + tmfloorpic = *rover->toppic; } if (bottomheight < tmceilingz && abs(delta1) >= abs(delta2) && !(rover->flags & FF_PLATFORM) @@ -1915,6 +1933,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmceilingz = tmdrpoffceilz = bottomheight; tmceilingrover = rover; tmceilingslope = *rover->b_slope; + tmceilingpic = *rover->bottompic; } } } @@ -1989,12 +2008,14 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) tmfloorz = tmdropoffz = polytop; tmfloorslope = NULL; tmfloorrover = NULL; + tmfloorpic = polysec->ceilingpic; } if (polybottom < tmceilingz && abs(delta1) >= abs(delta2)) { tmceilingz = tmdrpoffceilz = polybottom; tmceilingslope = NULL; tmceilingrover = NULL; + tmceilingpic = polysec->floorpic; } } plink = (polymaplink_t *)(plink->link.next); @@ -2412,6 +2433,8 @@ boolean PIT_PushableMoved(mobj_t *thing) ffloor_t *oldceilrover = tmceilingrover; pslope_t *oldfslope = tmfloorslope; pslope_t *oldcslope = tmceilingslope; + INT32 oldfpic = tmfloorpic; + INT32 oldcpic = tmceilingpic; // Move the player P_TryMove(thing, thing->x+stand->momx, thing->y+stand->momy, true); @@ -2428,6 +2451,8 @@ boolean PIT_PushableMoved(mobj_t *thing) tmceilingrover = oldceilrover; tmfloorslope = oldfslope; tmceilingslope = oldcslope; + tmfloorpic = oldfpic; + tmceilingpic = oldcpic; thing->momz = stand->momz; } else @@ -2677,9 +2702,14 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (!(thing->flags & MF_NOCLIPHEIGHT)) { // Assign thing's standingslope if needed - if (thing->z <= tmfloorz && !(thing->eflags & MFE_VERTICALFLIP)) { + if (thing->z <= tmfloorz && !(thing->eflags & MFE_VERTICALFLIP)) + { + K_UpdateMobjTerrain(thing, tmfloorpic); + if (!startingonground && tmfloorslope) + { P_HandleSlopeLanding(thing, tmfloorslope); + } if (thing->momz <= 0) { @@ -2687,12 +2717,19 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) P_SetPitchRollFromSlope(thing, thing->standingslope); if (thing->momz == 0 && thing->player && !startingonground) + { P_PlayerHitFloor(thing->player, true); + } } } - else if (thing->z+thing->height >= tmceilingz && (thing->eflags & MFE_VERTICALFLIP)) { + else if (thing->z+thing->height >= tmceilingz && (thing->eflags & MFE_VERTICALFLIP)) + { + K_UpdateMobjTerrain(thing, tmceilingpic); + if (!startingonground && tmceilingslope) + { P_HandleSlopeLanding(thing, tmceilingslope); + } if (thing->momz >= 0) { @@ -2700,12 +2737,18 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) P_SetPitchRollFromSlope(thing, thing->standingslope); if (thing->momz == 0 && thing->player && !startingonground) + { P_PlayerHitFloor(thing->player, true); + } } } } - else // don't set standingslope if you're not going to clip against it + else + { + // don't set standingslope if you're not going to clip against it thing->standingslope = NULL; + thing->terrain = NULL; + } /* FIXME: slope step down (even up) has some false positives, so just ignore them entirely. */ diff --git a/src/p_maputl.c b/src/p_maputl.c index 5acddf09c..555928a3a 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -342,6 +342,7 @@ fixed_t openceilingstep; fixed_t openceilingdrop; fixed_t openfloorstep; fixed_t openfloordrop; +INT32 opentoppic, openbottompic; // P_CameraLineOpening // P_LineOpening, but for camera @@ -537,6 +538,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) highceiling = INT32_MIN; lowfloor = INT32_MAX; opentopslope = openbottomslope = NULL; + opentoppic = openbottompic = -1; openceilingstep = 0; openceilingdrop = 0; openfloorstep = 0; @@ -556,6 +558,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) opentop = height[lo]; highceiling = height[hi]; opentopslope = sector[lo]->c_slope; + opentoppic = sector[lo]->ceilingpic; if (mobj) { @@ -575,6 +578,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) openbottom = height[hi]; lowfloor = height[lo]; openbottomslope = sector[hi]->f_slope; + openbottompic = sector[hi]->floorpic; if (mobj) { @@ -747,6 +751,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) if (bottomheight < open[FRONT].top) { open[FRONT].top = bottomheight; opentopslope = *rover->b_slope; + opentoppic = *rover->bottompic; open[FRONT].ceilingrover = rover; } else if (bottomheight < highceiling) @@ -758,6 +763,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) if (topheight > open[FRONT].bottom) { open[FRONT].bottom = topheight; openbottomslope = *rover->t_slope; + openbottompic = *rover->toppic; open[FRONT].floorrover = rover; } else if (topheight > lowfloor) @@ -789,6 +795,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) if (bottomheight < open[BACK].top) { open[BACK].top = bottomheight; opentopslope = *rover->b_slope; + opentoppic = *rover->bottompic; open[BACK].ceilingrover = rover; } else if (bottomheight < highceiling) @@ -800,6 +807,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) if (topheight > open[BACK].bottom) { open[BACK].bottom = topheight; openbottomslope = *rover->t_slope; + openbottompic = *rover->toppic; open[BACK].floorrover = rover; } else if (topheight > lowfloor) diff --git a/src/p_maputl.h b/src/p_maputl.h index 6f0c1f8ad..43a7fb507 100644 --- a/src/p_maputl.h +++ b/src/p_maputl.h @@ -63,6 +63,7 @@ extern fixed_t openceilingstep; extern fixed_t openceilingdrop; extern fixed_t openfloorstep; extern fixed_t openfloordrop; +extern INT32 opentoppic, openbottompic; void P_LineOpening(line_t *plinedef, mobj_t *mobj); diff --git a/src/p_mobj.c b/src/p_mobj.c index 37385c735..f3d326862 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -40,6 +40,7 @@ #include "k_color.h" #include "k_respawn.h" #include "k_bot.h" +#include "k_terrain.h" static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); @@ -1340,7 +1341,7 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy) mo->momy = FixedMul(mo->momy, mo->friction); } - mo->friction = ORIG_FRICTION; + K_SetDefaultFriction(mo); } } else @@ -1505,7 +1506,10 @@ void P_XYMovement(mobj_t *mo) oldy = mo->y; if (mo->flags & MF_NOCLIPHEIGHT) + { mo->standingslope = NULL; + mo->terrain = NULL; + } // adjust various things based on slope if (mo->standingslope && abs(mo->standingslope->zdelta) > FRACUNIT>>8) { @@ -1671,6 +1675,7 @@ void P_XYMovement(mobj_t *mo) { mo->momz = transfermomz; mo->standingslope = NULL; + mo->terrain = NULL; P_SetPitchRoll(mo, ANGLE_90, transferslope->xydirection + (transferslope->zangle @@ -1782,6 +1787,7 @@ void P_XYMovement(mobj_t *mo) mo->momz = P_MobjFlip(mo)*FRACUNIT/2; mo->z = predictedz + P_MobjFlip(mo); mo->standingslope = NULL; + mo->terrain = NULL; //CONS_Printf("Launched off of flat surface running into downward slope\n"); } } @@ -2274,6 +2280,8 @@ boolean P_ZMovement(mobj_t *mo) } P_CheckPosition(mo, mo->x, mo->y); // Sets mo->standingslope correctly + K_UpdateMobjTerrain(mo, ((mo->eflags & MFE_VERTICALFLIP) ? tmceilingpic : tmfloorpic)); + if (((mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope) && (mo->type != MT_STEAM)) { mo->standingslope = (mo->eflags & MFE_VERTICALFLIP) ? tmceilingslope : tmfloorslope; @@ -2503,12 +2511,17 @@ boolean P_ZMovement(mobj_t *mo) if (mo->type == MT_STEAM) return true; } - else if (!(mo->flags & MF_NOGRAVITY)) // Gravity here! + else { - /// \todo may not be needed (done in P_MobjThinker normally) - mo->eflags &= ~MFE_JUSTHITFLOOR; + mo->terrain = NULL; - P_CheckGravity(mo, true); + if (!(mo->flags & MF_NOGRAVITY)) // Gravity here! + { + /// \todo may not be needed (done in P_MobjThinker normally) + mo->eflags &= ~MFE_JUSTHITFLOOR; + + P_CheckGravity(mo, true); + } } if (((mo->z + mo->height > mo->ceilingz && !(mo->eflags & MFE_VERTICALFLIP)) @@ -2712,20 +2725,29 @@ void P_PlayerZMovement(mobj_t *mo) if (onground && !(mo->flags & MF_NOCLIPHEIGHT)) { if (mo->eflags & MFE_VERTICALFLIP) + { mo->z = mo->ceilingz - mo->height; + } else + { mo->z = mo->floorz; + } + + K_UpdateMobjTerrain(mo, (mo->eflags & MFE_VERTICALFLIP ? tmceilingpic : tmfloorpic)); // Get up if you fell. if (mo->player->panim == PA_HURT && mo->player->spinouttimer == 0 && mo->player->tumbleBounces == 0) + { P_SetPlayerMobjState(mo, S_KART_STILL); + } - if (!mo->standingslope && (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)) { + if (!mo->standingslope && (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)) + { // Handle landing on slope during Z movement P_HandleSlopeLanding(mo, (mo->eflags & MFE_VERTICALFLIP ? tmceilingslope : tmfloorslope)); } - if (P_MobjFlip(mo)*mo->momz < 0) // falling + if (P_MobjFlip(mo) * mo->momz < 0) // falling { boolean clipmomz = !(P_CheckDeathPitCollide(mo)); @@ -2736,29 +2758,38 @@ void P_PlayerZMovement(mobj_t *mo) P_PlayerPolyObjectZMovement(mo); if (clipmomz) + { mo->momz = (tmfloorthing ? tmfloorthing->momz : 0); + } } else if (tmfloorthing) - mo->momz = tmfloorthing->momz; - } - else if (!(mo->flags & MF_NOGRAVITY)) // Gravity here! - { - if (P_IsObjectInGoop(mo) && !(mo->flags & MF_NOCLIPHEIGHT)) { - if (mo->z < mo->floorz) - { - mo->z = mo->floorz; - mo->momz = 0; - } - else if (mo->z + mo->height > mo->ceilingz) - { - mo->z = mo->ceilingz - mo->height; - mo->momz = 0; - } + mo->momz = tmfloorthing->momz; + } + } + else + { + mo->terrain = NULL; + + if (!(mo->flags & MF_NOGRAVITY)) // Gravity here! + { + if (P_IsObjectInGoop(mo) && !(mo->flags & MF_NOCLIPHEIGHT)) + { + if (mo->z < mo->floorz) + { + mo->z = mo->floorz; + mo->momz = 0; + } + else if (mo->z + mo->height > mo->ceilingz) + { + mo->z = mo->ceilingz - mo->height; + mo->momz = 0; + } + } + /// \todo may not be needed (done in P_MobjThinker normally) + mo->eflags &= ~MFE_JUSTHITFLOOR; + P_CheckGravity(mo, true); } - /// \todo may not be needed (done in P_MobjThinker normally) - mo->eflags &= ~MFE_JUSTHITFLOOR; - P_CheckGravity(mo, true); } if (((mo->eflags & MFE_VERTICALFLIP && mo->z < mo->floorz) || (!(mo->eflags & MFE_VERTICALFLIP) && mo->z + mo->height > mo->ceilingz)) @@ -3048,6 +3079,26 @@ void P_MobjCheckWater(mobj_t *mobj) } } + if (mobj->terrain != NULL) + { + if (mobj->terrain->flags & TRF_LIQUID) + { + // This floor is water. + mobj->eflags |= MFE_TOUCHWATER; + + if (mobj->eflags & MFE_VERTICALFLIP) + { + mobj->watertop = thingtop + height; + mobj->waterbottom = thingtop; + } + else + { + mobj->watertop = mobj->z; + mobj->waterbottom = mobj->z - height; + } + } + } + if (mobj->watertop > top2) mobj->watertop = top2; @@ -3056,7 +3107,9 @@ void P_MobjCheckWater(mobj_t *mobj) // Spectators and dead players don't get to do any of the things after this. if (p && (p->spectator || p->playerstate != PST_LIVE)) + { return; + } // The rest of this code only executes on a water state change. if (!!(mobj->eflags & MFE_UNDERWATER) == wasinwater) @@ -3073,6 +3126,30 @@ void P_MobjCheckWater(mobj_t *mobj) || ((mobj->info->flags & MF_PUSHABLE) && mobj->fuse) // Previously pushable, might be moving still ) { + fixed_t waterZ = INT32_MAX; + fixed_t solidZ = INT32_MAX; + fixed_t diff = INT32_MAX; + + fixed_t thingZ = INT32_MAX; + boolean splashValid = false; + + if (mobj->eflags & MFE_VERTICALFLIP) + { + waterZ = mobj->waterbottom; + solidZ = mobj->ceilingz; + } + else + { + waterZ = mobj->watertop; + solidZ = mobj->floorz; + } + + diff = waterZ - solidZ; + if (mobj->eflags & MFE_VERTICALFLIP) + { + diff = -diff; + } + // Time to spawn the bubbles! { INT32 i; @@ -3131,12 +3208,15 @@ void P_MobjCheckWater(mobj_t *mobj) // Check to make sure you didn't just cross into a sector to jump out of // that has shallower water than the block you were originally in. - if ((!(mobj->eflags & MFE_VERTICALFLIP) && mobj->watertop-mobj->floorz <= height>>1) - || ((mobj->eflags & MFE_VERTICALFLIP) && mobj->ceilingz-mobj->waterbottom <= height>>1)) + if (diff <= (height >> 1)) + { return; + } - if (mobj->eflags & MFE_GOOWATER || wasingoo) { // Decide what happens to your momentum when you enter/leave goopy water. - if (P_MobjFlip(mobj)*mobj->momz > 0) + if (mobj->eflags & MFE_GOOWATER || wasingoo) + { + // Decide what happens to your momentum when you enter/leave goopy water. + if (P_MobjFlip(mobj) * mobj->momz > 0) { mobj->momz -= (mobj->momz/8); // cut momentum a little bit to prevent multiple bobs //CONS_Printf("leaving\n"); @@ -3148,25 +3228,42 @@ void P_MobjCheckWater(mobj_t *mobj) //CONS_Printf("entering\n"); } } - else if (wasinwater && P_MobjFlip(mobj)*mobj->momz > 0) - mobj->momz = FixedMul(mobj->momz, FixedDiv(780*FRACUNIT, 457*FRACUNIT)); // Give the mobj a little out-of-water boost. - - if (P_MobjFlip(mobj)*mobj->momz < 0) + else if (wasinwater && P_MobjFlip(mobj) * mobj->momz > 0) { - if ((mobj->eflags & MFE_VERTICALFLIP && thingtop-(height>>1)-mobj->momz <= mobj->waterbottom) - || (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z+(height>>1)-mobj->momz >= mobj->watertop)) + // Give the mobj a little out-of-water boost. + mobj->momz = FixedMul(mobj->momz, FixedDiv(780*FRACUNIT, 457*FRACUNIT)); + } + + if (mobj->eflags & MFE_VERTICALFLIP) + { + thingZ = thingtop - (height >> 1); + splashValid = (thingZ - mobj->momz <= waterZ); + } + else + { + thingZ = mobj->z + (height >> 1); + splashValid = (thingZ - mobj->momz >= waterZ); + } + + if (P_MobjFlip(mobj) * mobj->momz <= 0) + { + if (splashValid == true) { // Spawn a splash mobj_t *splish; mobjtype_t splishtype = (mobj->eflags & MFE_TOUCHLAVA) ? MT_LAVASPLISH : MT_SPLISH; + if (mobj->eflags & MFE_VERTICALFLIP) { - splish = P_SpawnMobj(mobj->x, mobj->y, mobj->waterbottom-FixedMul(mobjinfo[splishtype].height, mobj->scale), splishtype); + splish = P_SpawnMobj(mobj->x, mobj->y, waterZ - FixedMul(mobjinfo[splishtype].height, mobj->scale), splishtype); splish->flags2 |= MF2_OBJECTFLIP; splish->eflags |= MFE_VERTICALFLIP; } else - splish = P_SpawnMobj(mobj->x, mobj->y, mobj->watertop, splishtype); + { + splish = P_SpawnMobj(mobj->x, mobj->y, waterZ, splishtype); + } + splish->destscale = mobj->scale; P_SetScale(splish, mobj->scale); } @@ -3175,40 +3272,37 @@ void P_MobjCheckWater(mobj_t *mobj) if (p && p->waterskip < 2 && ((p->speed/3 > abs(mobj->momz)) // Going more forward than horizontal, so you can skip across the water. || (p->speed > 20*mapobjectscale && p->waterskip)) // Already skipped once, so you can skip once more! - && ((!(mobj->eflags & MFE_VERTICALFLIP) && thingtop - mobj->momz > mobj->watertop) - || ((mobj->eflags & MFE_VERTICALFLIP) && mobj->z - mobj->momz < mobj->waterbottom))) + && (splashValid == true)) { - const fixed_t hop = 5<scale; mobj->momx = (4*mobj->momx)/5; mobj->momy = (4*mobj->momy)/5; - - if (mobj->eflags & MFE_VERTICALFLIP) - mobj->momz = FixedMul(-hop, mobj->scale); - else - mobj->momz = FixedMul(hop, mobj->scale); + mobj->momz = hop * P_MobjFlip(mobj); p->waterskip++; } } - else if (P_MobjFlip(mobj)*mobj->momz > 0) + else if (P_MobjFlip(mobj) * mobj->momz > 0) { - if (((mobj->eflags & MFE_VERTICALFLIP && thingtop-(height>>1)-mobj->momz > mobj->waterbottom) - || (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z+(height>>1)-mobj->momz < mobj->watertop)) - && !(mobj->eflags & MFE_UNDERWATER)) // underwater check to prevent splashes on opposite side + if (splashValid == true && !(mobj->eflags & MFE_UNDERWATER)) // underwater check to prevent splashes on opposite side { // Spawn a splash mobj_t *splish; mobjtype_t splishtype = (mobj->eflags & MFE_TOUCHLAVA) ? MT_LAVASPLISH : MT_SPLISH; + if (mobj->eflags & MFE_VERTICALFLIP) { - splish = P_SpawnMobj(mobj->x, mobj->y, mobj->waterbottom-FixedMul(mobjinfo[splishtype].height, mobj->scale), splishtype); + splish = P_SpawnMobj(mobj->x, mobj->y, waterZ - FixedMul(mobjinfo[splishtype].height, mobj->scale), splishtype); splish->flags2 |= MF2_OBJECTFLIP; splish->eflags |= MFE_VERTICALFLIP; } else - splish = P_SpawnMobj(mobj->x, mobj->y, mobj->watertop, splishtype); + { + splish = P_SpawnMobj(mobj->x, mobj->y, waterZ, splishtype); + } + splish->destscale = mobj->scale; P_SetScale(splish, mobj->scale); } diff --git a/src/p_mobj.h b/src/p_mobj.h index e5bba25de..53614c5dd 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -397,6 +397,7 @@ typedef struct mobj_s fixed_t sprxoff, spryoff, sprzoff; // Sprite offsets in real space, does NOT affect position or collision + struct terrain_s *terrain; // Terrain definition of the floor this object last hit. NULL when in the air. INT32 hitlag; // Sal-style hit lag, straight from Captain Fetch's jowls // WARNING: New fields must be added separately to savegame and Lua. diff --git a/src/p_saveg.c b/src/p_saveg.c index 357dbb0aa..7710ee0de 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -38,6 +38,7 @@ // SRB2Kart #include "k_battle.h" #include "k_pwrlv.h" +#include "k_terrain.h" savedata_t savedata; UINT8 *save_p; @@ -1540,6 +1541,7 @@ typedef enum MD2_KITEMCAP = 1<<26, MD2_ITNEXT = 1<<27, MD2_LASTMOMZ = 1<<28, + MD2_TERRAIN = 1<<29, } mobj_diff2_t; typedef enum @@ -1782,6 +1784,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_ITNEXT; if (mobj->lastmomz) diff2 |= MD2_LASTMOMZ; + if (mobj->terrain != NULL) + diff2 |= MD2_TERRAIN; if (diff2 != 0) diff |= MD_MORE; @@ -1979,6 +1983,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) { WRITEINT32(save_p, mobj->lastmomz); } + if (diff2 & MD2_TERRAIN) + { + WRITEUINT32(save_p, K_GetTerrainHeapIndex(mobj->terrain)); + } WRITEUINT32(save_p, mobj->mobjnum); } @@ -3077,6 +3085,14 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) { mobj->lastmomz = READINT32(save_p); } + if (diff2 & MD2_TERRAIN) + { + mobj->terrain = (terrain_t *)(size_t)READUINT32(save_p); + } + else + { + mobj->terrain = NULL; + } if (diff & MD_REDFLAG) { diff --git a/src/p_setup.c b/src/p_setup.c index db9a35a11..bf64566b7 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -91,6 +91,7 @@ #include "k_waypoint.h" #include "k_bot.h" #include "k_grandprix.h" +#include "k_terrain.h" // TRF_TRIPWIRE #include "k_brightmap.h" // Replay names have time @@ -1940,18 +1941,15 @@ static void P_ProcessLinedefsAfterSidedefs(void) size_t i = numlines; register line_t *ld = lines; - const INT32 TEX_TRIPWIRE = R_TextureNumForName("TRIPWIRE"); - const INT32 TEX_4RIPWIRE = R_TextureNumForName("4RIPWIRE"); - for (; i--; ld++) { INT32 midtexture = sides[ld->sidenum[0]].midtexture; + terrain_t *terrain = K_GetTerrainForTextureNum(midtexture); ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here ld->backsector = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0; - if (midtexture == TEX_TRIPWIRE || - midtexture == TEX_4RIPWIRE) + if (terrain != NULL && (terrain->flags & TRF_TRIPWIRE)) { ld->tripwire = true; } diff --git a/src/p_slopes.c b/src/p_slopes.c index 1bde8f4ee..6c660ad0f 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -850,6 +850,7 @@ void P_SlopeLaunch(mobj_t *mo) //CONS_Printf("Launched off of slope.\n"); mo->standingslope = NULL; + mo->terrain = NULL; if (mo->player) { diff --git a/src/p_spec.c b/src/p_spec.c index bf7784cd3..fca9e9035 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -43,6 +43,7 @@ #include "k_kart.h" #include "console.h" // CON_LogMessage #include "k_respawn.h" +#include "k_terrain.h" #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -4333,7 +4334,9 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers // Conveyor stuff if (section3 == 2 || section3 == 4) + { player->onconveyor = section3; + } special = section1; @@ -4657,7 +4660,7 @@ DoneSection2: case 6: // SRB2kart 190117 - Sneaker Panel if (roversector || P_MobjReadyToTrigger(player->mo, sector)) { - if (!player->floorboost) + if (player->floorboost == 0) player->floorboost = 3; else player->floorboost = 2; @@ -5050,6 +5053,7 @@ void P_PlayerInSpecialSector(player_t *player) if (!player->mo) return; + K_ProcessTerrainEffect(player->mo); originalsector = player->mo->subsector->sector; P_PlayerOnSpecial3DFloor(player, originalsector); // Handle FOFs first. diff --git a/src/p_user.c b/src/p_user.c index 5813d1ac4..a5b07fbc6 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -52,6 +52,7 @@ #include "k_respawn.h" #include "k_bot.h" #include "k_grandprix.h" +#include "k_terrain.h" // K_SpawnSplashForMobj #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -1274,17 +1275,18 @@ void P_DoPlayerExit(player_t *player) // // Handles player hitting floor surface. // Returns whether to clip momz. -boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) +boolean P_PlayerHitFloor(player_t *player, boolean fromAir) { boolean clipmomz; - (void)dorollstuff; - I_Assert(player->mo != NULL); clipmomz = !(P_CheckDeathPitCollide(player->mo)); - // SRB2Kart: removed lots of really vanilla-specific code here + if (fromAir == true && clipmomz == true) + { + K_SpawnSplashForMobj(player->mo, abs(player->mo->momz)); + } return clipmomz; } @@ -2138,8 +2140,6 @@ void P_MovePlayer(player_t *player) player->mo->rollangle = 0; } - player->mo->movefactor = FRACUNIT; // We're not going to do any more with this, so let's change it back for the next frame. - //{ SRB2kart // Drifting sound @@ -2267,7 +2267,7 @@ void P_MovePlayer(player_t *player) K_SpawnSparkleTrail(player->mo); if (player->wipeoutslow > 1 && (leveltime & 1)) - K_SpawnWipeoutTrail(player->mo, false); + K_SpawnWipeoutTrail(player->mo); K_DriftDustHandling(player->mo); @@ -4502,8 +4502,6 @@ void P_PlayerThink(player_t *player) P_MovePlayer(player); } - player->mo->movefactor = FRACUNIT; // We're not going to do any more with this, so let's change it back for the next frame. - // Unset statis flag after moving. // In other words, if you manually set stasis via code, // it lasts for one tic. diff --git a/src/r_bsp.c b/src/r_bsp.c index e9d51c03a..6095f3739 100644 --- a/src/r_bsp.c +++ b/src/r_bsp.c @@ -23,6 +23,8 @@ #include "z_zone.h" // Check R_Prep3DFloors #include "taglist.h" +#include "k_terrain.h" + seg_t *curline; side_t *sidedef; line_t *linedef; @@ -67,11 +69,35 @@ boolean R_IsRipplePlane(sector_t *sector, ffloor_t *rover, int ceiling) static void R_PlaneLightOverride(sector_t *sector, boolean ceiling, INT32 *lightlevel) { - if (GETSECSPECIAL(sector->special, 4) == 6) // Fullbright sneaker panels + terrain_t *t = NULL; + + if (ceiling == true) { - if ((ceiling && (sector->flags & SF_FLIPSPECIAL_CEILING)) - || (!ceiling && (sector->flags & SF_FLIPSPECIAL_FLOOR))) + t = K_GetTerrainForFlatNum(sector->ceilingpic); + } + else + { + t = K_GetTerrainForFlatNum(sector->floorpic); + } + + if (t != NULL) + { + if (t->flags & TRF_SNEAKERPANEL) + { *lightlevel = 255; + } + } + else + { + // Sector effect sneaker panels (DEPRECATED) + if (GETSECSPECIAL(sector->special, 4) == 6) + { + if ((ceiling && (sector->flags & SF_FLIPSPECIAL_CEILING)) + || (!ceiling && (sector->flags & SF_FLIPSPECIAL_FLOOR))) + { + *lightlevel = 255; + } + } } } diff --git a/src/tables.c b/src/tables.c index 42ad6a73c..8c4dc50e1 100644 --- a/src/tables.c +++ b/src/tables.c @@ -185,6 +185,16 @@ INT32 AngleDeltaSigned(angle_t a1, angle_t a2) return (INT32)(a1) - (INT32)(a2); } +float AngleToFloat(angle_t x) +{ + return x / (float)ANG1; +} + +angle_t FloatToAngle(float f) +{ + return (angle_t)(f * ANG1); +} + #include "t_ftan.c" #include "t_fsin.c" diff --git a/src/tables.h b/src/tables.h index 5e5b6e57b..e122975e1 100644 --- a/src/tables.h +++ b/src/tables.h @@ -108,6 +108,8 @@ FUNCMATH angle_t FixedAngleC(fixed_t fa, fixed_t factor); // difference between two angle_t FUNCMATH INT32 AngleDelta(angle_t a1, angle_t a2); FUNCMATH INT32 AngleDeltaSigned(angle_t a1, angle_t a2); +FUNCMATH float AngleToFloat(angle_t x); +FUNCMATH angle_t FloatToAngle(float f); /// The FixedAcos function FUNCMATH angle_t FixedAcos(fixed_t x); diff --git a/src/w_wad.c b/src/w_wad.c index 60339c426..c2c4ed231 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -69,6 +69,8 @@ #include "m_misc.h" // M_MapNumber #include "g_game.h" // G_SetGameModified +#include "k_terrain.h" + #ifdef HWRENDER #include "hardware/hw_main.h" #include "hardware/hw_glob.h" @@ -866,6 +868,8 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) break; } + K_InitTerrain(numwadfiles - 1); + if (refreshdirmenu & REFRESHDIR_GAMEDATA) G_LoadGameData(); DEH_UpdateMaxFreeslots();