diff --git a/src/d_player.h b/src/d_player.h index f2943dd18..f66493a4e 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -446,7 +446,6 @@ typedef struct player_s INT32 underwatertilt; fixed_t offroad; // In Super Mario Kart, going offroad has lee-way of about 1 second before you start losing speed - UINT8 waterskip; // Water skipping counter UINT16 tiregrease; // Reduced friction timer after hitting a spring UINT16 springstars; // Spawn stars around a player when they hit a spring diff --git a/src/k_kart.c b/src/k_kart.c index 5aa1aaeba..a10386980 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3231,7 +3231,7 @@ tripwirepass_t K_TripwirePassConditions(player_t *player) if ( player->flamedash || - player->speed > 2 * K_GetKartSpeed(player, false, true) + player->speed > 2 * K_GetKartSpeed(player, false, false) ) return TRIPWIRE_BOOST; @@ -3249,17 +3249,241 @@ boolean K_TripwirePass(player_t *player) return (player->tripwirePass != TRIPWIRE_NONE); } -boolean K_WaterRun(player_t *player) +boolean K_MovingHorizontally(mobj_t *mobj) { - if ( - player->invincibilitytimer || - player->sneakertimer || - player->tiregrease || - player->flamedash || - player->speed > 2 * K_GetKartSpeed(player, false, true) - ) - return true; - return false; + return (P_AproxDistance(mobj->momx, mobj->momy) / 5 > abs(mobj->momz)); +} + +boolean K_WaterRun(mobj_t *mobj) +{ + switch (mobj->type) + { + case MT_JAWZ: + { + if (mobj->tracer != NULL && P_MobjWasRemoved(mobj->tracer) == false) + { + fixed_t jawzFeet = P_GetMobjFeet(mobj); + fixed_t chaseFeet = P_GetMobjFeet(mobj->tracer); + fixed_t footDiff = (chaseFeet - jawzFeet) * P_MobjFlip(mobj); + + // Water run if the player we're chasing is above/equal to us. + // Start water skipping if they're underneath the water. + return (footDiff > -mobj->tracer->height); + } + + return false; + } + + case MT_PLAYER: + { + if (mobj->player == NULL) + { + return false; + } + + if (mobj->player->invincibilitytimer + || mobj->player->sneakertimer + || mobj->player->tiregrease + || mobj->player->flamedash + || mobj->player->speed > 2 * K_GetKartSpeed(mobj->player, false, false)) + { + return true; + } + + return false; + } + + default: + { + return false; + } + } +} + +boolean K_WaterSkip(mobj_t *mobj) +{ + if (mobj->waterskip >= 2) + { + // Already finished waterskipping. + return false; + } + + switch (mobj->type) + { + case MT_PLAYER: + case MT_ORBINAUT: + case MT_JAWZ: + case MT_BALLHOG: + { + // Allow + break; + } + + default: + { + // Don't allow + return false; + } + } + + if (mobj->waterskip > 0) + { + // Already waterskipping. + // Simply make sure you haven't slowed down drastically. + return (P_AproxDistance(mobj->momx, mobj->momy) > 20 * mapobjectscale); + } + else + { + // Need to be moving horizontally and not vertically + // to be able to start a water skip. + return K_MovingHorizontally(mobj); + } +} + +void K_SpawnWaterRunParticles(mobj_t *mobj) +{ + fixed_t runSpeed = 14 * mobj->scale; + fixed_t curSpeed = INT32_MAX; + fixed_t topSpeed = INT32_MAX; + fixed_t trailScale = FRACUNIT; + + if (mobj->momz != 0) + { + // Only while touching ground. + return; + } + + if (mobj->watertop == INT32_MAX || mobj->waterbottom == INT32_MIN) + { + // Invalid water plane. + return; + } + + if (mobj->player != NULL) + { + if (mobj->player->spectator) + { + // Not as spectator. + return; + } + + if (mobj->player->carry == CR_SLIDING) + { + // Not in water slides. + return; + } + + topSpeed = K_GetKartSpeed(mobj->player, false, false); + runSpeed = FixedMul(runSpeed, mobj->movefactor); + } + else + { + topSpeed = FixedMul(mobj->scale, K_GetKartSpeedFromStat(5)); + } + + curSpeed = P_AproxDistance(mobj->momx, mobj->momy); + + if (curSpeed <= runSpeed) + { + // Not fast enough. + return; + } + + // Near the water plane. + if ((!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z + mobj->height >= mobj->watertop && mobj->z <= mobj->watertop) + || (mobj->eflags & MFE_VERTICALFLIP && mobj->z + mobj->height >= mobj->waterbottom && mobj->z <= mobj->waterbottom)) + { + if (topSpeed > runSpeed) + { + trailScale = FixedMul(FixedDiv(curSpeed - runSpeed, topSpeed - runSpeed), mapobjectscale); + } + else + { + trailScale = mapobjectscale; // Scaling is based off difference between runspeed and top speed + } + + if (trailScale > 0) + { + const angle_t forwardangle = K_MomentumAngle(mobj); + const fixed_t playerVisualRadius = mobj->radius + (8 * mobj->scale); + const size_t numFrames = S_WATERTRAIL8 - S_WATERTRAIL1; + const statenum_t curOverlayFrame = S_WATERTRAIL1 + (leveltime % numFrames); + const statenum_t curUnderlayFrame = S_WATERTRAILUNDERLAY1 + (leveltime % numFrames); + fixed_t x1, x2, y1, y2; + mobj_t *water; + + x1 = mobj->x + mobj->momx + P_ReturnThrustX(mobj, forwardangle + ANGLE_90, playerVisualRadius); + y1 = mobj->y + mobj->momy + P_ReturnThrustY(mobj, forwardangle + ANGLE_90, playerVisualRadius); + x1 = x1 + P_ReturnThrustX(mobj, forwardangle, playerVisualRadius); + y1 = y1 + P_ReturnThrustY(mobj, forwardangle, playerVisualRadius); + + x2 = mobj->x + mobj->momx + P_ReturnThrustX(mobj, forwardangle - ANGLE_90, playerVisualRadius); + y2 = mobj->y + mobj->momy + P_ReturnThrustY(mobj, forwardangle - ANGLE_90, playerVisualRadius); + x2 = x2 + P_ReturnThrustX(mobj, forwardangle, playerVisualRadius); + y2 = y2 + P_ReturnThrustY(mobj, forwardangle, playerVisualRadius); + + // Left + // underlay + water = P_SpawnMobj(x1, y1, + ((mobj->eflags & MFE_VERTICALFLIP) ? mobj->waterbottom - FixedMul(mobjinfo[MT_WATERTRAILUNDERLAY].height, mobj->scale) : mobj->watertop), MT_WATERTRAILUNDERLAY); + P_InitAngle(water, forwardangle - ANGLE_180 - ANGLE_22h); + water->destscale = trailScale; + water->momx = mobj->momx; + water->momy = mobj->momy; + water->momz = mobj->momz; + P_SetScale(water, trailScale); + P_SetMobjState(water, curUnderlayFrame); + + // overlay + water = P_SpawnMobj(x1, y1, + ((mobj->eflags & MFE_VERTICALFLIP) ? mobj->waterbottom - FixedMul(mobjinfo[MT_WATERTRAIL].height, mobj->scale) : mobj->watertop), MT_WATERTRAIL); + P_InitAngle(water, forwardangle - ANGLE_180 - ANGLE_22h); + water->destscale = trailScale; + water->momx = mobj->momx; + water->momy = mobj->momy; + water->momz = mobj->momz; + P_SetScale(water, trailScale); + P_SetMobjState(water, curOverlayFrame); + + // Right + // Underlay + water = P_SpawnMobj(x2, y2, + ((mobj->eflags & MFE_VERTICALFLIP) ? mobj->waterbottom - FixedMul(mobjinfo[MT_WATERTRAILUNDERLAY].height, mobj->scale) : mobj->watertop), MT_WATERTRAILUNDERLAY); + P_InitAngle(water, forwardangle - ANGLE_180 + ANGLE_22h); + water->destscale = trailScale; + water->momx = mobj->momx; + water->momy = mobj->momy; + water->momz = mobj->momz; + P_SetScale(water, trailScale); + P_SetMobjState(water, curUnderlayFrame); + + // Overlay + water = P_SpawnMobj(x2, y2, + ((mobj->eflags & MFE_VERTICALFLIP) ? mobj->waterbottom - FixedMul(mobjinfo[MT_WATERTRAIL].height, mobj->scale) : mobj->watertop), MT_WATERTRAIL); + P_InitAngle(water, forwardangle - ANGLE_180 + ANGLE_22h); + water->destscale = trailScale; + water->momx = mobj->momx; + water->momy = mobj->momy; + water->momz = mobj->momz; + P_SetScale(water, trailScale); + P_SetMobjState(water, curOverlayFrame); + + if (!S_SoundPlaying(mobj, sfx_s3kdbs)) + { + const INT32 volume = (min(trailScale, FRACUNIT) * 255) / FRACUNIT; + S_StartSoundAtVolume(mobj, sfx_s3kdbs, volume); + } + } + + // Little water sound while touching water - just a nicety. + if ((mobj->eflags & MFE_TOUCHWATER) && !(mobj->eflags & MFE_UNDERWATER)) + { + if (P_RandomChance(PR_BUBBLE, FRACUNIT/2) && leveltime % TICRATE == 0) + { + S_StartSound(mobj, sfx_floush); + } + } + } } static fixed_t K_FlameShieldDashVar(INT32 val) @@ -7956,9 +8180,6 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } } - if (P_IsObjectOnGround(player->mo)) - player->waterskip = 0; - if (player->instashield) player->instashield--; @@ -8752,6 +8973,16 @@ INT16 K_UpdateSteeringValue(INT16 inputSteering, INT16 destSteering) return outputSteering; } +static fixed_t K_GetUnderwaterStrafeMul(player_t *player) +{ + const fixed_t minSpeed = 11 * player->mo->scale; + fixed_t baseline = INT32_MAX; + + baseline = 2 * K_GetKartSpeed(player, false, true) / 3; + + return max(0, FixedDiv(player->speed - minSpeed, baseline - minSpeed)); +} + INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) { fixed_t turnfixed = turnvalue * FRACUNIT; @@ -8829,10 +9060,10 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) turnfixed = FixedMul(turnfixed, FRACUNIT + player->handleboost); } - if ((player->mo->eflags & MFE_UNDERWATER) && - player->speed > 11 * player->mo->scale) + if (player->mo->eflags & MFE_UNDERWATER) { - turnfixed /= 2; + fixed_t div = min(FRACUNIT + K_GetUnderwaterStrafeMul(player), 2*FRACUNIT); + turnfixed = FixedDiv(turnfixed, div); } // Weight has a small effect on turning @@ -8843,8 +9074,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) INT32 K_GetUnderwaterTurnAdjust(player_t *player) { - if ((player->mo->eflags & MFE_UNDERWATER) && - player->speed > 11 * player->mo->scale) + if (player->mo->eflags & MFE_UNDERWATER) { INT32 steer = (K_GetKartTurnValue(player, player->steering) << TICCMD_REDUCE); @@ -8852,8 +9082,7 @@ INT32 K_GetUnderwaterTurnAdjust(player_t *player) if (!player->drift) steer = 9 * steer / 5; - return FixedMul(steer, 8 * FixedDiv(player->speed, - 2 * K_GetKartSpeed(player, false, true) / 3)); + return FixedMul(steer, 8 * K_GetUnderwaterStrafeMul(player)); } else return 0; diff --git a/src/k_kart.h b/src/k_kart.h index 1c75288e5..117422ffe 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -142,7 +142,10 @@ boolean K_ApplyOffroad(player_t *player); boolean K_SlopeResistance(player_t *player); tripwirepass_t K_TripwirePassConditions(player_t *player); boolean K_TripwirePass(player_t *player); -boolean K_WaterRun(player_t *player); +boolean K_MovingHorizontally(mobj_t *mobj); +boolean K_WaterRun(mobj_t *mobj); +boolean K_WaterSkip(mobj_t *mobj); +void K_SpawnWaterRunParticles(mobj_t *mobj); void K_ApplyTripWire(player_t *player, tripwirestate_t state); INT16 K_GetSpindashChargeTime(player_t *player); fixed_t K_GetSpindashChargeSpeed(player_t *player); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index bd05ae8b5..7a72b31dc 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -954,12 +954,15 @@ static int lib_pCheckDeathPitCollide(lua_State *L) static int lib_pCheckSolidLava(lua_State *L) { + mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); ffloor_t *rover = *((ffloor_t **)luaL_checkudata(L, 2, META_FFLOOR)); //HUDSAFE INLEVEL + if (!mo) + return LUA_ErrInvalid(L, "mobj_t"); if (!rover) return LUA_ErrInvalid(L, "ffloor_t"); - lua_pushboolean(L, P_CheckSolidLava(rover)); + lua_pushboolean(L, P_CheckSolidLava(mo, rover)); return 1; } diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 418a43bf9..ecd87f49f 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -100,6 +100,7 @@ enum mobj_e { mobj_spryoff, mobj_sprzoff, mobj_hitlag, + mobj_waterskip, mobj_dispoffset }; @@ -181,6 +182,7 @@ static const char *const mobj_opt[] = { "spryoff", "sprzoff", "hitlag", + "waterskip", "dispoffset", NULL}; @@ -460,6 +462,9 @@ static int mobj_get(lua_State *L) case mobj_hitlag: lua_pushinteger(L, mo->hitlag); break; + case mobj_waterskip: + lua_pushinteger(L, mo->waterskip); + break; case mobj_dispoffset: lua_pushinteger(L, mo->dispoffset); break; @@ -835,6 +840,9 @@ static int mobj_set(lua_State *L) case mobj_hitlag: mo->hitlag = luaL_checkinteger(L, 3); break; + case mobj_waterskip: + mo->waterskip = (UINT8)luaL_checkinteger(L, 3); + break; case mobj_dispoffset: mo->dispoffset = luaL_checkinteger(L, 3); break; diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 85e73a12f..c5f5cc4d0 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -264,8 +264,6 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->aizdriftturn); else if (fastcmp(field,"offroad")) lua_pushinteger(L, plr->offroad); - else if (fastcmp(field,"waterskip")) - lua_pushinteger(L, plr->waterskip); else if (fastcmp(field,"tiregrease")) lua_pushinteger(L, plr->tiregrease); else if (fastcmp(field,"springstars")) @@ -634,8 +632,6 @@ static int player_set(lua_State *L) plr->aizdriftturn = luaL_checkinteger(L, 3); else if (fastcmp(field,"offroad")) plr->offroad = luaL_checkinteger(L, 3); - else if (fastcmp(field,"waterskip")) - plr->waterskip = luaL_checkinteger(L, 3); else if (fastcmp(field,"tiregrease")) plr->tiregrease = luaL_checkinteger(L, 3); else if (fastcmp(field,"springstars")) diff --git a/src/p_local.h b/src/p_local.h index 5fc73f1a2..37edb2b67 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -315,7 +315,7 @@ fixed_t P_CameraCeilingZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, f boolean P_InsideANonSolidFFloor(mobj_t *mobj, ffloor_t *rover); boolean P_CheckDeathPitCollide(mobj_t *mo); -boolean P_CheckSolidLava(ffloor_t *rover); +boolean P_CheckSolidLava(mobj_t *mobj, ffloor_t *rover); void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype); mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zofs, mobjtype_t type); @@ -337,8 +337,8 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled void P_Attract(mobj_t *source, mobj_t *enemy, boolean nightsgrab); mobj_t *P_GetClosestAxis(mobj_t *source); -boolean P_CanRunOnWater(player_t *player, ffloor_t *rover); -boolean P_CheckSolidFFloorSurface(player_t *player, ffloor_t *rover); +boolean P_CanRunOnWater(mobj_t *mobj, ffloor_t *rover); +boolean P_CheckSolidFFloorSurface(mobj_t *mobj, ffloor_t *rover); void P_MaceRotate(mobj_t *center, INT32 baserot, INT32 baseprevrot); diff --git a/src/p_map.c b/src/p_map.c index 62baa23e6..70ea44edd 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -1940,7 +1940,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) continue; } - if (thing->player && P_CheckSolidFFloorSurface(thing->player, rover)) + if (P_CheckSolidFFloorSurface(thing, rover)) ; else if (thing->type == MT_SKIM && (rover->flags & FF_SWIMMABLE)) ; @@ -2524,9 +2524,7 @@ static boolean P_WaterRunning(mobj_t *thing) static boolean P_WaterStepUp(mobj_t *thing) { - player_t *player = thing->player; - return (player && player->waterskip) || - P_WaterRunning(thing); + return (thing->waterskip > 0 || P_WaterRunning(thing)); } fixed_t P_BaseStepUp(void) diff --git a/src/p_maputl.c b/src/p_maputl.c index 09e5cdd5d..74fe2331d 100644 --- a/src/p_maputl.c +++ b/src/p_maputl.c @@ -777,7 +777,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) if (!(rover->flags & FF_EXISTS)) continue; - if (mobj->player && P_CheckSolidFFloorSurface(mobj->player, rover)) + if (P_CheckSolidFFloorSurface(mobj, rover)) ; else if (!((rover->flags & FF_BLOCKPLAYER && mobj->player) || (rover->flags & FF_BLOCKOTHERS && !mobj->player))) @@ -821,7 +821,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) if (!(rover->flags & FF_EXISTS)) continue; - if (mobj->player && P_CheckSolidFFloorSurface(mobj->player, rover)) + if (P_CheckSolidFFloorSurface(mobj, rover)) ; else if (!((rover->flags & FF_BLOCKPLAYER && mobj->player) || (rover->flags & FF_BLOCKOTHERS && !mobj->player))) diff --git a/src/p_mobj.c b/src/p_mobj.c index 9b08f0e75..e6894f349 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -1105,6 +1105,11 @@ fixed_t P_GetMobjGravity(mobj_t *mo) } } + if (mo->waterskip > 0) + { + gravityadd = (4*gravityadd)/3; + } + if (mo->player) { if (mo->flags2 & MF2_OBJECTFLIP) @@ -1118,11 +1123,6 @@ fixed_t P_GetMobjGravity(mobj_t *mo) P_PlayerFlip(mo); } - if (mo->player->waterskip) - { - gravityadd = (4*gravityadd)/3; - } - if (mo->player->trickpanel >= 2) { gravityadd = (5*gravityadd)/2; @@ -1938,7 +1938,7 @@ void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype) topheight = P_GetFOFTopZ(mo, sector, rover, mo->x, mo->y, NULL); bottomheight = P_GetFOFBottomZ(mo, sector, rover, mo->x, mo->y, NULL); - if (mo->player && P_CheckSolidFFloorSurface(mo->player, rover)) // only the player should stand on lava or run on water + if (P_CheckSolidFFloorSurface(mo, rover)) // only the player should stand on lava or run on water ; else if (motype != 0 && rover->flags & FF_SWIMMABLE) // "scenery" only continue; @@ -2097,11 +2097,18 @@ boolean P_CheckDeathPitCollide(mobj_t *mo) return false; } -boolean P_CheckSolidLava(ffloor_t *rover) +boolean P_CheckSolidLava(mobj_t *mobj, ffloor_t *rover) { + if (mobj->player == NULL) + { + return false; + } + if (rover->flags & FF_SWIMMABLE && GETSECSPECIAL(rover->master->frontsector->special, 1) == 3 && !(rover->master->flags & ML_BLOCKPLAYERS)) - return true; + { + return true; + } return false; } @@ -3079,31 +3086,115 @@ boolean P_SceneryZMovement(mobj_t *mo) return true; } +// // P_CanRunOnWater // -// Returns true if player can waterrun on the 3D floor +// Returns true if player can water run on a 3D floor // -boolean P_CanRunOnWater(player_t *player, ffloor_t *rover) +boolean P_CanRunOnWater(mobj_t *mobj, ffloor_t *rover) { - boolean flip = player->mo->eflags & MFE_VERTICALFLIP; - fixed_t surfaceheight = flip ? player->mo->waterbottom : player->mo->watertop; - fixed_t playerbottom = flip ? (player->mo->z + player->mo->height) : player->mo->z; - fixed_t clip = flip ? (surfaceheight - playerbottom) : (playerbottom - surfaceheight); - fixed_t span = player->mo->watertop - player->mo->waterbottom; + const boolean flip = (mobj->eflags & MFE_VERTICALFLIP); + player_t *player = mobj->player; - return - clip > -(player->mo->height / 2) && - span > player->mo->height && - player->speed / 5 > abs(player->mo->momz) && - player->speed > K_GetKartSpeed(player, false, false) && - K_WaterRun(player) && - (rover->flags & FF_SWIMMABLE); + fixed_t surfaceheight = INT32_MAX; + fixed_t surfDiff = INT32_MAX; + + fixed_t floorheight = INT32_MAX; + fixed_t floorDiff = INT32_MAX; + + fixed_t mobjbottom = INT32_MAX; + fixed_t maxStep = INT32_MAX; + boolean doifit = false; + + pslope_t *waterSlope = NULL; + angle_t ourZAng = 0; + angle_t waterZAng = 0; + + if (rover == NULL) + { + // No rover. + return false; + } + + if (!(rover->flags & FF_SWIMMABLE)) + { + // It's not even a water FOF. + return false; + } + + if (player != NULL + && player->carry != CR_NONE) // Special carry state. + { + // No good player state. + return false; + } + + if (P_IsObjectOnGround(mobj) == false) + { + // Don't allow jumping onto water to start a water run. + // (Already water running still counts as being on the ground.) + return false; + } + + if (K_WaterRun(mobj) == false) + { + // Basic conditions for enabling water run. + return false; + } + + if (mobj->standingslope != NULL) + { + ourZAng = mobj->standingslope->zangle; + } + + waterSlope = (flip ? *rover->b_slope : *rover->t_slope); + if (waterSlope != NULL) + { + waterZAng = waterSlope->zangle; + } + + if (ourZAng != waterZAng) + { + // The surface slopes are different. + return false; + } + + surfaceheight = flip ? P_GetFFloorBottomZAt(rover, mobj->x, mobj->y) : P_GetFFloorTopZAt(rover, mobj->x, mobj->y); + mobjbottom = flip ? (mobj->z + mobj->height) : mobj->z; + + doifit = flip ? (surfaceheight - mobj->floorz >= mobj->height) : (mobj->ceilingz - surfaceheight >= mobj->height); + + if (!doifit) + { + // Object can't fit in this space. + return false; + } + + maxStep = P_GetThingStepUp(mobj); + + surfDiff = flip ? (surfaceheight - mobjbottom) : (mobjbottom - surfaceheight); + if (surfDiff <= maxStep && surfDiff >= 0) + { + // We start water run IF we can step-down! + floorheight = flip ? P_GetSectorCeilingZAt(mobj->subsector->sector, mobj->x, mobj->y) : P_GetSectorFloorZAt(mobj->subsector->sector, mobj->x, mobj->y); + floorDiff = flip ? (floorheight - mobjbottom) : (mobjbottom - floorheight); + if (floorDiff <= maxStep && floorDiff >= 0) + { + // ... but NOT if real floor is in range. + // FIXME: Count solid FOFs in this check + return false; + } + + return true; + } + + return false; } -boolean P_CheckSolidFFloorSurface(player_t *player, ffloor_t *rover) +boolean P_CheckSolidFFloorSurface(mobj_t *mobj, ffloor_t *rover) { - return P_CheckSolidLava(rover) || - P_CanRunOnWater(player, rover); + return P_CheckSolidLava(mobj, rover) || + P_CanRunOnWater(mobj, rover); } // @@ -3125,6 +3216,8 @@ void P_MobjCheckWater(mobj_t *mobj) boolean wasgroundpounding = false; fixed_t top2 = P_GetSectorCeilingZAt(sector, mobj->x, mobj->y); fixed_t bot2 = P_GetSectorFloorZAt(sector, mobj->x, mobj->y); + pslope_t *topslope = NULL; + pslope_t *bottomslope = NULL; // Default if no water exists. mobj->watertop = mobj->waterbottom = mobj->z - 1000*FRACUNIT; @@ -3167,6 +3260,9 @@ void P_MobjCheckWater(mobj_t *mobj) mobj->watertop = topheight; mobj->waterbottom = bottomheight; + topslope = *rover->t_slope; + bottomslope = *rover->b_slope; + // Just touching the water? if (((mobj->eflags & MFE_VERTICALFLIP) && thingtop - height < bottomheight) || (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z + height > topheight)) @@ -3204,13 +3300,28 @@ void P_MobjCheckWater(mobj_t *mobj) mobj->watertop = mobj->z; mobj->waterbottom = mobj->z - height; } + + topslope = bottomslope = NULL; } } - // Spectators and dead players don't get to do any of the things after this. - if (p && (p->spectator || p->playerstate != PST_LIVE)) + if (P_IsObjectOnGround(mobj) == true) { - return; + mobj->waterskip = 0; + } + + if (p != NULL) + { + // Spectators and dead players don't get to do any of the things after this. + if (p->spectator || p->playerstate != PST_LIVE) + { + return; + } + } + + if (mobj->flags & MF_APPLYTERRAIN) + { + K_SpawnWaterRunParticles(mobj); } // The rest of this code only executes on a water state change. @@ -3219,20 +3330,21 @@ void P_MobjCheckWater(mobj_t *mobj) return; } - if (p && !p->waterskip && - p->curshield != KSHIELD_BUBBLE && wasinwater) + if (p != NULL + && p->curshield != KSHIELD_BUBBLE + && mobj->waterskip == 0 + && wasinwater) { + // Play the gasp sound S_StartSound(mobj, sfx_s3k38); } - if ((p) // Players - || (mobj->flags & MF_PUSHABLE) // Pushables - || ((mobj->info->flags & MF_PUSHABLE) && mobj->fuse) // Previously pushable, might be moving still - ) + if (mobj->flags & MF_APPLYTERRAIN) { fixed_t waterZ = INT32_MAX; fixed_t solidZ = INT32_MAX; fixed_t diff = INT32_MAX; + INT32 waterDelta = 0; fixed_t thingZ = INT32_MAX; boolean splashValid = false; @@ -3241,11 +3353,19 @@ void P_MobjCheckWater(mobj_t *mobj) { waterZ = mobj->waterbottom; solidZ = mobj->ceilingz; + if (bottomslope) + { + waterDelta = bottomslope->zdelta; + } } else { waterZ = mobj->watertop; solidZ = mobj->floorz; + if (topslope) + { + waterDelta = topslope->zdelta; + } } diff = waterZ - solidZ; @@ -3314,25 +3434,22 @@ void P_MobjCheckWater(mobj_t *mobj) splish->destscale = mobj->scale; P_SetScale(splish, mobj->scale); + + // skipping stone! + if (K_WaterSkip(mobj) == true + && abs(waterDelta) < FRACUNIT/21) // Only on flat water + { + const fixed_t hop = 5 * mapobjectscale; + + mobj->momx = (4*mobj->momx)/5; + mobj->momy = (4*mobj->momy)/5; + mobj->momz = hop * P_MobjFlip(mobj); + + mobj->waterskip++; + } } - - // skipping stone! - 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! - && (splashValid == true)) - { - const fixed_t hop = 5 * mobj->scale; - - mobj->momx = (4*mobj->momx)/5; - mobj->momy = (4*mobj->momy)/5; - mobj->momz = hop * P_MobjFlip(mobj); - - p->waterskip++; - } - } - else if (P_MobjFlip(mobj) * mobj->momz > 0) + else { if (splashValid == true && !(mobj->eflags & MFE_UNDERWATER)) // underwater check to prevent splashes on opposite side { @@ -6717,11 +6834,13 @@ static boolean P_MobjRegularThink(mobj_t *mobj) case MT_ORBINAUT: { Obj_OrbinautThink(mobj); + P_MobjCheckWater(mobj); break; } case MT_JAWZ: { Obj_JawzThink(mobj); + P_MobjCheckWater(mobj); break; } case MT_EGGMANITEM: @@ -6785,6 +6904,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj) if (mobj->threshold > 0) mobj->threshold--; + + P_MobjCheckWater(mobj); } break; case MT_SINK: @@ -9872,6 +9993,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) mobj->colorized = false; mobj->hitlag = 0; + mobj->waterskip = 0; // Set shadowscale here, before spawn hook so that Lua can change it P_DefaultMobjShadowScale(mobj); diff --git a/src/p_mobj.h b/src/p_mobj.h index 34329107b..1755ea81d 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -409,6 +409,7 @@ typedef struct mobj_s struct mobj_s *terrainOverlay; // Overlay sprite object for terrain INT32 hitlag; // Sal-style hit lag, straight from Captain Fetch's jowls + UINT8 waterskip; // Water skipping counter INT32 dispoffset; diff --git a/src/p_saveg.c b/src/p_saveg.c index 233056f21..dd77337b9 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -280,7 +280,6 @@ static void P_NetArchivePlayers(void) WRITEINT32(save_p, players[i].underwatertilt); WRITEFIXED(save_p, players[i].offroad); - WRITEUINT8(save_p, players[i].waterskip); WRITEUINT16(save_p, players[i].tiregrease); WRITEUINT16(save_p, players[i].springstars); @@ -577,7 +576,6 @@ static void P_NetUnArchivePlayers(void) players[i].underwatertilt = READINT32(save_p); players[i].offroad = READFIXED(save_p); - players[i].waterskip = READUINT8(save_p); players[i].tiregrease = READUINT16(save_p); players[i].springstars = READUINT16(save_p); @@ -1638,6 +1636,7 @@ typedef enum MD2_ITNEXT = 1<<27, MD2_LASTMOMZ = 1<<28, MD2_TERRAIN = 1<<29, + MD2_WATERSKIP = 1<<30, } mobj_diff2_t; typedef enum @@ -1872,6 +1871,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) } if (mobj->hitlag) diff2 |= MD2_HITLAG; + if (mobj->waterskip) + diff2 |= MD2_WATERSKIP; if (mobj->dispoffset) diff2 |= MD2_DISPOFFSET; if (mobj == waypointcap) @@ -2082,6 +2083,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) { WRITEINT32(save_p, mobj->hitlag); } + if (diff2 & MD2_WATERSKIP) + { + WRITEUINT8(save_p, mobj->waterskip); + } if (diff2 & MD2_DISPOFFSET) { WRITEINT32(save_p, mobj->dispoffset); @@ -3191,6 +3196,10 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) { mobj->hitlag = READINT32(save_p); } + if (diff2 & MD2_WATERSKIP) + { + mobj->waterskip = READUINT8(save_p); + } if (diff2 & MD2_DISPOFFSET) { mobj->dispoffset = READINT32(save_p); diff --git a/src/p_user.c b/src/p_user.c index 5261b84cb..8334db3dd 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -2292,100 +2292,6 @@ void P_MovePlayer(player_t *player) //GAMEPLAY STUFF// ////////////////// - if (((!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z + player->mo->height >= player->mo->watertop && player->mo->z <= player->mo->watertop) - || (player->mo->eflags & MFE_VERTICALFLIP && player->mo->z + player->mo->height >= player->mo->waterbottom && player->mo->z <= player->mo->waterbottom)) - && (player->speed > runspd) - && player->mo->momz == 0 && player->carry != CR_SLIDING && !player->spectator) - { - fixed_t playerTopSpeed = K_GetKartSpeed(player, false, false); - fixed_t trailScale = FixedMul(FixedDiv(player->speed - runspd, playerTopSpeed - runspd), mapobjectscale); - - if (playerTopSpeed > runspd) - trailScale = FixedMul(FixedDiv(player->speed - runspd, playerTopSpeed - runspd), mapobjectscale); - else - trailScale = mapobjectscale; // Scaling is based off difference between runspeed and top speed - - if (trailScale > 0) - { - const angle_t forwardangle = K_MomentumAngle(player->mo); - const fixed_t playerVisualRadius = player->mo->radius + (8 * player->mo->scale); - const size_t numFrames = S_WATERTRAIL8 - S_WATERTRAIL1; - const statenum_t curOverlayFrame = S_WATERTRAIL1 + (leveltime % numFrames); - const statenum_t curUnderlayFrame = S_WATERTRAILUNDERLAY1 + (leveltime % numFrames); - fixed_t x1, x2, y1, y2; - mobj_t *water; - - x1 = player->mo->x + player->mo->momx + P_ReturnThrustX(player->mo, forwardangle + ANGLE_90, playerVisualRadius); - y1 = player->mo->y + player->mo->momy + P_ReturnThrustY(player->mo, forwardangle + ANGLE_90, playerVisualRadius); - x1 = x1 + P_ReturnThrustX(player->mo, forwardangle, playerVisualRadius); - y1 = y1 + P_ReturnThrustY(player->mo, forwardangle, playerVisualRadius); - - x2 = player->mo->x + player->mo->momx + P_ReturnThrustX(player->mo, forwardangle - ANGLE_90, playerVisualRadius); - y2 = player->mo->y + player->mo->momy + P_ReturnThrustY(player->mo, forwardangle - ANGLE_90, playerVisualRadius); - x2 = x2 + P_ReturnThrustX(player->mo, forwardangle, playerVisualRadius); - y2 = y2 + P_ReturnThrustY(player->mo, forwardangle, playerVisualRadius); - - // Left - // underlay - water = P_SpawnMobj(x1, y1, - ((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAILUNDERLAY].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAILUNDERLAY); - P_InitAngle(water, forwardangle - ANGLE_180 - ANGLE_22h); - water->destscale = trailScale; - water->momx = player->mo->momx; - water->momy = player->mo->momy; - water->momz = player->mo->momz; - P_SetScale(water, trailScale); - P_SetMobjState(water, curUnderlayFrame); - - // overlay - water = P_SpawnMobj(x1, y1, - ((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAIL].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAIL); - P_InitAngle(water, forwardangle - ANGLE_180 - ANGLE_22h); - water->destscale = trailScale; - water->momx = player->mo->momx; - water->momy = player->mo->momy; - water->momz = player->mo->momz; - P_SetScale(water, trailScale); - P_SetMobjState(water, curOverlayFrame); - - // Right - // Underlay - water = P_SpawnMobj(x2, y2, - ((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAILUNDERLAY].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAILUNDERLAY); - P_InitAngle(water, forwardangle - ANGLE_180 + ANGLE_22h); - water->destscale = trailScale; - water->momx = player->mo->momx; - water->momy = player->mo->momy; - water->momz = player->mo->momz; - P_SetScale(water, trailScale); - P_SetMobjState(water, curUnderlayFrame); - - // Overlay - water = P_SpawnMobj(x2, y2, - ((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAIL].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAIL); - P_InitAngle(water, forwardangle - ANGLE_180 + ANGLE_22h); - water->destscale = trailScale; - water->momx = player->mo->momx; - water->momy = player->mo->momy; - water->momz = player->mo->momz; - P_SetScale(water, trailScale); - P_SetMobjState(water, curOverlayFrame); - - if (!S_SoundPlaying(player->mo, sfx_s3kdbs)) - { - const INT32 volume = (min(trailScale, FRACUNIT) * 255) / FRACUNIT; - S_StartSoundAtVolume(player->mo, sfx_s3kdbs, volume); - } - } - } - - // Little water sound while touching water - just a nicety. - if ((player->mo->eflags & MFE_TOUCHWATER) && !(player->mo->eflags & MFE_UNDERWATER) && !player->spectator) - { - if (P_RandomChance(PR_BUBBLE, FRACUNIT/2) && leveltime % TICRATE == 0) - S_StartSound(player->mo, sfx_floush); - } - //////////////////////////// //SPINNING AND SPINDASHING// ////////////////////////////