Allow Mobjs to waterrun

Waterrunning has been moved over to flag2 MF2_WATERRUN
Applying the flag allows to stand/move on water.
This commit is contained in:
NepDisk 2025-03-17 14:13:15 -04:00
parent b84257f2ab
commit 76ec93f957
12 changed files with 210 additions and 132 deletions

View file

@ -649,6 +649,7 @@ enum
MF2_LINKDRAW = 1<<28,
MF2_SHIELD = 1<<29,
MF2_SPLAT = 1<<30,
MF2_WATERRUN = 1<<31,
};
enum

View file

@ -623,7 +623,6 @@ struct player_t
UINT16 sneakertimer; // Duration of a Sneaker Boost (from Sneakers or level boosters)
UINT8 floorboost; // (0 to 3) - Prevents Sneaker sounds for a brief duration when triggered by a floor panel
boolean waterrun; // Tracks condition of water run
UINT8 boostcharge; // Charge during race start

View file

@ -194,6 +194,7 @@ const char *const MOBJFLAG2_LIST[] = {
"AMBUSH", // Alternate behaviour typically set by MTF_AMBUSH
"LINKDRAW", // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
"SHIELD", // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
"WATERRUN", // Mobj is able to run on Water!
NULL
};

View file

@ -417,12 +417,12 @@ static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS][2] =
INT32 K_GetShieldFromPlayer(player_t *player)
{
if (player->flametimer > 0)
{
return KSHIELD_FLAME;
}
switch (player->itemtype)
{
case KITEM_THUNDERSHIELD: return KSHIELD_THUNDER;
@ -3118,13 +3118,147 @@ boolean K_TripwirePass(player_t *player)
return (player->tripwirePass != TRIPWIRE_NONE);
}
boolean K_WaterRun(player_t *player)
boolean K_ItemMobjAllowedtoWaterRun(mobj_t *item)
{
if (player->waterrun)
return true;
switch (item->type)
{
case MT_ORBINAUT:
case MT_JAWZ:
case MT_JAWZ_DUD:
case MT_DROPTARGET:
case MT_BANANA_SHIELD:
case MT_DROPTARGET_SHIELD:
case MT_SSMINE_SHIELD:
return true;
break;
default:
return false;
break;
}
return false;
}
boolean K_WaterRun(mobj_t *mobj)
{
// Let dragged items waterrun with the player for free.
switch (mobj->type)
{
case MT_BANANA_SHIELD:
case MT_DROPTARGET_SHIELD:
case MT_SSMINE_SHIELD:
if (mobj->target && (mobj->target->flags2 & MF2_WATERRUN))
return true;
break;
default:
break;
}
if (mobj->flags2 & MF2_WATERRUN)
{
return true;
}
return false;
}
void K_SpawnWaterTrail(mobj_t *mobj)
{
fixed_t topspeed = mobj->player ? K_GetKartSpeed(mobj->player, false, false) : K_GetKartSpeedFromStat(5);
fixed_t runspd = 14*mobj->scale; //srb2kart
runspd = FixedMul(runspd, mobj->movefactor);
fixed_t trailScale;
if (topspeed > runspd)
{
if (mobj->player)
{
trailScale = FixedMul(FixedDiv(mobj->player->speed - runspd, topspeed - runspd), mapobjectscale);
}
else
{
trailScale = FixedMul(FixedDiv(R_PointToDist2(0, 0, mobj->momx, mobj->momy) - runspd, topspeed - runspd), 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 visualradius = mobj->radius + (8 * mobj->scale);
const SINT8 numFrames = 5;
const INT32 curFrame = (leveltime % numFrames)|FF_PAPERSPRITE;
fixed_t x1, x2, y1, y2;
mobj_t *water;
x1 = mobj->x + mobj->momx + P_ReturnThrustX(mobj, forwardangle + ANGLE_90, visualradius);
y1 = mobj->y + mobj->momy + P_ReturnThrustY(mobj, forwardangle + ANGLE_90, visualradius);
x1 = x1 + P_ReturnThrustX(mobj, forwardangle, visualradius);
y1 = y1 + P_ReturnThrustY(mobj, forwardangle, visualradius);
x2 = mobj->x + mobj->momx + P_ReturnThrustX(mobj, forwardangle - ANGLE_90, visualradius);
y2 = mobj->y + mobj->momy + P_ReturnThrustY(mobj, forwardangle - ANGLE_90, visualradius);
x2 = x2 + P_ReturnThrustX(mobj, forwardangle, visualradius);
y2 = y2 + P_ReturnThrustY(mobj, forwardangle, visualradius);
// Left
// underlay
water = P_SpawnMobj(x1, y1,
((mobj->eflags & MFE_VERTICALFLIP) ? mobj->waterbottom - FixedMul(mobjinfo[MT_WATERTRAILUNDERLAY].height, mobj->scale) : mobj->watertop), MT_WATERTRAILUNDERLAY);
water->angle = 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, S_WATERTRAILSUNDERLAY);
water->frame = curFrame|FF_ADD|FF_TRANS40;
// overlay
water = P_SpawnMobj(x1, y1,
((mobj->eflags & MFE_VERTICALFLIP) ? mobj->waterbottom - FixedMul(mobjinfo[MT_WATERTRAIL].height, mobj->scale) : mobj->watertop), MT_WATERTRAIL);
water->angle = 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, S_WATERTRAILS);
water->frame = curFrame;
// Right
// Underlay
water = P_SpawnMobj(x2, y2,
((mobj->eflags & MFE_VERTICALFLIP) ? mobj->waterbottom - FixedMul(mobjinfo[MT_WATERTRAILUNDERLAY].height, mobj->scale) : mobj->watertop), MT_WATERTRAILUNDERLAY);
water->angle = 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, S_WATERTRAILSUNDERLAY);
water->frame = curFrame|FF_ADD|FF_TRANS40;
// Overlay
water = P_SpawnMobj(x2, y2,
((mobj->eflags & MFE_VERTICALFLIP) ? mobj->waterbottom - FixedMul(mobjinfo[MT_WATERTRAIL].height, mobj->scale) : mobj->watertop), MT_WATERTRAIL);
water->angle = 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, S_WATERTRAILS);
water->frame = curFrame;
if (!S_SoundPlaying(mobj, sfx_s3kdbs))
{
const INT32 volume = (min(trailScale, FRACUNIT) * 255) / FRACUNIT;
S_StartSoundAtVolume(mobj, sfx_s3kdbs, volume);
}
}
}
static fixed_t K_FlameShieldDashVar(INT32 val)
{
// 1 second = 15% + ????% top speed
@ -3214,7 +3348,7 @@ static void K_GetKartBoostPower(player_t *player)
{
ADDBOOST(FRACUNIT/5, 0); // + 20% top speed, + 0% acceleration
}
if (player->flamestore) // Flame Shield dash
{
fixed_t dash = K_FlameShieldDashVar(player->flamedash);
@ -3222,10 +3356,10 @@ static void K_GetKartBoostPower(player_t *player)
fixed_t boost = 0;
fixed_t val = 52428; // Rim idea: diminish starts around 1.2x sneaker speed and plateaus around 1.4-1.5x
fixed_t accel = 3*FRACUNIT;
intermediate = FixedDiv(FixedMul(val, FRACUNIT*-1/2) - FRACUNIT/4,-val+FRACUNIT/2);
boost = FixedMul(val,(FRACUNIT-FixedDiv(FRACUNIT,(dash+intermediate))));
ADDBOOST(boost, accel);
}
@ -4015,6 +4149,15 @@ static mobj_t *K_SpawnKartMissile(mobj_t *source, mobjtype_t type, angle_t an, I
th->flags2 |= flags2;
if (source->flags2 & MF2_WATERRUN)
{
// Allow certain items to run on water as well!
if (K_ItemMobjAllowedtoWaterRun(th))
{
th->flags2 |= MF2_WATERRUN;
}
}
th->threshold = 10;
if (th->info->seesound)
@ -5064,7 +5207,7 @@ void K_DoWaterRunPanel(player_t *player)
}
player->sneakertimer = TICRATE*2;
player->waterrun = true;
player->mo->flags2 |= MF2_WATERRUN;
// set angle for spun out players:
player->boostangle = player->mo->angle;
@ -6993,7 +7136,10 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
player->floorboost--;
if (player->sneakertimer == 0)
player->waterrun = false;
{
// No more waterrunning for you pal!
player->mo->flags2 &= ~MF2_WATERRUN;
}
if (player->driftboost)
player->driftboost--;
@ -8375,7 +8521,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue)
if (player->invincibilitytimer || player->sneakertimer || player->growshrinktimer > 0)
turnvalue = FixedMul(turnvalue, FixedDiv(5*FRACUNIT, 4*FRACUNIT));
if (player->flamedash && player->flamestore) // Reduce turning
{
fixed_t dashval = ((player->flamedash<<FRACBITS) / TICRATE) / 40; // 1 second = -2.5% handling
@ -9722,7 +9868,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
}
break;
case KITEM_FLAMESHIELD:
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO && player->flametimer == 0)
{
player->itemamount--;

View file

@ -152,7 +152,9 @@ boolean K_SlopeResistance(player_t *player);
fixed_t K_PlayerTripwireSpeedThreshold(player_t *player);
tripwirepass_t K_TripwirePassConditions(player_t *player);
boolean K_TripwirePass(player_t *player);
boolean K_WaterRun(player_t *player);
boolean K_WaterRun(mobj_t *mobj);
void K_SpawnWaterTrail(mobj_t *mobj);
boolean K_ItemMobjAllowedtoWaterRun(mobj_t *item);
void K_SetTireGrease(player_t *player, tic_t tics);
void K_SquishPlayer(player_t *player, mobj_t *inflictor, mobj_t *source);
void K_ApplyTripWire(player_t *player, tripwirestate_t state);

View file

@ -360,8 +360,6 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->sneakertimer);
else if (fastcmp(field,"floorboost"))
lua_pushinteger(L, plr->floorboost);
else if (fastcmp(field,"waterrun"))
lua_pushinteger(L, plr->waterrun);
else if (fastcmp(field,"growshrinktimer"))
lua_pushinteger(L, plr->growshrinktimer);
else if (fastcmp(field,"growcancel"))
@ -777,8 +775,6 @@ static int player_set(lua_State *L)
plr->sneakertimer = luaL_checkinteger(L, 3);
else if (fastcmp(field,"floorboost"))
plr->floorboost = luaL_checkinteger(L, 3);
else if (fastcmp(field,"waterrun"))
plr->waterrun = luaL_checkboolean(L, 3);
else if (fastcmp(field,"growshrinktimer"))
plr->growshrinktimer = luaL_checkinteger(L, 3);
else if (fastcmp(field,"growcancel"))

View file

@ -362,7 +362,7 @@ 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_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);

View file

@ -2467,17 +2467,8 @@ BlockItReturn_t PIT_PushableMoved(mobj_t *thing)
static boolean P_WaterRunning(mobj_t *thing)
{
ffloor_t *rover = thing->floorrover;
if (thing->player)
{
return rover && (rover->fofflags & FOF_SWIMMABLE) &&
P_IsObjectOnGround(thing) && thing->player->waterrun;
}
else
{
// Maybe move waterrun bool to mobj_t someday..?
return false;
}
return rover && (rover->fofflags & FOF_SWIMMABLE) &&
P_IsObjectOnGround(thing) && (thing->flags2 & MF2_WATERRUN);
}
static boolean P_WaterStepUp(mobj_t *thing)

View file

@ -2997,11 +2997,11 @@ boolean P_SceneryZMovement(mobj_t *mo)
//
// P_CanRunOnWater
//
// Returns true if player can water run on a 3D floor
// Returns true if mobj 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)
{
const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP);
const boolean flip = (mobj->eflags & MFE_VERTICALFLIP);
fixed_t surfaceheight = INT32_MAX;
fixed_t playerbottom = INT32_MAX;
fixed_t surfDiff = INT32_MAX;
@ -3024,28 +3024,29 @@ boolean P_CanRunOnWater(player_t *player, ffloor_t *rover)
return false;
}
if (player->carry != CR_NONE) // Special carry state.
if (mobj->player && mobj->player->carry != CR_NONE) // Special carry state.
{
// No good player state.
return false;
}
if (P_IsObjectOnGround(player->mo) == false)
if (P_IsObjectOnGround(mobj) == false && !K_ItemMobjAllowedtoWaterRun(mobj))
{
// Don't allow jumping onto water to start a water run.
// (Unless you are some specfic item mobjs!)
// (Already water running still counts as being on the ground.)
return false;
}
if (K_WaterRun(player) == false)
if (K_WaterRun(mobj) == false)
{
// Basic conditions for enabling water run.
return false;
}
if (player->mo->standingslope != NULL)
if (mobj->standingslope != NULL)
{
ourZAng = player->mo->standingslope->zangle;
ourZAng = mobj->standingslope->zangle;
}
waterSlope = (flip ? *rover->b_slope : *rover->t_slope);
@ -3060,10 +3061,10 @@ boolean P_CanRunOnWater(player_t *player, ffloor_t *rover)
return false;
}
surfaceheight = flip ? P_GetFFloorBottomZAt(rover, player->mo->x, player->mo->y) : P_GetFFloorTopZAt(rover, player->mo->x, player->mo->y);
playerbottom = flip ? (player->mo->z + player->mo->height) : player->mo->z;
surfaceheight = flip ? P_GetFFloorBottomZAt(rover, mobj->x, mobj->y) : P_GetFFloorTopZAt(rover, mobj->x, mobj->y);
playerbottom = flip ? (mobj->z + mobj->height) : mobj->z;
doifit = flip ? (surfaceheight - player->mo->floorz >= player->mo->height) : (player->mo->ceilingz - surfaceheight >= player->mo->height);
doifit = flip ? (surfaceheight - mobj->floorz >= mobj->height) : (mobj->ceilingz - surfaceheight >= mobj->height);
if (!doifit)
{
@ -3071,7 +3072,7 @@ boolean P_CanRunOnWater(player_t *player, ffloor_t *rover)
return false;
}
maxStep = P_GetThingStepUp(player->mo, player->mo->x, player->mo->y);
maxStep = P_GetThingStepUp(mobj, mobj->x, mobj->y);
surfDiff = flip ? (surfaceheight - playerbottom) : (playerbottom - surfaceheight);
if (surfDiff <= maxStep && surfDiff >= 0)
@ -3087,13 +3088,12 @@ boolean P_CheckSolidFFloorSurface(mobj_t *mobj, ffloor_t *rover)
{
if (!mobj->player)
{
// future proff if we ever decide to allow mobjs
// to interact with P_CheckSolidFFloorSurface
return false;
// They can run on water now!
return P_CanRunOnWater(mobj, rover);
}
return P_CheckSolidLava(rover) ||
P_CanRunOnWater(mobj->player, rover);
P_CanRunOnWater(mobj, rover);
}
//
@ -6730,6 +6730,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
mobj->movecount = 2;
mobj->frame++;
}
mobj->flags2 &= ~MF2_WATERRUN;
}
else
{
@ -6768,6 +6769,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
P_Thrust(mobj, mobj->angle, thrustamount);
if (R_PointToDist2(0, 0, mobj->momx, mobj->momy) < 8*FRACUNIT)
{
// Not moving fast enough to water run.
mobj->flags2 &= ~MF2_WATERRUN;
}
sec2 = P_ThingOnSpecial3DFloor(mobj);
if ((mobj->terrain && mobj->terrain->pogoSpring > 0) || (sec2 && (sec2->specialflags & (SSF_YELLOWPOGOSPRING|SSF_REDPOGOSPRING)))
|| (P_IsObjectOnRealGround(mobj, mobj->subsector->sector)
@ -6834,6 +6841,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
K_DriftDustHandling(mobj);
if (R_PointToDist2(0, 0, mobj->momx, mobj->momy) < 8*FRACUNIT)
{
// Not moving fast enough to water run.
mobj->flags2 &= ~MF2_WATERRUN;
}
sec2 = P_ThingOnSpecial3DFloor(mobj);
if ((mobj->terrain && mobj->terrain->pogoSpring > 0) || (sec2 && (sec2->specialflags & (SSF_YELLOWPOGOSPRING|SSF_REDPOGOSPRING)))
|| (P_IsObjectOnRealGround(mobj, mobj->subsector->sector)
@ -6854,6 +6867,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
S_StartSound(mobj, mobj->info->deathsound);
mobj->flags &= ~MF_NOCLIPTHING;
}
mobj->flags2 &= ~MF2_WATERRUN;
}
else
{
@ -6870,6 +6884,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
K_DoPogoSpring(mobj, 0, 1);
}
if (R_PointToDist2(0, 0, mobj->momx, mobj->momy) < 8*FRACUNIT)
{
// Not moving fast enough to water run.
mobj->flags2 &= ~MF2_WATERRUN;
}
if (mobj->threshold > 0)
mobj->threshold--;
@ -7031,6 +7051,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
mobj->renderflags = (mobj->renderflags|RF_FULLBRIGHT) ^ RF_FULLDARK; // the difference between semi and fullbright
if (R_PointToDist2(0, 0, mobj->momx, mobj->momy) < 8*FRACUNIT)
{
// Not moving fast enough to water run.
mobj->flags2 &= ~MF2_WATERRUN;
}
if (mobj->threshold > 0)
mobj->threshold--;
break;

View file

@ -203,7 +203,7 @@ typedef enum
MF2_LINKDRAW = 1<<28, // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
MF2_SHIELD = 1<<29, // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
MF2_SPLAT = 1<<30, // Renders as a splat
// free: to and including 1<<31
MF2_WATERRUN = 1<<31, // Mobj is able to run on Water!
} mobjflag2_t;
typedef enum

View file

@ -315,7 +315,6 @@ static void P_NetArchivePlayers(savebuffer_t *save)
WRITEUINT16(save->p, players[i].sneakertimer);
WRITEUINT8(save->p, players[i].floorboost);
WRITEUINT8(save->p, players[i].waterrun);
WRITEUINT8(save->p, players[i].boostcharge);
@ -625,7 +624,6 @@ static void P_NetUnArchivePlayers(savebuffer_t *save)
players[i].sneakertimer = READUINT16(save->p);
players[i].floorboost = READUINT8(save->p);
players[i].waterrun = READUINT8(save->p);
players[i].boostcharge = READUINT8(save->p);

View file

@ -2238,89 +2238,7 @@ void P_MovePlayer(player_t *player)
&& (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 SINT8 numFrames = 5;
const INT32 curFrame = (leveltime % numFrames)|FF_PAPERSPRITE;
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);
water->angle = 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, S_WATERTRAILSUNDERLAY);
water->frame = curFrame|FF_ADD|FF_TRANS40;
// 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);
water->angle = 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, S_WATERTRAILS);
water->frame = curFrame;
// 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);
water->angle = 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, S_WATERTRAILSUNDERLAY);
water->frame = curFrame|FF_ADD|FF_TRANS40;
// 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);
water->angle = 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, S_WATERTRAILS);
water->frame = curFrame;
if (!S_SoundPlaying(player->mo, sfx_s3kdbs))
{
const INT32 volume = (min(trailScale, FRACUNIT) * 255) / FRACUNIT;
S_StartSoundAtVolume(player->mo, sfx_s3kdbs, volume);
}
}
K_SpawnWaterTrail(player->mo);
}
// Little water sound while touching water - just a nicety.