diff --git a/src/deh_tables.c b/src/deh_tables.c index cfe834370..bdb74b55b 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -1219,6 +1219,11 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_CHAOSEMERALD2", "S_CHAOSEMERALD_UNDER", + // Super Emeralds + "S_SUPEREMERALD1", + "S_SUPEREMERALD2", + "S_SUPEREMERALD_UNDER", + "S_EMERALDSPARK1", "S_EMERALDSPARK2", "S_EMERALDSPARK3", @@ -4557,6 +4562,8 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_SPECIAL_UFO_POD", "S_SPECIAL_UFO_OVERLAY", + "S_SPECIAL_UFO_GLASS", + "S_SPECIAL_UFO_GLASS_UNDER", "S_SPECIAL_UFO_ARM", "S_SPECIAL_UFO_STEM", diff --git a/src/info.c b/src/info.c index 8cfb54d28..1edddcb49 100644 --- a/src/info.c +++ b/src/info.c @@ -144,6 +144,7 @@ char sprnames[NUMSPRITES + 1][5] = "NSTR", // NiGHTS star "EMBM", // Emblem "EMRC", // Chaos Emeralds + "SEMR", // Super Emeralds "ESPK", "SHRD", // Emerald Hunt @@ -794,6 +795,8 @@ char sprnames[NUMSPRITES + 1][5] = "UFOB", "UFOA", "UFOS", + "SSCA", + "SSCB", "UQMK", @@ -1775,6 +1778,11 @@ state_t states[NUMSTATES] = {SPR_EMRC, FF_FULLBRIGHT|FF_ADD, 1, {NULL}, 0, 0, S_CHAOSEMERALD1}, // S_CHAOSEMERALD2 {SPR_EMRC, FF_FULLBRIGHT|1, -1, {NULL}, 1, 0, S_NULL}, // S_CHAOSEMERALD_UNDER + // Super Emeralds + {SPR_SEMR, FF_SEMIBRIGHT, 1, {NULL}, 0, 0, S_SUPEREMERALD2}, // S_SUPEREMERALD1 + {SPR_SEMR, FF_FULLBRIGHT|FF_ADD, 1, {NULL}, 0, 0, S_SUPEREMERALD1}, // S_SUPEREMERALD2 + {SPR_SEMR, FF_FULLBRIGHT|1, -1, {NULL}, 1, 0, S_NULL}, // S_SUPEREMERALD_UNDER + {SPR_ESPK, FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_EMERALDSPARK2}, // S_EMERALDSPARK1 {SPR_ESPK, FF_FULLBRIGHT|1, 3, {NULL}, 0, 0, S_EMERALDSPARK3}, // S_EMERALDSPARK2 {SPR_ESPK, FF_FULLBRIGHT|2, 3, {NULL}, 0, 0, S_EMERALDSPARK4}, // S_EMERALDSPARK3 @@ -5184,6 +5192,8 @@ state_t states[NUMSTATES] = {SPR_UFOB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_POD {SPR_UFOB, 1|FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 1, 1, S_NULL}, // S_SPECIAL_UFO_OVERLAY + {SPR_SSCA, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_GLASS + {SPR_SSCB, FF_SUBTRACT, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_GLASS_UNDER {SPR_UFOA, FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_ARM {SPR_UFOS, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SPECIAL_UFO_STEM @@ -22530,7 +22540,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOSQUISH, // flags + MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_NOSQUISH|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -22557,7 +22567,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_SCENERY|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOSQUISH, // flags + MF_SCENERY|MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPTHING|MF_NOSQUISH|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -22584,7 +22594,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT, // flags + MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags S_NULL // raisestate }, @@ -29318,7 +29328,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_SPECIAL_UFO -1, // doomednum - S_CHAOSEMERALD1, // spawnstate + S_INVISIBLE, // spawnstate 101, // spawnhealth S_NULL, // seestate sfx_None, // seesound diff --git a/src/info.h b/src/info.h index f7496fc14..6c9d7c773 100644 --- a/src/info.h +++ b/src/info.h @@ -695,6 +695,7 @@ typedef enum sprite SPR_NSTR, // NiGHTS star SPR_EMBM, // Emblem SPR_EMRC, // Chaos Emeralds + SPR_SEMR, // Super Emeralds SPR_ESPK, SPR_SHRD, // Emerald Hunt @@ -1345,6 +1346,8 @@ typedef enum sprite SPR_UFOB, SPR_UFOA, SPR_UFOS, + SPR_SSCA, + SPR_SSCB, SPR_UQMK, @@ -2256,6 +2259,11 @@ typedef enum state S_CHAOSEMERALD2, S_CHAOSEMERALD_UNDER, + // Super Emeralds + S_SUPEREMERALD1, + S_SUPEREMERALD2, + S_SUPEREMERALD_UNDER, + S_EMERALDSPARK1, S_EMERALDSPARK2, S_EMERALDSPARK3, @@ -5610,6 +5618,8 @@ typedef enum state S_SPECIAL_UFO_POD, S_SPECIAL_UFO_OVERLAY, + S_SPECIAL_UFO_GLASS, + S_SPECIAL_UFO_GLASS_UNDER, S_SPECIAL_UFO_ARM, S_SPECIAL_UFO_STEM, diff --git a/src/k_hud.c b/src/k_hud.c index cd8f77af6..f4261f302 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -13,6 +13,8 @@ #include "k_kart.h" #include "k_battle.h" #include "k_grandprix.h" +#include "k_specialstage.h" +#include "k_objects.h" #include "k_boss.h" #include "k_color.h" #include "k_director.h" @@ -77,6 +79,9 @@ patch_t *kp_facehighlight[8]; static patch_t *kp_nocontestminimap; static patch_t *kp_spbminimap; +static patch_t *kp_wouldyoustillcatchmeifiwereaworm; +static patch_t *kp_catcherminimap; +static patch_t *kp_emeraldminimap; static patch_t *kp_capsuleminimap[3]; static patch_t *kp_ringsticker[2]; @@ -348,7 +353,13 @@ void K_LoadKartHUDGraphics(void) // Special minimap icons HU_UpdatePatch(&kp_nocontestminimap, "MINIDEAD"); + HU_UpdatePatch(&kp_spbminimap, "SPBMMAP"); + + HU_UpdatePatch(&kp_wouldyoustillcatchmeifiwereaworm, "MINIPROG"); + HU_UpdatePatch(&kp_catcherminimap, "UFOMAP"); + HU_UpdatePatch(&kp_emeraldminimap, "EMEMAP"); + HU_UpdatePatch(&kp_capsuleminimap[0], "MINICAP1"); HU_UpdatePatch(&kp_capsuleminimap[1], "MINICAP2"); HU_UpdatePatch(&kp_capsuleminimap[2], "MINICAP3"); @@ -1662,17 +1673,27 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, U if (modeattacking & ATTACKING_SPB && stplyr->SPBdistance > 0) { UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); - int ybar = 180; - int widthbar = 120; + INT32 ybar = 180; + INT32 widthbar = 120, xbar = 160 - widthbar/2, currentx; + INT32 barflags = V_SNAPTOBOTTOM|V_SLIDEIN; - V_DrawFill(160 - widthbar / 2, ybar, widthbar, 1, 6); - V_DrawMappedPatch(160 + widthbar/2 - 7, ybar - 7, FRACUNIT, faceprefix[stplyr->skin][FACE_MINIMAP], colormap); + V_DrawScaledPatch(xbar, ybar - 2, barflags, kp_wouldyoustillcatchmeifiwereaworm); + + V_DrawMappedPatch(160 + widthbar/2 - 7, ybar - 7, barflags, faceprefix[stplyr->skin][FACE_MINIMAP], colormap); // vibes-based math - int bombxoff = (stplyr->SPBdistance/mapobjectscale - mobjinfo[MT_SPB].radius/FRACUNIT - mobjinfo[MT_PLAYER].radius/FRACUNIT) * 8; - bombxoff = sqrt(bombxoff) - 5; - bombxoff = max(0, min(bombxoff, widthbar)); - V_DrawScaledPatch(160 + widthbar/2 - bombxoff, ybar - 7, FRACUNIT, W_CachePatchName("SPBMMAP", PU_CACHE)); + currentx = (stplyr->SPBdistance/mapobjectscale - mobjinfo[MT_SPB].radius/FRACUNIT - mobjinfo[MT_PLAYER].radius/FRACUNIT) * 8; + if (currentx > 0) + { + currentx = sqrt(currentx); + if (currentx > widthbar) + currentx = widthbar; + } + else + { + currentx = 0; + } + V_DrawScaledPatch(xbar - currentx - 5, ybar - 7, barflags, kp_spbminimap); } } @@ -3024,7 +3045,7 @@ static void K_drawKartWanted(void) } if (battlewanted[0] != -1) - colormap = R_GetTranslationColormap(0, players[battlewanted[0]].skincolor, GTC_CACHE); + colormap = R_GetTranslationColormap(TC_DEFAULT, players[battlewanted[0]].skincolor, GTC_CACHE); V_DrawFixedPatch(basex< 1 ? kp_wantedsplit : kp_wanted), colormap); /*if (basey2) V_DrawFixedPatch(basex< PROGRESSION_BAR_WIDTH) + { + return PROGRESSION_BAR_WIDTH; + } + + if (dist < 0) + { + return 0; + } + + return dist; +} + +static void K_drawKartProgressionMinimapIcon(UINT32 distancetofinish, INT32 hudx, INT32 hudy, INT32 flags, patch_t *icon, UINT8 *colormap) +{ + if (distancetofinish == UINT32_MAX) + return; + + hudx += K_getKartProgressionMinimapDistance(distancetofinish); + + hudx = ((hudx - (SHORT(icon->width)/2))<height)/2))<width)-SHORT(icon->width))/2)<height)-SHORT(icon->height))/2)<width))/2)<height))/2)<width) / 2); - amypos = (amnumypos / FRACUNIT) + (SHORT(minimapinfo.minimap_pic->height) / 2); + amxpos = (amnumxpos / FRACUNIT); + amypos = (amnumypos / FRACUNIT); if (flags & V_NOSCALESTART) { @@ -3634,17 +3696,26 @@ static void K_drawKartMinimapWaypoint(waypoint_t *wp, INT32 hudx, INT32 hudy, IN static void K_drawKartMinimap(void) { patch_t *workingPic; + INT32 i = 0; INT32 x, y; + INT32 minimaptrans = 4; INT32 splitflags = 0; + UINT8 skin = 0; UINT8 *colormap = NULL; + SINT8 localplayers[MAXSPLITSCREENPLAYERS]; SINT8 numlocalplayers = 0; + mobj_t *mobj, *next; // for SPB drawing (or any other item(s) we may wanna draw, I dunno!) + fixed_t interpx, interpy; + boolean doprogressionbar = false; + boolean dofade = false, doencore = false; + // Draw the HUD only when playing in a level. // hu_stuff needs this, unlike st_stuff. if (gamestate != GS_LEVEL) @@ -3655,16 +3726,58 @@ static void K_drawKartMinimap(void) if (stplyr != &players[displayplayers[0]]) return; - if (minimapinfo.minimap_pic == NULL) + if (specialstageinfo.valid == true) { - return; // no pic, just get outta here + // future work: maybe make this a unique gametype rule? + // I would do this now if it were easier to get the + // distancetofinish for an arbitrary object. ~toast 070423 + doprogressionbar = true; } - if (r_splitscreen < 2) // 1/2P right aligned + if (doprogressionbar == false) { - splitflags = (V_SLIDEIN|V_SNAPTORIGHT); + if (minimapinfo.minimap_pic == NULL) + { + return; // no pic, just get outta here + } + + else if (r_splitscreen < 2) // 1/2P right aligned + { + splitflags = (V_SLIDEIN|V_SNAPTORIGHT); + } + else if (r_splitscreen == 3) // 4P splits + { + dofade = true; + } + // 3P lives in the middle of the bottom right + // viewport and shouldn't fade in OR slide + + x = MINI_X; + y = MINI_Y; + + workingPic = minimapinfo.minimap_pic; + + doencore = encoremode; } - else if (r_splitscreen == 3) // 4P centered + else + { + x = BASEVIDWIDTH/2; + + if (r_splitscreen > 0) + { + y = BASEVIDHEIGHT/2; + dofade = true; + } + else + { + y = 180; + splitflags = (V_SLIDEIN|V_SNAPTOBOTTOM); + } + + workingPic = kp_wouldyoustillcatchmeifiwereaworm; + } + + if (dofade) { const tic_t length = TICRATE/2; @@ -3672,34 +3785,49 @@ static void K_drawKartMinimap(void) return; if (lt_exitticker < length) minimaptrans = (((INT32)lt_exitticker)*minimaptrans)/((INT32)length); + + if (!minimaptrans) + return; } - // 3P lives in the middle of the bottom right player and shouldn't fade in OR slide - - if (!minimaptrans) - return; - - x = MINI_X - (SHORT(minimapinfo.minimap_pic->width)/2); - y = MINI_Y - (SHORT(minimapinfo.minimap_pic->height)/2); minimaptrans = ((10-minimaptrans)<width), y, splitflags|minimaptrans|V_FLIP, minimapinfo.minimap_pic); + if (doencore) + { + V_DrawScaledPatch( + x + (SHORT(workingPic->width)/2), + y - (SHORT(workingPic->height)/2), + splitflags|minimaptrans|V_FLIP, + workingPic + ); + } else - V_DrawScaledPatch(x, y, splitflags|minimaptrans, minimapinfo.minimap_pic); + { + V_DrawScaledPatch( + x - (SHORT(workingPic->width)/2), + y - (SHORT(workingPic->height)/2), + splitflags|minimaptrans, + workingPic + ); + } // most icons will be rendered semi-ghostly. splitflags |= V_HUDTRANSHALF; // let offsets transfer to the heads, too! - if (encoremode) - x += SHORT(minimapinfo.minimap_pic->leftoffset); + if (doencore) + x += SHORT(workingPic->leftoffset); else - x -= SHORT(minimapinfo.minimap_pic->leftoffset); - y -= SHORT(minimapinfo.minimap_pic->topoffset); + x -= SHORT(workingPic->leftoffset); + y -= SHORT(workingPic->topoffset); + + if (doprogressionbar == true) + { + x -= PROGRESSION_BAR_WIDTH/2; + } // Draw the super item in Battle - if ((gametyperules & GTR_OVERTIME) && battleovertime.enabled) + if (doprogressionbar == false && (gametyperules & GTR_OVERTIME) && battleovertime.enabled) { if (battleovertime.enabled >= 10*TICRATE || (battleovertime.enabled & 1)) { @@ -3717,7 +3845,7 @@ static void K_drawKartMinimap(void) localplayers[i] = -1; // Player's tiny icons on the Automap. (drawn opposite direction so player 1 is drawn last in splitscreen) - if (ghosts) + if (ghosts && doprogressionbar == true) // future work: show ghosts on progression bar { demoghost *g = ghosts; while (g) @@ -3742,19 +3870,15 @@ static void K_drawKartMinimap(void) K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap); g = g->next; } - - if (!stplyr->mo || stplyr->spectator || stplyr->exiting) - return; - - localplayers[numlocalplayers++] = stplyr-players; } - else + { for (i = MAXPLAYERS-1; i >= 0; i--) { if (!playeringame[i]) continue; - if (!players[i].mo || players[i].spectator || !players[i].mo->skin || players[i].exiting) + if (!players[i].mo || players[i].spectator || !players[i].mo->skin + || (doprogressionbar == false && players[i].exiting)) continue; // This player is out of the game! @@ -3781,7 +3905,7 @@ static void K_drawKartMinimap(void) if (mobj->health <= 0 && (players[i].pflags & PF_NOCONTEST)) { workingPic = kp_nocontestminimap; - R_GetTranslationColormap(0, mobj->color, GTC_CACHE); + colormap = R_GetTranslationColormap(TC_DEFAULT, mobj->color, GTC_CACHE); if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer)) mobj = mobj->tracer; @@ -3803,22 +3927,33 @@ static void K_drawKartMinimap(void) colormap = NULL; } - interpx = R_InterpolateFixed(mobj->old_x, mobj->x); - interpy = R_InterpolateFixed(mobj->old_y, mobj->y); - - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, faceprefix[skin][FACE_MINIMAP], colormap); - - // Target reticule - if (((gametyperules & GTR_CIRCUIT) && players[i].position == spbplace) - || ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[i]))) + if (doprogressionbar == false) { - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL); + interpx = R_InterpolateFixed(mobj->old_x, mobj->x); + interpy = R_InterpolateFixed(mobj->old_y, mobj->y); + + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap); + + // Target reticule + if (((gametyperules & GTR_CIRCUIT) && players[i].position == spbplace) + || ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[i]))) + { + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL); + } + } + else + { + K_drawKartProgressionMinimapIcon(players[i].distancetofinish, x, y, splitflags, workingPic, colormap); } } } // draw minimap-pertinent objects - for (mobj = trackercap; mobj; mobj = next) + if (doprogressionbar == true) + { + // future work: support these specific objects on this + } + else for (mobj = trackercap; mobj; mobj = next) { next = mobj->itnext; @@ -3872,7 +4007,34 @@ static void K_drawKartMinimap(void) } // ...but first, any boss targets. - if (bossinfo.valid == true) + if (doprogressionbar == true) + { + if (specialstageinfo.valid == true) + { + UINT32 distancetofinish = K_GetSpecialUFODistance(); + if (distancetofinish > 0 && specialstageinfo.ufo != NULL && P_MobjWasRemoved(specialstageinfo.ufo) == false) + { + colormap = NULL; + if (specialstageinfo.ufo->health > 1) + { + workingPic = kp_catcherminimap; + } + else + { + workingPic = kp_emeraldminimap; + if (specialstageinfo.ufo->color) + { + colormap = R_GetTranslationColormap(TC_DEFAULT, specialstageinfo.ufo->color, GTC_CACHE); + } + } + + K_drawKartProgressionMinimapIcon(distancetofinish, x, y, splitflags, workingPic, colormap); + } + } + + // future work: support boss minimap icons on the progression bar + } + else if (bossinfo.valid == true) { for (i = 0; i < NUMWEAKSPOTS; i++) { @@ -3912,7 +4074,7 @@ static void K_drawKartMinimap(void) if (mobj->health <= 0 && (players[localplayers[i]].pflags & PF_NOCONTEST)) { workingPic = kp_nocontestminimap; - R_GetTranslationColormap(0, mobj->color, GTC_CACHE); + colormap = R_GetTranslationColormap(TC_DEFAULT, mobj->color, GTC_CACHE); if (mobj->tracer && !P_MobjWasRemoved(mobj->tracer)) mobj = mobj->tracer; @@ -3934,20 +4096,27 @@ static void K_drawKartMinimap(void) colormap = NULL; } - interpx = R_InterpolateFixed(mobj->old_x, mobj->x); - interpy = R_InterpolateFixed(mobj->old_y, mobj->y); - - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap); - - // Target reticule - if (((gametyperules & GTR_CIRCUIT) && players[localplayers[i]].position == spbplace) - || ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[localplayers[i]]))) + if (doprogressionbar == false) { - K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL); + interpx = R_InterpolateFixed(mobj->old_x, mobj->x); + interpy = R_InterpolateFixed(mobj->old_y, mobj->y); + + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, workingPic, colormap); + + // Target reticule + if (((gametyperules & GTR_CIRCUIT) && players[localplayers[i]].position == spbplace) + || ((gametyperules & (GTR_BOSS|GTR_POINTLIMIT)) == GTR_POINTLIMIT && K_IsPlayerWanted(&players[localplayers[i]]))) + { + K_drawKartMinimapIcon(interpx, interpy, x, y, splitflags, kp_wantedreticle, NULL); + } + } + else + { + K_drawKartProgressionMinimapIcon(players[i].distancetofinish, x, y, splitflags, workingPic, colormap); } } - if (cv_kartdebugwaypoints.value != 0) + if (doprogressionbar == false && cv_kartdebugwaypoints.value != 0) { size_t idx; @@ -3968,6 +4137,8 @@ static void K_drawKartMinimap(void) } } +#undef PROGRESSION_BAR_WIDTH + static void K_drawKartFinish(boolean finish) { INT32 timer, minsplitstationary, pnum = 0, splitflags = V_SPLITSCREEN; @@ -4579,7 +4750,7 @@ static void K_drawInput(void) else { UINT8 *colormap; - colormap = R_GetTranslationColormap(0, stplyr->skincolor, GTC_CACHE); + colormap = R_GetTranslationColormap(TC_DEFAULT, stplyr->skincolor, GTC_CACHE); V_DrawFixedPatch(x<old_x, mobj->x), - R_InterpolateFixed(mobj->old_y, mobj->y), - R_InterpolateFixed(mobj->old_z, mobj->z) + (mobj->height >> 1), + R_InterpolateFixed(mobj->old_x, mobj->x) + mobj->sprxoff, + R_InterpolateFixed(mobj->old_y, mobj->y) + mobj->spryoff, + R_InterpolateFixed(mobj->old_z, mobj->z) + mobj->sprzoff + (mobj->height >> 1), }; targetList.push_back({mobj, pos, R_PointToDist2(origin->x, origin->y, pos.x, pos.y)}); diff --git a/src/k_kart.c b/src/k_kart.c index dda407cd2..80b268cb5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8589,11 +8589,16 @@ void K_UpdateDistanceFromFinishLine(player_t *const player) } // nextwaypoint is now the waypoint that is in front of us - if (player->exiting || player->spectator) + if ((player->exiting && !(player->pflags & PF_NOCONTEST)) || player->spectator) { // Player has finished, we don't need to calculate this player->distancetofinish = 0U; } + else if (player->pflags & PF_NOCONTEST) + { + // We also don't need to calculate this, but there's also no need to destroy the data... + ; + } else if ((player->currentwaypoint != NULL) && (player->nextwaypoint != NULL) && (finishline != NULL)) { const boolean useshortcuts = false; diff --git a/src/k_specialstage.h b/src/k_specialstage.h index 6b20ca220..329d7f99f 100644 --- a/src/k_specialstage.h +++ b/src/k_specialstage.h @@ -24,8 +24,10 @@ extern struct specialstageinfo { boolean valid; ///< If true, then data in this struct is valid - UINT32 beamDist; ///< Where the exit beam is. mobj_t *ufo; ///< The Chaos Emerald capsule. + UINT32 maxDist; ///< The distance from one end of the track to another. + + UINT32 beamDist; ///< Where the exit beam is. } specialstageinfo; /*-------------------------------------------------- diff --git a/src/objects/ufo.c b/src/objects/ufo.c index a632986d0..3e85b59e1 100644 --- a/src/objects/ufo.c +++ b/src/objects/ufo.c @@ -54,6 +54,8 @@ enum { UFO_PIECE_TYPE_POD, + UFO_PIECE_TYPE_GLASS, + UFO_PIECE_TYPE_GLASS_UNDER, UFO_PIECE_TYPE_ARM, UFO_PIECE_TYPE_STEM, }; @@ -345,6 +347,7 @@ static void UFOMove(mobj_t *ufo) ufo->momx = 0; ufo->momy = 0; ufo->momz = ufo_speed(ufo); + ufo_distancetofinish(ufo) = 0; return; } @@ -514,6 +517,90 @@ void Obj_SpecialUFOThinker(mobj_t *ufo) } } +// The following is adapted from monitor.c for UFO Catcher damage +// I couldn't just exose the relevant things via k_object.h +// because they're *just* too specific to Monitors... ~toast 070423 + +#define shard_can_roll(o) ((o)->extravalue1) + +static inline boolean +can_shard_state_roll (statenum_t state) +{ + switch (state) + { + case S_MONITOR_BIG_SHARD: + case S_MONITOR_SMALL_SHARD: + return true; + + default: + return false; + } +} + +static void +spawn_shard +( mobj_t * part, + statenum_t state) +{ + mobj_t *ufo = ufo_piece_owner(part); + + // These divisions and multiplications are done on the + // offsets to give bigger increments of randomness. + + const fixed_t h = FixedDiv( + ufo->height, ufo->scale); + + const UINT16 rad = (ufo->radius / ufo->scale) / 4; + const UINT16 tall = (h / FRACUNIT); + + mobj_t *p = P_SpawnMobjFromMobj(ufo, + P_RandomRange(PR_ITEM_DEBRIS, -(rad), rad) * 8 * FRACUNIT, + P_RandomRange(PR_ITEM_DEBRIS, -(rad), rad) * 8 * FRACUNIT, + P_RandomKey(PR_ITEM_DEBRIS, tall + 1) * 4 * FRACUNIT, + MT_MONITOR_SHARD); + + P_SetScale(p, (p->destscale = p->destscale * 3)); + + angle_t th = R_PointToAngle2(ufo->x, ufo->y, p->x, p->y); + + th -= P_RandomKey(PR_ITEM_DEBRIS, ANGLE_45) - ANGLE_22h; + + p->hitlag = 0; + + P_Thrust(p, th, 6 * p->scale); + p->momz = P_RandomRange(PR_ITEM_DEBRIS, 3, 10) * p->scale; + + P_SetMobjState(p, state); + + shard_can_roll(p) = can_shard_state_roll(state); + + if (shard_can_roll(p)) + { + p->rollangle = P_Random(PR_ITEM_DEBRIS); + } + + if (P_RandomChance(PR_ITEM_DEBRIS, FRACUNIT/2)) + { + p->renderflags |= RF_DONTDRAW; + } +} + +static void +spawn_debris (mobj_t *part) +{ + mobj_t *ufo = ufo_piece_owner(part); + + INT32 i; + + for (i = ufo->health; + i <= mobjinfo[ufo->type].spawnhealth; i += 5) + { + spawn_shard(part, S_MONITOR_BIG_SHARD); + spawn_shard(part, S_MONITOR_SMALL_SHARD); + spawn_shard(part, S_MONITOR_TWINKLE); + } +} + static void UFOCopyHitlagToPieces(mobj_t *ufo) { mobj_t *piece = NULL; @@ -523,6 +610,12 @@ static void UFOCopyHitlagToPieces(mobj_t *ufo) { piece->hitlag = ufo->hitlag; piece->eflags = (piece->eflags & ~MFE_DAMAGEHITLAG) | (ufo->eflags & MFE_DAMAGEHITLAG); + + if (ufo_piece_type(piece) == UFO_PIECE_TYPE_GLASS) + { + spawn_debris (piece); + } + piece = ufo_piece_next(piece); } } @@ -543,6 +636,8 @@ static void UFOKillPiece(mobj_t *piece) switch (ufo_piece_type(piece)) { + case UFO_PIECE_TYPE_GLASS: + case UFO_PIECE_TYPE_GLASS_UNDER: case UFO_PIECE_TYPE_STEM: { piece->tics = 1; @@ -686,15 +781,16 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN // Speed up on damage! ufo_speed(ufo) += addSpeed; + ufo->health = max(1, ufo->health - damage); + K_SetHitLagForObjects(ufo, inflictor, (damage / 3) + 2, true); UFOCopyHitlagToPieces(ufo); - if (damage >= ufo->health - 1) + if (ufo->health == 1) { // Destroy the UFO parts, and make the emerald collectible! UFOKillPieces(ufo); - ufo->health = 1; ufo->flags = (ufo->flags & ~MF_SHOOTABLE) | (MF_SPECIAL|MF_PICKUPFROMBELOW); ufo->shadowscale = FRACUNIT/3; @@ -711,7 +807,6 @@ boolean Obj_SpecialUFODamage(mobj_t *ufo, mobj_t *inflictor, mobj_t *source, UIN S_StartSound(ufo, sfx_clawht); S_StopSoundByID(ufo, sfx_clawzm); P_StartQuake(64<health -= damage; return true; } @@ -793,18 +888,25 @@ void Obj_UFOPieceThink(mobj_t *piece) return; } - piece->destscale = 3 * ufo->destscale / 2; piece->scalespeed = ufo->scalespeed; switch (ufo_piece_type(piece)) { case UFO_PIECE_TYPE_POD: { + piece->destscale = 3 * ufo->destscale / 2; UFOMoveTo(piece, ufo->x, ufo->y, ufo->z + (132 * piece->scale)); if (S_SoundPlaying(ufo, sfx_clawzm) && ufo_speed(ufo) > 70*FRACUNIT) SpawnUFOSpeedLines(piece); break; } + case UFO_PIECE_TYPE_GLASS: + case UFO_PIECE_TYPE_GLASS_UNDER: + { + piece->destscale = 5 * ufo->destscale / 3; + UFOMoveTo(piece, ufo->x, ufo->y, ufo->z); + break; + } case UFO_PIECE_TYPE_ARM: { fixed_t dis = (88 * piece->scale); @@ -812,6 +914,7 @@ void Obj_UFOPieceThink(mobj_t *piece) fixed_t x = ufo->x - FixedMul(dis, FINECOSINE(piece->angle >> ANGLETOFINESHIFT)); fixed_t y = ufo->y - FixedMul(dis, FINESINE(piece->angle >> ANGLETOFINESHIFT)); + piece->destscale = 3 * ufo->destscale / 2; UFOMoveTo(piece, x, y, ufo->z + (24 * piece->scale)); piece->angle -= FixedMul(ANG2, FixedDiv(ufo_speed(ufo), UFO_BASE_SPEED)); @@ -822,6 +925,7 @@ void Obj_UFOPieceThink(mobj_t *piece) fixed_t stemZ = ufo->z + (294 * piece->scale); fixed_t sc = FixedDiv(FixedDiv(ufo->ceilingz - stemZ, piece->scale), 15 * FRACUNIT); + piece->destscale = 3 * ufo->destscale / 2; UFOMoveTo(piece, ufo->x, ufo->y, stemZ); if (sc > 0) { @@ -901,19 +1005,51 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) ufo = P_SpawnMobj(start->mobj->x, start->mobj->y, start->mobj->z, MT_SPECIAL_UFO); ufo_waypoint(ufo) = (INT32)K_GetWaypointHeapIndex(start); UFOUpdateDistanceToFinish(ufo); + specialstageinfo.maxDist = ufo_distancetofinish(ufo); } ufo_speed(ufo) = FixedMul(UFO_START_SPEED, K_GetKartGameSpeedScalar(gamespeed)); - // TODO: Adjustable Special Stage emerald color - ufo->color = SKINCOLOR_CHAOSEMERALD1; + // Adjustable Special Stage emerald color/shape + { + overlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); - overlay = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_OVERLAY); - P_SetTarget(&overlay->target, ufo); - overlay->color = ufo->color; + ufo->color = SKINCOLOR_CHAOSEMERALD1; + i = P_GetNextEmerald(); + if (i > 0) + { + ufo->color += (i - 1) % 7; + if (i > 7) + { + // Super Emeralds + P_SetMobjState(ufo, S_SUPEREMERALD1); + P_SetMobjState(overlay, S_SUPEREMERALD_UNDER); + } + else + { + // Chaos Emerald + P_SetMobjState(ufo, S_CHAOSEMERALD1); + P_SetMobjState(overlay, S_CHAOSEMERALD_UNDER); + } + } + else + { + // Prize -- todo, currently using standard Emerald + P_SetMobjState(ufo, S_CHAOSEMERALD1); + P_SetMobjState(overlay, S_CHAOSEMERALD_UNDER); + } - // TODO: Super Emeralds / Chaos Rings - P_SetMobjState(overlay, S_CHAOSEMERALD_UNDER); + if (P_MobjWasRemoved(ufo)) // uh oh ! + { + // Attempted crash prevention with custom SOC + return NULL; + } + + overlay->color = ufo->color; + P_SetTarget(&overlay->target, ufo); + + ufo->sprzoff = 32 * mapobjectscale; + } // Create UFO pieces. // First: UFO center. @@ -930,6 +1066,39 @@ static mobj_t *InitSpecialUFO(waypoint_t *start) P_SetTarget(&ufo_pieces(ufo), piece); prevPiece = piece; + // Next, the glass ball. + { + piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); + P_SetTarget(&ufo_piece_owner(piece), ufo); + + P_SetMobjState(piece, S_SPECIAL_UFO_GLASS); + ufo_piece_type(piece) = UFO_PIECE_TYPE_GLASS; + + /*overlay = P_SpawnMobjFromMobj(piece, 0, 0, 0, MT_OVERLAY); + P_SetTarget(&overlay->target, piece); + P_SetMobjState(overlay, S_SPECIAL_UFO_GLASS_UNDER); + overlay->dispoffset = -20;*/ + + P_SetTarget(&ufo_piece_next(prevPiece), piece); + P_SetTarget(&ufo_piece_prev(piece), prevPiece); + prevPiece = piece; + } + + // This SHOULD have been an MT_OVERLAY... but it simply doesn't + // draw-order stack with the Emerald correctly any other way. + { + piece = P_SpawnMobjFromMobj(ufo, 0, 0, 0, MT_SPECIAL_UFO_PIECE); + P_SetTarget(&ufo_piece_owner(piece), ufo); + + P_SetMobjState(piece, S_SPECIAL_UFO_GLASS_UNDER); + ufo_piece_type(piece) = UFO_PIECE_TYPE_GLASS_UNDER; + piece->dispoffset = -2; + + P_SetTarget(&ufo_piece_next(prevPiece), piece); + P_SetTarget(&ufo_piece_prev(piece), prevPiece); + prevPiece = piece; + } + // Add the catcher arms. for (i = 0; i < UFO_NUMARMS; i++) { diff --git a/src/p_user.c b/src/p_user.c index a9a2f24a4..e05d1a0f7 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -313,15 +313,29 @@ boolean P_PlayerMoving(INT32 pnum) // UINT8 P_GetNextEmerald(void) { - INT16 mapnum = gamemap-1; + cupheader_t *cup = NULL; - if (mapnum > nummapheaders || !mapheaderinfo[mapnum]) + if (grandprixinfo.gp == true) + { + cup = grandprixinfo.cup; + } + + if (cup == NULL) + { + INT16 mapnum = gamemap-1; + + if (mapnum < nummapheaders && mapheaderinfo[mapnum]) + { + cup = mapheaderinfo[mapnum]->cup; + } + } + + if (cup == NULL) + { return 0; + } - if (!mapheaderinfo[mapnum]->cup || mapheaderinfo[mapnum]->cup->cachedlevels[CUPCACHE_SPECIAL] != mapnum) - return 0; - - return mapheaderinfo[mapnum]->cup->emeraldnum; + return cup->emeraldnum; } //