diff --git a/src/d_player.h b/src/d_player.h index a3f3143aa..f1d8e02bd 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -71,7 +71,8 @@ typedef enum PF_GAINAX = 1<<3, PF_KICKSTARTACCEL = 1<<4, // Accessibility feature: Is accelerate in kickstart mode? - // 1<<5 free + + PF_POINTME = 1<<5, // An object is calling for my attention (via Obj_PointPlayersToMobj). Unset every frame! // 1<<6 free PF_WANTSTOJOIN = 1<<7, // Spectator that wants to join diff --git a/src/g_game.c b/src/g_game.c index 6f6e3bc79..a4ec56cf2 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3405,7 +3405,7 @@ static gametype_t defaultgametypes[] = TOL_BATTLE, int_scoreortimeattack, 0, - 2, + 3, }, // GT_SPECIAL diff --git a/src/info.c b/src/info.c index 360e3d887..394f73f99 100644 --- a/src/info.c +++ b/src/info.c @@ -29308,7 +29308,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY|MF_DONTENCOREMAP, // flags + MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DRAWFROMFARAWAY|MF_SCENERY|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, diff --git a/src/k_battle.c b/src/k_battle.c index 6ba2cc412..31cd4b39d 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -500,7 +500,7 @@ void K_RunPaperItemSpawners(void) const UINT8 r = spotMap[P_RandomKey(PR_ITEM_ROULETTE, spotAvailable)]; Obj_ItemSpotAssignMonitor(spotList[r], Obj_SpawnMonitor( - spotList[r], 1 + pcount, firstUnspawnedEmerald)); + spotList[r], 3, firstUnspawnedEmerald)); } for (i = 0; i < spotCount; ++i) @@ -570,6 +570,8 @@ void K_RunPaperItemSpawners(void) static void K_SpawnOvertimeLaser(fixed_t x, fixed_t y, fixed_t scale) { + const fixed_t heightPadding = 346 * scale; + UINT8 i, j; for (i = 0; i <= r_splitscreen; i++) @@ -586,10 +588,12 @@ static void K_SpawnOvertimeLaser(fixed_t x, fixed_t y, fixed_t scale) if (player->mo->eflags & MFE_VERTICALFLIP) { zpos = player->mo->z + player->mo->height; + zpos = min(zpos + heightPadding, player->mo->ceilingz); } else { zpos = player->mo->z; + zpos = max(zpos - heightPadding, player->mo->floorz); } flip = P_MobjFlip(player->mo); @@ -665,9 +669,17 @@ void K_RunBattleOvertime(void) const fixed_t minradius = 768 * mapobjectscale; if (battleovertime.radius > minradius) - battleovertime.radius -= 2*mapobjectscale; - else + battleovertime.radius -= (battleovertime.initial_radius / (30*TICRATE)); + + if (battleovertime.radius < minradius) battleovertime.radius = minradius; + + // Subtract the 10 second grace period of the barrier + if (battleovertime.enabled < 25*TICRATE) + { + battleovertime.enabled++; + Obj_PointPlayersToXY(battleovertime.x, battleovertime.y); + } } if (battleovertime.radius > 0) diff --git a/src/k_battle.h b/src/k_battle.h index 9aa19cd71..f5a7547aa 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -10,8 +10,8 @@ extern "C" { #define BATTLE_SPAWN_INTERVAL (4*TICRATE) #define BATTLE_DESPAWN_TIME (15*TICRATE) -#define BATTLE_POWERUP_TIME (20*TICRATE) -#define BATTLE_UFO_TIME (25*TICRATE) +#define BATTLE_POWERUP_TIME (30*TICRATE) +#define BATTLE_UFO_TIME (20*TICRATE) #define BATTLE_MONITOR_SPAWN_LIMIT (3) @@ -19,6 +19,7 @@ extern struct battleovertime { UINT16 enabled; ///< Has this been initalized yet? fixed_t radius; ///< Radius of kill field + fixed_t initial_radius; ///< Starting radius of kill field fixed_t x, y, z; ///< Position to center on } battleovertime; diff --git a/src/k_hud.c b/src/k_hud.c index 1f2977ba6..bcd784306 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -113,6 +113,7 @@ static patch_t *kp_battleinfo; static patch_t *kp_wanted; static patch_t *kp_wantedsplit; static patch_t *kp_wantedreticle; +static patch_t *kp_minimapdot; static patch_t *kp_itembg[4]; static patch_t *kp_ringbg[4]; @@ -445,6 +446,7 @@ void K_LoadKartHUDGraphics(void) HU_UpdatePatch(&kp_wanted, "K_WANTED"); HU_UpdatePatch(&kp_wantedsplit, "4PWANTED"); HU_UpdatePatch(&kp_wantedreticle, "MMAPWANT"); + HU_UpdatePatch(&kp_minimapdot, "MMAPDOT"); // Kart Item Windows HU_UpdatePatch(&kp_itembg[0], "K_ITBG"); @@ -2219,7 +2221,7 @@ static boolean K_drawKartPositionFaces(void) if ((gametyperules & GTR_BUMPERS) && (players[rankplayer[i]].pflags & PF_ELIMINATED)) V_DrawScaledPatch(FACE_X-4, Y-3, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_ranknobumpers); - else + else if (gametyperules & GTR_CIRCUIT) { INT32 pos = players[rankplayer[i]].position; if (pos < 0 || pos > MAXPLAYERS) @@ -2227,6 +2229,34 @@ static boolean K_drawKartPositionFaces(void) // Draws the little number over the face V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facenum[pos]); } + else + { + INT32 flags = V_HUDTRANS | V_SLIDEIN | V_SNAPTOLEFT; + + colormap = NULL; + + if (g_pointlimit <= players[rankplayer[i]].roundscore) + { + if (leveltime % 8 < 4) + { + colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_TANGERINE, GTC_CACHE); + } + + flags |= V_STRINGDANCE; + } + + V_DrawStringScaled( + (FACE_X - 5) * FRACUNIT, + (Y + 10) * FRACUNIT, + FRACUNIT, + FRACUNIT, + FRACUNIT, + flags, + colormap, + PINGF_FONT, + va("%d", players[rankplayer[i]].roundscore) + ); + } Y -= 18; } @@ -3712,6 +3742,8 @@ static void K_drawKartMinimapWaypoint(waypoint_t *wp, INT32 hudx, INT32 hudy, IN K_drawKartMinimapDot(wp->mobj->x, wp->mobj->y, hudx, hudy, flags | V_NOSCALESTART, pal, size); } +#define ICON_DOT_RADIUS (10) + static void K_drawKartMinimap(void) { patch_t *workingPic; @@ -3894,6 +3926,8 @@ static void K_drawKartMinimap(void) { for (i = MAXPLAYERS-1; i >= 0; i--) { + boolean nocontest = false; + if (!playeringame[i]) continue; if (!players[i].mo || players[i].spectator || !players[i].mo->skin @@ -3937,6 +3971,8 @@ static void K_drawKartMinimap(void) colormap = R_GetTranslationColormap(TC_DEFAULT, mobj->color, GTC_CACHE); mobj = mobj->tracer; + + nocontest = true; } else { @@ -3957,6 +3993,8 @@ static void K_drawKartMinimap(void) if (doprogressionbar == false) { + angle_t ang = R_InterpolateAngle(mobj->old_angle, mobj->angle); + interpx = R_InterpolateFixed(mobj->old_x, mobj->x); interpy = R_InterpolateFixed(mobj->old_y, mobj->y); @@ -3968,6 +4006,19 @@ static void K_drawKartMinimap(void) { K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL); } + + if (!nocontest) + { + K_drawKartMinimapIcon( + interpx, + interpy, + x + FixedMul(FCOS(ang), ICON_DOT_RADIUS), + y - FixedMul(FSIN(ang), ICON_DOT_RADIUS), + splitflags, + kp_minimapdot, + colormap + ); + } } else { @@ -4098,6 +4149,8 @@ static void K_drawKartMinimap(void) for (i = 0; i < numlocalplayers; i++) { + boolean nocontest = false; + if (localplayers[i] == -1) continue; // this doesn't interest us @@ -4122,6 +4175,8 @@ static void K_drawKartMinimap(void) colormap = R_GetTranslationColormap(TC_DEFAULT, mobj->color, GTC_CACHE); mobj = mobj->tracer; + + nocontest = true; } else { @@ -4142,6 +4197,8 @@ static void K_drawKartMinimap(void) if (doprogressionbar == false) { + angle_t ang = R_InterpolateAngle(mobj->old_angle, mobj->angle); + interpx = R_InterpolateFixed(mobj->old_x, mobj->x); interpy = R_InterpolateFixed(mobj->old_y, mobj->y); @@ -4153,6 +4210,19 @@ static void K_drawKartMinimap(void) { K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL); } + + if (!nocontest) + { + K_drawKartMinimapIcon( + interpx, + interpy, + x + FixedMul(FCOS(ang), ICON_DOT_RADIUS), + y - FixedMul(FSIN(ang), ICON_DOT_RADIUS), + splitflags, + kp_minimapdot, + colormap + ); + } } else { diff --git a/src/k_kart.c b/src/k_kart.c index e3d5d5a2a..0735baf58 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3837,13 +3837,14 @@ void K_BattleAwardHit(player_t *player, player_t *victim, mobj_t *inflictor, UIN } } - P_AddPlayerScore(player, points); - K_SpawnBattlePoints(player, victim, points); - + // Check this before adding to player score if ((gametyperules & GTR_BUMPERS) && finishOff && g_pointlimit <= player->roundscore) { P_DoAllPlayersExit(0, false); } + + P_AddPlayerScore(player, points); + K_SpawnBattlePoints(player, victim, points); } void K_SpinPlayer(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 type) @@ -7635,7 +7636,7 @@ static inline BlockItReturn_t PIT_AttractingRings(mobj_t *thing) return BMIT_CONTINUE; // invalid } - if (!(thing->type == MT_RING || thing->type == MT_FLINGRING)) + if (!(thing->type == MT_RING || thing->type == MT_FLINGRING || thing->type == MT_EMERALD)) { return BMIT_CONTINUE; // not a ring } @@ -7645,7 +7646,7 @@ static inline BlockItReturn_t PIT_AttractingRings(mobj_t *thing) return BMIT_CONTINUE; // dead } - if (thing->extravalue1) + if (thing->extravalue1 && thing->type != MT_EMERALD) { return BMIT_CONTINUE; // in special ring animation } @@ -8424,6 +8425,8 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) } K_HandleDelayedHitByEm(player); + + player->pflags &= ~PF_POINTME; } void K_KartResetPlayerColor(player_t *player) @@ -8648,7 +8651,7 @@ void K_KartPlayerAfterThink(player_t *player) player->jawztargetdelay = 0; } - if (player->itemtype == KITEM_LIGHTNINGSHIELD) + if (player->itemtype == KITEM_LIGHTNINGSHIELD || ((gametyperules & GTR_POWERSTONES) && K_IsPlayerWanted(player))) { K_LookForRings(player->mo); } diff --git a/src/k_objects.h b/src/k_objects.h index 4f24a6a05..92477ca4d 100644 --- a/src/k_objects.h +++ b/src/k_objects.h @@ -145,6 +145,7 @@ void Obj_SpawnGachaBomRebound(mobj_t *source, mobj_t *target); /* Servant Hand */ void Obj_ServantHandHandling(player_t *player); +void Obj_PointPlayersToXY(fixed_t x, fixed_t y); /* Super Flicky Controller */ void Obj_SpawnSuperFlickySwarm(player_t *owner, tic_t time); diff --git a/src/k_roulette.c b/src/k_roulette.c index c85162c12..9e065c81f 100644 --- a/src/k_roulette.c +++ b/src/k_roulette.c @@ -7,7 +7,7 @@ // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- -/// \file k_roulette.c +/// \file 0 /// \brief Item roulette code. #include "k_roulette.h" @@ -111,36 +111,36 @@ static UINT8 K_KartItemOddsRace[NUMKARTRESULTS-1][8] = static UINT8 K_KartItemOddsBattle[NUMKARTRESULTS-1][2] = { - { 2, 1 }, // Sneaker + { 0, 1 }, // Sneaker { 0, 0 }, // Rocket Sneaker - { 4, 1 }, // Invincibility + { 0, 1 }, // Invincibility { 0, 0 }, // Banana - { 1, 0 }, // Eggman Monitor - { 8, 0 }, // Orbinaut - { 8, 1 }, // Jawz - { 6, 1 }, // Mine - { 2, 0 }, // Land Mine + { 0, 0 }, // Eggman Monitor + { 2, 0 }, // Orbinaut + { 3, 1 }, // Jawz + { 2, 1 }, // Mine + { 0, 0 }, // Land Mine { 2, 1 }, // Ballhog { 0, 0 }, // Self-Propelled Bomb - { 2, 1 }, // Grow + { 1, 1 }, // Grow { 0, 0 }, // Shrink - { 4, 0 }, // Lightning Shield + { 0, 0 }, // Lightning Shield { 1, 0 }, // Bubble Shield - { 1, 0 }, // Flame Shield - { 2, 0 }, // Hyudoro - { 3, 0 }, // Pogo Spring + { 0, 0 }, // Flame Shield + { 0, 0 }, // Hyudoro + { 0, 0 }, // Pogo Spring { 0, 0 }, // Super Ring { 0, 0 }, // Kitchen Sink - { 2, 0 }, // Drop Target - { 4, 0 }, // Garden Top - { 0, 0 }, // Gachabom + { 0, 0 }, // Drop Target + { 0, 0 }, // Garden Top + { 5, 0 }, // Gachabom { 0, 0 }, // Sneaker x2 { 0, 1 }, // Sneaker x3 { 0, 0 }, // Banana x3 { 2, 0 }, // Orbinaut x3 - { 1, 1 }, // Orbinaut x4 - { 5, 1 }, // Jawz x2 - { 0, 0 } // Gachabom x3 + { 2, 1 }, // Orbinaut x4 + { 2, 1 }, // Jawz x2 + { 2, 0 } // Gachabom x3 }; static UINT8 K_KartItemOddsSpecial[NUMKARTRESULTS-1][4] = diff --git a/src/objects/battle-ufo.cpp b/src/objects/battle-ufo.cpp index db8ef23c9..b7aff6296 100644 --- a/src/objects/battle-ufo.cpp +++ b/src/objects/battle-ufo.cpp @@ -132,6 +132,11 @@ void Obj_BattleUFOThink(mobj_t *mobj) { ufo->spawn_beam(); } + + if (!battleovertime.enabled) + { + Obj_PointPlayersToXY(mobj->x, mobj->y); + } } void Obj_BattleUFODeath(mobj_t *mobj) diff --git a/src/objects/servant-hand.c b/src/objects/servant-hand.c index 3a94ab69a..2b9cb7527 100644 --- a/src/objects/servant-hand.c +++ b/src/objects/servant-hand.c @@ -5,10 +5,12 @@ #include "../k_kart.h" #include "../k_objects.h" #include "../v_video.h" +#include "../r_main.h" +#include "../g_game.h" void Obj_ServantHandHandling(player_t *player) { - if (player->pflags & PF_WRONGWAY) + if (player->pflags & PF_WRONGWAY || player->pflags & PF_POINTME) { if (player->handtimer < TICRATE) { @@ -101,3 +103,18 @@ void Obj_ServantHandHandling(player_t *player) player->hand->renderflags |= (RF_DONTDRAW & ~K_GetPlayerDontDrawFlag(player)); } } + +void Obj_PointPlayersToXY(fixed_t x, fixed_t y) +{ + for(int i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || !players[i].mo) + continue; + + angle_t angletotarget = R_PointToAngle2( + players[i].mo->x, players[i].mo->y, + x, y); + players[i].pflags |= PF_POINTME; + players[i].besthanddirection = angletotarget; + } +} diff --git a/src/p_enemy.c b/src/p_enemy.c index 5e786c677..478a7441b 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3800,7 +3800,7 @@ void A_AttractChase(mobj_t *actor) if (actor->flags2 & MF2_NIGHTSPULL || !actor->health) return; - if (actor->extravalue1) // SRB2Kart + if (actor->extravalue1 && actor->type != MT_EMERALD) // SRB2Kart { if (!actor->target || P_MobjWasRemoved(actor->target) || !actor->target->player) { diff --git a/src/p_inter.c b/src/p_inter.c index f1c8409e3..dadc45fd9 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -453,7 +453,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) return; } case MT_EMERALD: - if (!P_CanPickupItem(player, 0)) + if (!P_CanPickupItem(player, 0) || P_PlayerInPain(player)) return; if (special->threshold > 0) @@ -829,6 +829,8 @@ void P_CheckTimeLimit(void) thinker_t *th; mobj_t *center = NULL; + fixed_t rx, ry; + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) { mobj_t *thismo; @@ -860,7 +862,26 @@ void P_CheckTimeLimit(void) battleovertime.z = center->z; } - battleovertime.radius = 4096 * mapobjectscale; + // Get largest radius from center point to minimap edges + + rx = max( + abs(battleovertime.x - (minimapinfo.min_x * FRACUNIT)), + abs(battleovertime.x - (minimapinfo.max_x * FRACUNIT)) + ); + + ry = max( + abs(battleovertime.y - (minimapinfo.min_y * FRACUNIT)), + abs(battleovertime.y - (minimapinfo.max_y * FRACUNIT)) + ); + + battleovertime.initial_radius = min( + max(max(rx, ry), 4096 * mapobjectscale), + // Prevent overflow in K_RunBattleOvertime + FixedDiv(INT32_MAX, M_PI_FIXED) / 2 + ); + + battleovertime.radius = battleovertime.initial_radius; + battleovertime.enabled = 1; S_StartSound(NULL, sfx_kc47); @@ -2447,7 +2468,10 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da K_BattleAwardHit(source->player, player, inflictor, damage); } - K_TakeBumpersFromPlayer(source->player, player, damage); + if (K_Bumpers(source->player) < K_StartingBumperCount() || (damagetype & DMG_STEAL)) + { + K_TakeBumpersFromPlayer(source->player, player, damage); + } if (damagetype & DMG_STEAL) { diff --git a/src/p_mobj.c b/src/p_mobj.c index ec92a5efc..39c7710f7 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -7538,6 +7538,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj) { if (mobj->threshold > 0) mobj->threshold--; + + A_AttractChase(mobj); } /*FALLTHRU*/ case MT_MONITOR: @@ -8448,6 +8450,18 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } case MT_BATTLEUFO: { + if (battleovertime.enabled >= 10*TICRATE) + { + fixed_t distance = R_PointToDist2(mobj->x, mobj->y, battleovertime.x, battleovertime.y); + + if (distance > battleovertime.radius) + { + // Delete emeralds to let them reappear + P_KillMobj(mobj, NULL, NULL, DMG_NORMAL); + return false; + } + } + Obj_BattleUFOThink(mobj); break; } diff --git a/src/p_saveg.c b/src/p_saveg.c index 87a8f636b..885b7b5c0 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -5734,6 +5734,7 @@ static void P_NetArchiveMisc(savebuffer_t *save, boolean resending) // battleovertime_t WRITEUINT16(save->p, battleovertime.enabled); WRITEFIXED(save->p, battleovertime.radius); + WRITEFIXED(save->p, battleovertime.initial_radius); WRITEFIXED(save->p, battleovertime.x); WRITEFIXED(save->p, battleovertime.y); WRITEFIXED(save->p, battleovertime.z); @@ -5908,6 +5909,7 @@ static boolean P_NetUnArchiveMisc(savebuffer_t *save, boolean reloading) // battleovertime_t battleovertime.enabled = READUINT16(save->p); battleovertime.radius = READFIXED(save->p); + battleovertime.initial_radius = READFIXED(save->p); battleovertime.x = READFIXED(save->p); battleovertime.y = READFIXED(save->p); battleovertime.z = READFIXED(save->p);