New solid object bouncing, and updated player bouncing

- Solid objects have new bouncing physics, it's a mix of the object bumping and wall bumping. It replaces the "solid" option on K_KartBouncing.
- Goomba stomp momentum is decided in K_KartBouncing itself instead of needing to be manually passed in as a boolean, so you'll be able to goomba stomp all objects instead of just players.
- Fixed bumping not carrying all of its intended momentum, due to a bug making friction apply very strongly.
This commit is contained in:
Sally Coolatta 2021-04-22 20:29:59 -04:00
parent dcee9269dd
commit 5ac5ca89d0
5 changed files with 270 additions and 162 deletions

View file

@ -46,7 +46,7 @@ boolean K_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
{
// Player Damage
P_DamageMobj(t2, t1, t1->target, 1, DMG_WIPEOUT);
K_KartBouncing(t2, t1, false, false);
K_KartBouncing(t2, t1);
S_StartSound(t2, sfx_s3k7b);
}
@ -430,7 +430,7 @@ boolean K_KitchenSinkCollide(mobj_t *t1, mobj_t *t2)
boolean K_FallingRockCollide(mobj_t *t1, mobj_t *t2)
{
if (t2->player || t2->type == MT_FALLINGROCK)
K_KartBouncing(t2, t1, false, false);
K_KartBouncing(t2, t1);
return true;
}
@ -457,8 +457,8 @@ boolean K_SMKIceBlockCollide(mobj_t *t1, mobj_t *t2)
return true;
*/
K_KartBouncing(t2, t1, false, true);
return false;
K_KartSolidBounce(t1, t2);
return true;
}
boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
@ -537,25 +537,24 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
if (t1Condition == true)
{
P_PlayerRingBurst(t2->player, 1);
if (t2->player->rings <= 0)
{
P_DamageMobj(t2, t1, t1, 1, DMG_STING);
stung = true;
}
P_PlayerRingBurst(t2->player, 1);
stung = true;
}
if (t2Condition == true)
{
P_PlayerRingBurst(t1->player, 1);
if (t1->player->rings <= 0)
{
P_DamageMobj(t1, t2, t2, 1, DMG_STING);
stung = true;
}
P_PlayerRingBurst(t2->player, 1);
}
return stung;

View file

@ -1123,16 +1123,137 @@ fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against)
return FixedMul(weight, mobj->scale);
}
boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid)
static void K_SpawnBumpForObjs(mobj_t *mobj1, mobj_t *mobj2)
{
mobj_t *fx;
mobj_t *fx = P_SpawnMobj(
mobj1->x/2 + mobj2->x/2,
mobj1->y/2 + mobj2->y/2,
mobj1->z/2 + mobj2->z/2,
MT_BUMP
);
fixed_t avgScale = (mobj1->scale + mobj2->scale) / 2;
if (mobj1->eflags & MFE_VERTICALFLIP)
{
fx->eflags |= MFE_VERTICALFLIP;
}
else
{
fx->eflags &= ~MFE_VERTICALFLIP;
}
P_SetScale(fx, (fx->destscale = avgScale));
if ((mobj1->player && mobj1->player->itemtype == KITEM_BUBBLESHIELD)
|| (mobj2->player && mobj2->player->itemtype == KITEM_BUBBLESHIELD))
{
S_StartSound(mobj1, sfx_s3k44);
}
else
{
S_StartSound(mobj1, sfx_s3k49);
}
}
static void K_PlayerJustBumped(player_t *player)
{
mobj_t *playerMobj = NULL;
if (player == NULL)
{
return;
}
playerMobj = player->mo;
if (playerMobj == NULL || P_MobjWasRemoved(playerMobj))
{
return;
}
if (abs(player->rmomx) < playerMobj->scale && abs(player->rmomy) < playerMobj->scale)
{
// Because this is done during collision now, rmomx and rmomy need to be recalculated
// so that friction doesn't immediately decide to stop the player if they're at a standstill
player->rmomx = playerMobj->momx - player->cmomx;
player->rmomy = playerMobj->momy - player->cmomy;
}
player->justbumped = bumptime;
player->spindash = 0;
if (player->spinouttimer)
{
player->wipeoutslow = wipeoutslowtime+1;
player->spinouttimer = max(wipeoutslowtime+1, player->spinouttimer);
//player->spinouttype = KSPIN_WIPEOUT; // Enforce type
}
}
static fixed_t K_GetBounceForce(mobj_t *mobj1, mobj_t *mobj2, fixed_t distx, fixed_t disty)
{
const fixed_t forceMul = (4 * FRACUNIT) / 10; // Multiply by this value to make it feel like old bumps.
fixed_t momdifx, momdify;
fixed_t distx, disty;
fixed_t dot, force;
fixed_t dot;
fixed_t force = 0;
momdifx = mobj1->momx - mobj2->momx;
momdify = mobj1->momy - mobj2->momy;
if (distx == 0 && disty == 0)
{
// if there's no distance between the 2, they're directly on top of each other, don't run this
return 0;
}
{ // Normalize distance to the sum of the two objects' radii, since in a perfect world that would be the distance at the point of collision...
fixed_t dist = P_AproxDistance(distx, disty);
fixed_t nx, ny;
dist = dist ? dist : 1;
nx = FixedDiv(distx, dist);
ny = FixedDiv(disty, dist);
distx = FixedMul(mobj1->radius + mobj2->radius, nx);
disty = FixedMul(mobj1->radius + mobj2->radius, ny);
if (momdifx == 0 && momdify == 0)
{
// If there's no momentum difference, they're moving at exactly the same rate. Pretend they moved into each other.
momdifx = -nx;
momdify = -ny;
}
}
dot = FixedMul(momdifx, distx) + FixedMul(momdify, disty);
if (dot >= 0)
{
// They're moving away from each other
return 0;
}
// Return the push force!
force = FixedDiv(dot, FixedMul(distx, distx) + FixedMul(disty, disty));
return FixedMul(force, forceMul);
}
boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2)
{
const fixed_t minBump = 25*mapobjectscale;
mobj_t *goombaBounce = NULL;
fixed_t distx, disty, dist;
fixed_t force;
fixed_t mass1, mass2;
if (!mobj1 || !mobj2)
if ((!mobj1 || P_MobjWasRemoved(mobj1))
|| (!mobj2 || P_MobjWasRemoved(mobj2)))
{
return false;
}
// Don't bump when you're being reborn
if ((mobj1->player && mobj1->player->playerstate != PST_LIVE)
@ -1176,135 +1297,145 @@ boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean sol
return false;
}
mass1 = K_GetMobjWeight(mobj1, mobj2);
// Adds the OTHER object's momentum times a bunch, for the best chance of getting the correct direction
distx = (mobj1->x + mobj2->momx) - (mobj2->x + mobj1->momx);
disty = (mobj1->y + mobj2->momy) - (mobj2->y + mobj1->momy);
if (solid == true && mass1 > 0)
mass2 = mass1;
else
mass2 = K_GetMobjWeight(mobj2, mobj1);
force = K_GetBounceForce(mobj1, mobj2, distx, disty);
momdifx = mobj1->momx - mobj2->momx;
momdify = mobj1->momy - mobj2->momy;
// Adds the OTHER player's momentum times a bunch, for the best chance of getting the correct direction
distx = (mobj1->x + mobj2->momx*3) - (mobj2->x + mobj1->momx*3);
disty = (mobj1->y + mobj2->momy*3) - (mobj2->y + mobj1->momy*3);
if (distx == 0 && disty == 0)
if (force == 0)
{
// if there's no distance between the 2, they're directly on top of each other, don't run this
return false;
}
{ // Normalize distance to the sum of the two objects' radii, since in a perfect world that would be the distance at the point of collision...
fixed_t dist = P_AproxDistance(distx, disty);
fixed_t nx, ny;
mass1 = K_GetMobjWeight(mobj1, mobj2);
mass2 = K_GetMobjWeight(mobj2, mobj1);
dist = dist ? dist : 1;
nx = FixedDiv(distx, dist);
ny = FixedDiv(disty, dist);
distx = FixedMul(mobj1->radius+mobj2->radius, nx);
disty = FixedMul(mobj1->radius+mobj2->radius, ny);
if (momdifx == 0 && momdify == 0)
{
// If there's no momentum difference, they're moving at exactly the same rate. Pretend they moved into each other.
momdifx = -nx;
momdify = -ny;
}
if ((P_IsObjectOnGround(mobj1) && mobj2->momz < 0) // Grounded
|| (mass2 == 0 && mass1 > 0)) // The other party is immovable
{
goombaBounce = mobj2;
}
else if ((P_IsObjectOnGround(mobj2) && mobj1->momz < 0) // Grounded
|| (mass1 == 0 && mass2 > 0)) // The other party is immovable
{
goombaBounce = mobj1;
}
if (goombaBounce != NULL)
{
// Perform a Goomba Bounce by reversing your z momentum.
goombaBounce->momz = -goombaBounce->momz;
}
else
{
// Trade z momentum values.
fixed_t newz = mobj1->momz;
mobj1->momz = mobj2->momz;
mobj2->momz = newz;
}
// Multiply by force
distx = FixedMul(force, distx);
disty = FixedMul(force, disty);
dist = FixedHypot(distx, disty);
// if the speed difference is less than this let's assume they're going proportionately faster from each other
if (P_AproxDistance(momdifx, momdify) < (25*mapobjectscale))
if (dist < minBump)
{
fixed_t momdiflength = P_AproxDistance(momdifx, momdify);
fixed_t normalisedx = FixedDiv(momdifx, momdiflength);
fixed_t normalisedy = FixedDiv(momdify, momdiflength);
momdifx = FixedMul((25*mapobjectscale), normalisedx);
momdify = FixedMul((25*mapobjectscale), normalisedy);
}
fixed_t normalisedx = FixedDiv(distx, dist);
fixed_t normalisedy = FixedDiv(disty, dist);
dot = FixedMul(momdifx, distx) + FixedMul(momdify, disty);
if (dot >= 0)
{
// They're moving away from each other
return false;
}
force = FixedDiv(dot, FixedMul(distx, distx)+FixedMul(disty, disty));
if (bounce == true && mass2 > 0) // Perform a Goomba Bounce.
mobj1->momz = -mobj1->momz;
else
{
fixed_t newz = mobj1->momz;
if (mass2 > 0)
mobj1->momz = mobj2->momz;
if (mass1 > 0 && solid == false)
mobj2->momz = newz;
distx = FixedMul(minBump, normalisedx);
disty = FixedMul(minBump, normalisedy);
}
if (mass2 > 0)
{
mobj1->momx = mobj1->momx - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), distx);
mobj1->momy = mobj1->momy - FixedMul(FixedMul(FixedDiv(2*mass2, mass1 + mass2), force), disty);
mobj1->momx = mobj1->momx - FixedMul(FixedDiv(2*mass2, mass1 + mass2), distx);
mobj1->momy = mobj1->momy - FixedMul(FixedDiv(2*mass2, mass1 + mass2), disty);
}
if (mass1 > 0 && solid == false)
if (mass1 > 0)
{
mobj2->momx = mobj2->momx - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -distx);
mobj2->momy = mobj2->momy - FixedMul(FixedMul(FixedDiv(2*mass1, mass1 + mass2), force), -disty);
mobj2->momx = mobj2->momx - FixedMul(FixedDiv(2*mass1, mass1 + mass2), -distx);
mobj2->momy = mobj2->momy - FixedMul(FixedDiv(2*mass1, mass1 + mass2), -disty);
}
// Do the bump fx when we've CONFIRMED we can bump.
if ((mobj1->player && mobj1->player->itemtype == KITEM_BUBBLESHIELD) || (mobj2->player && mobj2->player->itemtype == KITEM_BUBBLESHIELD))
S_StartSound(mobj1, sfx_s3k44);
else
S_StartSound(mobj1, sfx_s3k49);
K_SpawnBumpForObjs(mobj1, mobj2);
fx = P_SpawnMobj(mobj1->x/2 + mobj2->x/2, mobj1->y/2 + mobj2->y/2, mobj1->z/2 + mobj2->z/2, MT_BUMP);
if (mobj1->eflags & MFE_VERTICALFLIP)
fx->eflags |= MFE_VERTICALFLIP;
else
fx->eflags &= ~MFE_VERTICALFLIP;
P_SetScale(fx, mobj1->scale);
K_PlayerJustBumped(mobj1->player);
K_PlayerJustBumped(mobj2->player);
// Because this is done during collision now, rmomx and rmomy need to be recalculated
// so that friction doesn't immediately decide to stop the player if they're at a standstill
// Also set justbumped here
if (mobj1->player)
return true;
}
// K_KartBouncing, but simplified to act like P_BouncePlayerMove
boolean K_KartSolidBounce(mobj_t *bounceMobj, mobj_t *solidMobj)
{
const fixed_t minBump = 25*mapobjectscale;
fixed_t distx, disty, dist;
fixed_t force;
if ((!bounceMobj || P_MobjWasRemoved(bounceMobj))
|| (!solidMobj || P_MobjWasRemoved(solidMobj)))
{
mobj1->player->rmomx = mobj1->momx - mobj1->player->cmomx;
mobj1->player->rmomy = mobj1->momy - mobj1->player->cmomy;
mobj1->player->justbumped = bumptime;
mobj1->player->spindash = 0;
if (mobj1->player->spinouttimer)
{
mobj1->player->wipeoutslow = wipeoutslowtime+1;
mobj1->player->spinouttimer = max(wipeoutslowtime+1, mobj1->player->spinouttimer);
//mobj1->player->spinouttype = KSPIN_WIPEOUT; // Enforce type
}
return false;
}
if (mobj2->player)
// Don't bump when you're being reborn
if (bounceMobj->player && bounceMobj->player->playerstate != PST_LIVE)
return false;
if (bounceMobj->player && bounceMobj->player->respawn.state != RESPAWNST_NONE)
return false;
// Don't bump if you've recently bumped
if (bounceMobj->player && bounceMobj->player->justbumped)
{
mobj2->player->rmomx = mobj2->momx - mobj2->player->cmomx;
mobj2->player->rmomy = mobj2->momy - mobj2->player->cmomy;
mobj2->player->justbumped = bumptime;
mobj2->player->spindash = 0;
if (mobj2->player->spinouttimer)
{
mobj2->player->wipeoutslow = wipeoutslowtime+1;
mobj2->player->spinouttimer = max(wipeoutslowtime+1, mobj2->player->spinouttimer);
//mobj2->player->spinouttype = KSPIN_WIPEOUT; // Enforce type
}
bounceMobj->player->justbumped = bumptime;
return false;
}
// Adds the OTHER object's momentum times a bunch, for the best chance of getting the correct direction
{
distx = (bounceMobj->x + solidMobj->momx) - (solidMobj->x + bounceMobj->momx);
disty = (bounceMobj->y + solidMobj->momy) - (solidMobj->y + bounceMobj->momy);
}
force = K_GetBounceForce(bounceMobj, solidMobj, distx, disty);
if (force == 0)
{
return false;
}
// Multiply by force
distx = FixedMul(force, distx);
disty = FixedMul(force, disty);
dist = FixedHypot(distx, disty);
{
// Normalize to the desired push value.
fixed_t normalisedx = FixedDiv(distx, dist);
fixed_t normalisedy = FixedDiv(disty, dist);
fixed_t bounceSpeed;
bounceSpeed = FixedHypot(bounceMobj->momx, bounceMobj->momy);
bounceSpeed = FixedMul(bounceSpeed, (FRACUNIT - (FRACUNIT>>2) - (FRACUNIT>>3)));
bounceSpeed += minBump;
distx = FixedMul(bounceSpeed, normalisedx);
disty = FixedMul(bounceSpeed, normalisedy);
}
bounceMobj->momx = bounceMobj->momx - distx;
bounceMobj->momy = bounceMobj->momy - disty;
bounceMobj->momz = -bounceMobj->momz;
K_SpawnBumpForObjs(bounceMobj, solidMobj);
K_PlayerJustBumped(bounceMobj->player);
return true;
}

View file

@ -37,7 +37,8 @@ UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 bestbum
INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival);
INT32 K_GetShieldFromItem(INT32 item);
fixed_t K_GetMobjWeight(mobj_t *mobj, mobj_t *against);
boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2, boolean bounce, boolean solid);
boolean K_KartBouncing(mobj_t *mobj1, mobj_t *mobj2);
boolean K_KartSolidBounce(mobj_t *bounceMobj, mobj_t *solidMobj);
void K_KartPainEnergyFling(player_t *player);
void K_FlipFromObject(mobj_t *mo, mobj_t *master);
void K_MatchGenericExtraFlags(mobj_t *mo, mobj_t *master);

View file

@ -3381,14 +3381,12 @@ static int lib_kKartBouncing(lua_State *L)
{
mobj_t *mobj1 = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
mobj_t *mobj2 = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
boolean bounce = lua_optboolean(L, 3);
boolean solid = lua_optboolean(L, 4);
NOHUD
if (!mobj1)
return LUA_ErrInvalid(L, "mobj_t");
if (!mobj2)
return LUA_ErrInvalid(L, "mobj_t");
K_KartBouncing(mobj1, mobj2, bounce, solid);
K_KartBouncing(mobj1, mobj2);
return 0;
}

View file

@ -1247,33 +1247,19 @@ static boolean PIT_CheckThing(mobj_t *thing)
return true;
}
// The bump has to happen last
if (P_IsObjectOnGround(thing) && tmthing->momz < 0 && tmthing->player->trickpanel)
{
// The bump has to happen last
mobj_t *mo1 = tmthing;
mobj_t *mo2 = thing;
boolean zbounce = false;
P_DamageMobj(thing, tmthing, tmthing, 1, DMG_WIPEOUT|DMG_STEAL);
}
else if (P_IsObjectOnGround(tmthing) && thing->momz < 0 && thing->player->trickpanel)
{
P_DamageMobj(tmthing, thing, thing, 1, DMG_WIPEOUT|DMG_STEAL);
}
if (P_IsObjectOnGround(thing) && tmthing->momz < 0)
{
zbounce = true;
mo1 = thing;
mo2 = tmthing;
if (tmthing->player->trickpanel)
P_DamageMobj(thing, tmthing, tmthing, 1, DMG_WIPEOUT|DMG_STEAL);
}
else if (P_IsObjectOnGround(tmthing) && thing->momz < 0)
{
zbounce = true;
if (thing->player->trickpanel)
P_DamageMobj(tmthing, thing, thing, 1, DMG_WIPEOUT|DMG_STEAL);
}
if (K_KartBouncing(mo1, mo2, zbounce, false))
{
K_PvPTouchDamage(mo1, mo2);
}
if (K_KartBouncing(tmthing, thing) == true)
{
K_PvPTouchDamage(tmthing, thing);
}
return true;
@ -1300,8 +1286,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
}
else
{
K_KartBouncing(tmthing, thing, false, true);
return false;
K_KartSolidBounce(tmthing, thing);
return true;
}
}
else if (thing->type == MT_SMK_PIPE)
@ -1322,8 +1308,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
return true; // kill
}
K_KartBouncing(tmthing, thing, false, true);
return false;
K_KartSolidBounce(tmthing, thing);
return true;
}
else if (thing->type == MT_SMK_THWOMP)
{
@ -1362,12 +1348,13 @@ static boolean PIT_CheckThing(mobj_t *thing)
P_DamageMobj(tmthing, thing, thing, 1, DMG_TUMBLE);
else
{
if (thing->flags2 & MF2_AMBUSH)
if ((K_KartSolidBounce(tmthing, thing) == true) && (thing->flags2 & MF2_AMBUSH))
{
P_DamageMobj(tmthing, thing, thing, 1, DMG_WIPEOUT);
K_KartBouncing(tmthing, thing, false, true);
}
}
return false;
return true;
}
else if (thing->type == MT_KART_LEFTOVER)
{
@ -1377,12 +1364,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->z + tmthing->height < thing->z)
return true; // underneath
if (P_IsObjectOnGround(thing) && tmthing->momz < 0)
K_KartBouncing(tmthing, thing, true, false);
else
K_KartBouncing(tmthing, thing, false, false);
return false;
K_KartBouncing(tmthing, thing);
return true;
}
else if (thing->flags & MF_SOLID)
{
@ -1392,12 +1375,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->z + tmthing->height < thing->z)
return true; // underneath
if (P_IsObjectOnGround(thing) && tmthing->momz < 0)
K_KartBouncing(tmthing, thing, true, true);
else
K_KartBouncing(tmthing, thing, false, true);
return false;
K_KartSolidBounce(tmthing, thing);
return true;
}
}