diff --git a/src/k_hud_track.cpp b/src/k_hud_track.cpp index 1ab870ea3..2f9c190e2 100644 --- a/src/k_hud_track.cpp +++ b/src/k_hud_track.cpp @@ -232,6 +232,8 @@ private: case MT_BATTLEUFO_SPAWNER: case MT_WAYPOINT: + case MT_BUBBLESHIELDTRAP: + case MT_GARDENTOP: return {}; default: @@ -341,11 +343,36 @@ bool is_object_tracking_target(const mobj_t* mobj) case MT_WAYPOINT: return cv_kartdebugwaypoints.value; + case MT_BUBBLESHIELDTRAP: + return mobj->tracer && !P_MobjWasRemoved(mobj->tracer) + && mobj->tracer->player && P_IsDisplayPlayer(mobj->tracer->player) + && mobj->tracer->player == &players[displayplayers[R_GetViewNumber()]]; + + case MT_GARDENTOP: + return mobj->tracer && !P_MobjWasRemoved(mobj->tracer) + && mobj->tracer->player && P_IsDisplayPlayer(mobj->tracer->player) + && mobj->tracer->player == &players[displayplayers[R_GetViewNumber()]] + && Obj_GardenTopPlayerNeedsHelp(mobj); + default: return false; } } +bool can_object_be_offscreen(const mobj_t* mobj) +{ + switch (mobj->type) + { + // You can see this, you fucking liar + case MT_GARDENTOP: + case MT_BUBBLESHIELDTRAP: + return false; + + default: + return true; + } +} + Visibility is_object_visible(const mobj_t* mobj) { switch (mobj->type) @@ -381,7 +408,7 @@ void K_DrawTargetTracking(const TargetTracking& target) const trackingResult_t& result = target.result; int32_t timer = 0; - if (result.onScreen == false) + if (can_object_be_offscreen(target.mobj) && result.onScreen == false) { // Off-screen, draw alongside the borders of the screen. // Probably the most complicated thing. @@ -578,7 +605,9 @@ void K_DrawTargetTracking(const TargetTracking& target) .align(Draw::Align::kCenter); }; - switch (target.mobj->type) // debug + srb2::Draw::Font splitfont = (r_splitscreen > 1) ? Draw::Font::kThin : Draw::Font::kMenu; + + switch (target.mobj->type) { case MT_BATTLEUFO_SPAWNER: debug().text("BUFO ID: {}", Obj_BattleUFOSpawnerID(target.mobj)); @@ -592,6 +621,21 @@ void K_DrawTargetTracking(const TargetTracking& target) } break; + case MT_BUBBLESHIELDTRAP: + Draw(FixedToFloat(result.x), FixedToFloat(result.y)) + .flags(V_SPLITSCREEN) + .font(Draw::Font::kMenu) + .align(Draw::Align::kCenter) + .text(((leveltime/3)%2) ? "\xB3 " : " \xB2"); + break; + + case MT_GARDENTOP: + Draw(FixedToFloat(result.x), FixedToFloat(result.y)) + .flags(V_SPLITSCREEN) + .font(splitfont) + .align(Draw::Align::kCenter) + .text("Try \xA7!"); + default: break; } diff --git a/src/k_objects.h b/src/k_objects.h index fa708ae5c..93e6692d0 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -27,6 +27,7 @@ void Obj_GardenTopThink(mobj_t *top); void Obj_GardenTopSparkThink(mobj_t *spark); void Obj_GardenTopArrowThink(mobj_t *arrow); boolean Obj_GardenTopPlayerIsGrinding(const player_t *player); +boolean Obj_GardenTopPlayerNeedsHelp(const mobj_t *top); /* Shrink */ void Obj_PohbeeThinker(mobj_t *pohbee); diff --git a/src/objects/gardentop.c b/src/objects/gardentop.c index 143dca17f..c3b6b5e77 100644 --- a/src/objects/gardentop.c +++ b/src/objects/gardentop.c @@ -47,6 +47,8 @@ enum { #define top_waveangle(o) ((o)->movedir) /* wavepause will take mobjinfo reactiontime automatically */ #define top_wavepause(o) ((o)->reactiontime) +#define top_helpme(o) ((o)->cusval) +#define top_lifetime(o) ((o)->cvmem) #define spark_top(o) ((o)->target) #define spark_angle(o) ((o)->movedir) @@ -133,6 +135,8 @@ init_top top_float(top) = 0; top_sound(top) = sfx_None; top_waveangle(top) = 0; + top_helpme(top) = (mode == TOP_ANCHORED) ? 1 : 0; + top_lifetime(top) = 0; } static void @@ -191,6 +195,8 @@ spawn_grind_spark (mobj_t *top) player = get_rider_player(rider); } + top_helpme(top) = 0; + spark = P_SpawnMobjFromMobj( top, x, y, 0, MT_DRIFTSPARK); @@ -432,6 +438,8 @@ anchor_top (mobj_t *top) top->momy = 0; top->momz = rider->momz; + top_lifetime(top)++; + /* The Z momentum can put the Top slightly ahead of the player in that axis too. It looks cool if the Top falls below you but not if it bounces up. */ @@ -687,3 +695,11 @@ Obj_GardenTopPlayerIsGrinding (const player_t *player) return top ? is_top_grinding(top) : false; } + +boolean +Obj_GardenTopPlayerNeedsHelp (const mobj_t *top) +{ + if (top && top_mode(top) != TOP_ANCHORED) + return false; + return top ? (top_helpme(top) || top_lifetime(top) < 3*TICRATE) : false; +} diff --git a/src/p_inter.c b/src/p_inter.c index 4208d47d8..86a2a2094 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -634,6 +634,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_SetTarget(&special->tracer, toucher); toucher->flags |= MF_NOGRAVITY; toucher->momz = (8*toucher->scale) * P_MobjFlip(toucher); + + // Snap to the unfortunate player and quit moving laterally, or we can end up quite far away + special->momx = 0; + special->momy = 0; + special->x = toucher->x; + special->y = toucher->y; + special->z = toucher->z; + S_StartSound(toucher, sfx_s1b2); return; diff --git a/src/p_mobj.c b/src/p_mobj.c index a686fa609..7a3083a8d 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5359,6 +5359,12 @@ static boolean P_IsTrackerType(INT32 type) case MT_WAYPOINT: // debug return true; + case MT_BUBBLESHIELDTRAP: // Mash prompt + return true; + + case MT_GARDENTOP: // Frey + return true; + default: return false; }