diff --git a/src/k_collide.cpp b/src/k_collide.cpp index 0b78db526..76917d2b5 100644 --- a/src/k_collide.cpp +++ b/src/k_collide.cpp @@ -18,6 +18,7 @@ #include "k_podium.h" #include "k_powerup.h" #include "k_hitlag.h" +#include "m_random.h" angle_t K_GetCollideAngle(mobj_t *t1, mobj_t *t2) { @@ -1170,3 +1171,114 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2) return false; } + +void K_PuntHazard(mobj_t *t1, mobj_t *t2) +{ + // TODO: spawn a unique mobjtype other than MT_GHOST + mobj_t *img = P_SpawnGhostMobj(t1); + + K_MakeObjectReappear(t1); + + img->flags &= ~MF_NOGRAVITY; + img->renderflags = t1->renderflags & ~RF_DONTDRAW; + img->extravalue1 = 1; + img->extravalue2 = 2; + img->fuse = 2*TICRATE; + + struct Vector + { + fixed_t x_, y_, z_; + fixed_t h_ = FixedHypot(x_, y_); + fixed_t speed_ = std::max(60 * mapobjectscale, FixedHypot(h_, z_) * 2); + + explicit Vector(fixed_t x, fixed_t y, fixed_t z) : x_(x), y_(y), z_(z) {} + explicit Vector(const mobj_t* mo) : + Vector(std::max( + Vector(mo->x - mo->old_x, mo->y - mo->old_y, mo->z - mo->old_z), + Vector(mo->momx, mo->momy, mo->momz) + )) + { + } + explicit Vector(const Vector&) = default; + + bool operator<(const Vector& b) const { return speed_ < b.speed_; } + + void invert() + { + x_ = -x_; + y_ = -y_; + z_ = -z_; + } + + void thrust(mobj_t* mo) const + { + angle_t yaw = R_PointToAngle2(0, 0, h_, z_); + yaw = std::max(AbsAngle(yaw), static_cast(ANGLE_11hh)) + (yaw & ANGLE_180); + + P_InstaThrust(mo, R_PointToAngle2(0, 0, x_, y_), FixedMul(speed_, FCOS(yaw))); + mo->momz = FixedMul(speed_, FSIN(yaw)); + } + }; + + Vector h_vector(t1); + Vector p_vector(t2); + + h_vector.invert(); + + std::max(h_vector, p_vector).thrust(img); + + K_DoPowerClash(img, t2); // applies hitlag + P_SpawnMobj(t2->x/2 + t1->x/2, t2->y/2 + t1->y/2, t2->z/2 + t1->z/2, MT_ITEMCLASH); +} + +boolean K_PuntCollide(mobj_t *t1, mobj_t *t2) +{ + if (t1->flags & MF_DONTPUNT) + { + return false; + } + + if (!t2->player) + { + return false; + } + + if (!K_PlayerCanPunt(t2->player)) + { + return false; + } + + if (t1->flags & MF_ELEMENTAL) + { + K_MakeObjectReappear(t1); + + // copied from MT_ITEMCAPSULE + UINT8 i; + INT16 spacing = (t1->radius >> 1) / t1->scale; + // dust effects + for (i = 0; i < 10; i++) + { + mobj_t *puff = P_SpawnMobjFromMobj( + t1, + P_RandomRange(PR_ITEM_DEBRIS, -spacing, spacing) * FRACUNIT, + P_RandomRange(PR_ITEM_DEBRIS, -spacing, spacing) * FRACUNIT, + P_RandomRange(PR_ITEM_DEBRIS, 0, 4*spacing) * FRACUNIT, + MT_SPINDASHDUST + ); + + puff->momz = puff->scale * P_MobjFlip(puff); + + P_Thrust(puff, R_PointToAngle2(t2->x, t2->y, puff->x, puff->y), 3*puff->scale); + + puff->momx += t2->momx / 2; + puff->momy += t2->momy / 2; + puff->momz += t2->momz / 2; + } + } + else + { + K_PuntHazard(t1, t2); + } + + return true; +} diff --git a/src/k_collide.h b/src/k_collide.h index 84d9a324e..525e5b9c3 100644 --- a/src/k_collide.h +++ b/src/k_collide.h @@ -33,6 +33,9 @@ boolean K_SMKIceBlockCollide(mobj_t *t1, mobj_t *t2); boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2); +void K_PuntHazard(mobj_t *t1, mobj_t *t2); +boolean K_PuntCollide(mobj_t *t1, mobj_t *t2); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_kart.c b/src/k_kart.c index e5e07eae8..cc6137a2b 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -12636,4 +12636,34 @@ boolean K_IsPlayingDisplayPlayer(player_t *player) return P_IsDisplayPlayer(player) && (!player->exiting); } +boolean K_PlayerCanPunt(player_t *player) +{ + if (player->invincibilitytimer > 0) + { + return true; + } + + if (player->flamedash > 0 && player->itemtype == KITEM_FLAMESHIELD) + { + return true; + } + + if (player->growshrinktimer > 0) + { + return true; + } + + if (player->tripwirePass >= TRIPWIRE_BLASTER && player->speed >= 2 * K_GetKartSpeed(player, false, false)) + { + return true; + } + + return false; +} + +void K_MakeObjectReappear(mobj_t *mo) +{ + (!P_MobjWasRemoved(mo->punt_ref) ? mo->punt_ref : mo)->reappear = leveltime + (30*TICRATE); +} + //} diff --git a/src/k_kart.h b/src/k_kart.h index 4ddd8b1c3..7b3d6d62c 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -245,6 +245,9 @@ void K_SetTireGrease(player_t *player, tic_t tics); boolean K_IsPlayingDisplayPlayer(player_t *player); +boolean K_PlayerCanPunt(player_t *player); +void K_MakeObjectReappear(mobj_t *mo); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/p_inter.c b/src/p_inter.c index cce3bf9c1..69d0c2a17 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -43,6 +43,7 @@ #include "k_hitlag.h" #include "acs/interface.h" #include "k_powerup.h" +#include "k_collide.h" // CTF player names #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" @@ -2929,6 +2930,14 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da invincible = false; } + // TODO: doing this from P_DamageMobj limits punting to objects that damage the player. + // And it may be kind of yucky. + // But this is easier than accounting for every condition in PIT_CheckThing! + if (inflictor && K_PuntCollide(inflictor, target)) + { + return false; + } + if (invincible && type != DMG_STUMBLE && type != DMG_WHUMBLE) { const INT32 oldHitlag = target->hitlag; diff --git a/src/p_local.h b/src/p_local.h index 045a59472..4cd26c9fd 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -595,6 +595,8 @@ mobj_t *P_FindMobjFromTID(mtag_t tid, mobj_t *i, mobj_t *activator); void P_DeleteMobjStringArgs(mobj_t *mobj); +tic_t P_MobjIsReappearing(const mobj_t *mobj); + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/p_map.c b/src/p_map.c index 3b50ee1fd..9888c23c3 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -563,6 +563,10 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) if ((thing->flags & MF_NOCLIPTHING) || !(thing->flags & (MF_SOLID|MF_SPECIAL|MF_PAIN|MF_SHOOTABLE|MF_SPRING))) return BMIT_CONTINUE; + // Thing is respawning + if (P_MobjIsReappearing(thing)) + return BMIT_CONTINUE; + blockdist = thing->radius + tm.thing->radius; if (abs(thing->x - tm.x) >= blockdist || abs(thing->y - tm.y) >= blockdist) @@ -1631,7 +1635,10 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing) if (tm.thing->z + tm.thing->height < thing->z) return BMIT_CONTINUE; // underneath - K_KartSolidBounce(tm.thing, thing); + if (!K_PuntCollide(thing, tm.thing)) + { + K_KartSolidBounce(tm.thing, thing); + } return BMIT_CONTINUE; } } @@ -2346,7 +2353,8 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y, TryMoveResult_t *re // Check things first, possibly picking things up. // MF_NOCLIPTHING: used by camera to not be blocked by things - if (!(thing->flags & MF_NOCLIPTHING)) + // Respawning things should also be intangible to other things + if (!(thing->flags & MF_NOCLIPTHING) && !P_MobjIsReappearing(thing)) { for (bx = xl; bx <= xh; bx++) { diff --git a/src/p_mobj.c b/src/p_mobj.c index e781107f0..fdb6e0f24 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -15381,3 +15381,9 @@ void P_DeleteMobjStringArgs(mobj_t *mobj) mobj->script_stringargs[i] = NULL; } } + +tic_t P_MobjIsReappearing(const mobj_t *mobj) +{ + tic_t t = (!P_MobjWasRemoved(mobj->punt_ref) ? mobj->punt_ref : mobj)->reappear; + return t - min(leveltime, t); +} diff --git a/src/p_mobj.h b/src/p_mobj.h index 96f6aa5bb..1fbb676bf 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -437,7 +437,12 @@ struct mobj_t boolean frozen; + // Object was punted and is temporarily invisible and + // intangible. This is the leveltime that it will + // reappear. tic_t reappear; + + // If punt_ref, set punt_ref->reappear, treat as if this->reappear mobj_t *punt_ref; // WARNING: New fields must be added separately to savegame and Lua. diff --git a/src/p_user.c b/src/p_user.c index cab3659dd..516bc168a 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1166,6 +1166,9 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) K_ReduceVFX(ghost, mobj->player); + ghost->reappear = mobj->reappear; + P_SetTarget(&ghost->punt_ref, mobj->punt_ref); + return ghost; } diff --git a/src/r_things.cpp b/src/r_things.cpp index 0ecf02c12..deacfce92 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -3746,6 +3746,12 @@ boolean R_ThingVisible (mobj_t *thing) if (r_viewmobj && (thing == r_viewmobj || (r_viewmobj->player && r_viewmobj->player->followmobj == thing))) return false; + if (tic_t t = P_MobjIsReappearing(thing)) + { + // Flicker back in + return t <= 2*TICRATE && (leveltime & 1); + } + if ((viewssnum == 0 && (thing->renderflags & RF_DONTDRAWP1)) || (viewssnum == 1 && (thing->renderflags & RF_DONTDRAWP2)) || (viewssnum == 2 && (thing->renderflags & RF_DONTDRAWP3)) diff --git a/src/s_sound.c b/src/s_sound.c index f744b1598..33cf4c3be 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -948,6 +948,9 @@ boolean S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, INT32 if (!listener) return false; + if (source->thinker.function.acp1 == (actionf_p1)P_MobjThinker && P_MobjIsReappearing(source)) + return false; + // Init listensource with default listener listensource.x = listener->x; listensource.y = listener->y;