diff --git a/src/k_battle.c b/src/k_battle.c index 20b0f47e7..e64fc5a9a 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -439,14 +439,12 @@ void K_RunPaperItemSpawners(void) if (pcount > 0) { #define MAXITEM 64 - UINT8 item = 0; mobj_t *spotList[MAXITEM]; - boolean spotUsed[MAXITEM]; + UINT8 spotMap[MAXITEM]; + UINT8 spotCount = 0, spotBackup = 0; INT16 starti = 0; - memset(spotUsed, false, sizeof(spotUsed)); - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) { if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) @@ -454,21 +452,23 @@ void K_RunPaperItemSpawners(void) mo = (mobj_t *)th; - if (mo->type == MT_PAPERITEMSPOT) - { - if (item >= MAXITEM) - continue; - - spotList[item] = mo; - item++; - } - else if (mo->type == MT_EMERALD) + if (mo->type == MT_EMERALD) { emeraldsSpawned |= mo->extravalue1; } + + if (mo->type != MT_PAPERITEMSPOT) + continue; + + if (spotCount >= MAXITEM) + continue; + + spotList[spotCount] = mo; + spotMap[spotCount] = spotCount; + spotCount++; } - if (item <= 0) + if (spotCount <= 0) { return; } @@ -488,24 +488,34 @@ void K_RunPaperItemSpawners(void) } } - for (i = starti; i < min(item + starti, pcount); i++) + //CONS_Printf("leveltime = %d ", leveltime); + + spotBackup = spotCount; + for (i = starti; i < pcount; i++) { - UINT8 r = P_RandomKey(item); - UINT8 recursion = 0; + UINT8 r = 0, key = 0; mobj_t *drop = NULL; SINT8 flip = 1; - while (spotUsed[r] == true) + if (spotCount == 0) { - r = P_RandomKey(item); - - if ((recursion++) > MAXITEM) - { - // roll with it anyway I guess - break; - } + // all are accessible again + spotCount = spotBackup; } + if (spotCount == 1) + { + key = 0; + } + else + { + key = P_RandomKey(spotCount); + } + + r = spotMap[key]; + + //CONS_Printf("[%d %d %d] ", i, key, r); + flip = P_MobjFlip(spotList[r]); // When -1, we're spawning a Chaos Emerald. @@ -537,8 +547,23 @@ void K_RunPaperItemSpawners(void) } K_FlipFromObject(drop, spotList[r]); - spotUsed[r] = true; + + spotCount--; + if (key != spotCount) + { + // So the core theory of what's going on is that we keep every + // available option at the front of the array, so we don't have + // to skip over any gaps or do recursion to avoid doubles. + // But because spotCount can be reset in the case of a low + // quanitity of item spawnpoints in a map, we still need every + // entry in the array, even outside of the "visible" range. + // A series of swaps allows us to adhere to both constraints. + // -toast 22/03/22 (semipalindromic!) + spotMap[key] = spotMap[spotCount]; + spotMap[spotCount] = r; // was set to spotMap[key] previously + } } + //CONS_Printf("\n"); } } } diff --git a/src/k_hud.c b/src/k_hud.c index 2035a3a85..206da4e04 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -1088,6 +1088,7 @@ static void K_initKartHUD(void) } } +// see also MT_PLAYERARROW mobjthinker in p_mobj.c static void K_drawKartItem(void) { // ITEM_X = BASEVIDWIDTH-50; // 270 @@ -1181,15 +1182,15 @@ static void K_drawKartItem(void) localpatch = kp_landmine[offset]; //localcolor = SKINCOLOR_JET; break; - case 16: // Land Mine + case 16: // Drop Target localpatch = kp_droptarget[offset]; //localcolor = SKINCOLOR_LIME; break; - /*case 15: // Pogo Spring + /*case 17: // Pogo Spring localpatch = kp_pogospring[offset]; localcolor = SKINCOLOR_TANGERINE; break; - case 16: // Kitchen Sink + case 18: // Kitchen Sink localpatch = kp_kitchensink[offset]; localcolor = SKINCOLOR_STEEL; break;*/ @@ -1782,7 +1783,7 @@ static boolean K_drawKartPositionFaces(void) // FACE_X = 15; // 15 // FACE_Y = 72; // 72 - INT32 Y = FACE_Y+9; // +9 to offset where it's being drawn if there are more than one + INT32 Y = FACE_Y-9; // -9 to offset where it's being drawn if there are more than one INT32 i, j, ranklines, strank = -1; boolean completed[MAXPLAYERS]; INT32 rankplayer[MAXPLAYERS]; @@ -1838,29 +1839,32 @@ static boolean K_drawKartPositionFaces(void) } if (ranklines < 5) - Y -= (9*ranklines); + Y += (9*ranklines); else - Y -= (9*5); + Y += (9*5); + + ranklines--; + i = ranklines; if (gametype == GT_BATTLE || strank <= 2) // too close to the top, or playing battle, or a spectator? would have had (strank == -1) called out, but already caught by (strank <= 2) { - i = 0; - if (ranklines > 5) // could be both... - ranklines = 5; + if (i > 4) // could be both... + i = 4; + ranklines = 0; } - else if (strank+3 > ranklines) // too close to the bottom? + else if (strank+2 >= ranklines) // too close to the bottom? { - i = ranklines - 5; - if (i < 0) - i = 0; + ranklines -= 4; + if (ranklines < 0) + ranklines = 0; } else { - i = strank-2; - ranklines = strank+3; + i = strank+2; + ranklines = strank-2; } - for (; i < ranklines; i++) + for (; i >= ranklines; i--) { if (!playeringame[rankplayer[i]]) continue; if (players[rankplayer[i]].spectator) continue; @@ -1920,7 +1924,7 @@ static boolean K_drawKartPositionFaces(void) V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facenum[pos]); } - Y += 18; + Y -= 18; } return false; @@ -2143,20 +2147,28 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN INT32 i, rightoffset = 240; const UINT8 *colormap; INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; - int y2; + int basey = y, basex = x, y2; //this function is designed for 9 or less score lines only //I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up V_DrawFill(1-duptweak, 26, dupadjust-2, 1, 0); // Draw a horizontal line because it looks nice! - if (scorelines > 8) + + scorelines--; + if (scorelines >= 8) { V_DrawFill(160, 26, 1, 147, 0); // Draw a vertical line to separate the two sides. V_DrawFill(1-duptweak, 173, dupadjust-2, 1, 0); // And a horizontal line near the bottom. rightoffset = (BASEVIDWIDTH/2) - 4 - x; + x = (BASEVIDWIDTH/2) + 4; + y += 18*(scorelines-8); + } + else + { + y += 18*scorelines; } - for (i = 0; i < scorelines; i++) + for (i = scorelines; i >= 0; i--) { char strtime[MAXPLAYERNAME+1]; @@ -2201,7 +2213,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN y2 += SHORT (kp_alagles[0]->height) + 1; } - if (scorelines > 8) + if (scorelines >= 8) V_DrawThinString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); else V_DrawString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); @@ -2244,7 +2256,7 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN if (gametype == GT_RACE) { #define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time)) - if (scorelines > 8) + if (scorelines >= 8) { if (players[tab[i].num].exiting) V_DrawRightAlignedThinString(x+rightoffset, y-1, hilicol|V_6WIDTHSPACE, timestring(players[tab[i].num].realtime)); @@ -2267,11 +2279,11 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN else V_DrawRightAlignedString(x+rightoffset, y, 0, va("%u", tab[i].count)); - y += 18; - if (i == 7) + y -= 18; + if (i == 8) { - y = 33; - x = (BASEVIDWIDTH/2) + 4; + y = basey + 7*18; + x = basex; } } } diff --git a/src/k_kart.c b/src/k_kart.c index 3bfac8732..58cb6fe31 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -613,7 +613,7 @@ INT32 K_KartGetItemOdds( if (gametype == GT_BATTLE) { - I_Assert(pos < 6); // DO NOT allow positions past the bounds of the table + I_Assert(pos < 2); // DO NOT allow positions past the bounds of the table newodds = K_KartItemOddsBattle[item-1][pos]; } else diff --git a/src/p_inter.c b/src/p_inter.c index ce97f51d0..aceb8336c 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -112,10 +112,12 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon) if (player->exiting || mapreset || (player->pflags & PF_ELIMINATED)) return false; + if ((gametyperules & GTR_BUMPERS) // No bumpers in Match #ifndef OTHERKARMAMODES - if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) // No bumpers in Match - return false; + && !weapon #endif + && player->bumpers <= 0) + return false; if (weapon) { @@ -274,9 +276,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) P_KillMobj(special, toucher, toucher, DMG_NORMAL); break; case MT_SPHEREBOX: - if (player->bumpers <= 0) + if (!P_CanPickupItem(player, 0)) return; + special->momx = special->momy = special->momz = 0; P_SetTarget(&special->target, toucher); P_KillMobj(special, toucher, toucher, DMG_NORMAL); break; diff --git a/src/p_mobj.c b/src/p_mobj.c index 8987612b1..3a4cd1751 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -5778,6 +5778,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) } break; + // see also K_drawKartItem in k_hud.c case MT_PLAYERARROW: if (mobj->target && mobj->target->health && mobj->target->player && !mobj->target->player->spectator @@ -5799,7 +5800,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->x = mobj->target->x; mobj->y = mobj->target->y; - mobj->angle = R_PointToAngle(mobj->x, mobj->y) + ANGLE_90; // literally only happened because i wanted to ^L^R the SPR_ITEM's + P_InitAngle (mobj, R_PointToAngle(mobj->x, mobj->y) + ANGLE_90); // literally only happened because i wanted to ^L^R the SPR_ITEM's if (!r_splitscreen && players[displayplayers[0]].mo) { @@ -5852,7 +5853,79 @@ static void P_MobjSceneryThink(mobj_t *mobj) { P_SetMobjState(mobj, S_PLAYERARROW_BOX); mobj->tracer->sprite = SPR_ITEM; - mobj->tracer->frame = FF_FULLBRIGHT|(((mobj->target->player->itemroulette % (13*3)) / 3) + 1); + switch((mobj->target->player->itemroulette % (16*3)) / 3) + { + // Each case is handled in threes, to give three frames of in-game time to see the item on the roulette + case 0: // Sneaker + mobj->tracer->frame = KITEM_SNEAKER; + //localcolor = SKINCOLOR_RASPBERRY; + break; + case 1: // Banana + mobj->tracer->frame = KITEM_BANANA; + //localcolor = SKINCOLOR_YELLOW; + break; + case 2: // Orbinaut + mobj->tracer->frame = KITEM_ORBINAUT; + //localcolor = SKINCOLOR_STEEL; + break; + case 3: // Mine + mobj->tracer->frame = KITEM_MINE; + //localcolor = SKINCOLOR_JET; + break; + case 4: // Grow + mobj->tracer->frame = KITEM_GROW; + //localcolor = SKINCOLOR_TEAL; + break; + case 5: // Hyudoro + mobj->tracer->frame = KITEM_HYUDORO; + //localcolor = SKINCOLOR_STEEL; + break; + case 6: // Rocket Sneaker + mobj->tracer->frame = KITEM_ROCKETSNEAKER; + //localcolor = SKINCOLOR_TANGERINE; + break; + case 7: // Jawz + mobj->tracer->frame = KITEM_JAWZ; + //localcolor = SKINCOLOR_JAWZ; + break; + case 8: // Self-Propelled Bomb + mobj->tracer->frame = KITEM_SPB; + //localcolor = SKINCOLOR_JET; + break; + case 9: // Shrink + mobj->tracer->frame = KITEM_SHRINK; + //localcolor = SKINCOLOR_ORANGE; + break; + case 10: // Invincibility + mobj->tracer->frame = KITEM_INVINCIBILITY; + //localcolor = SKINCOLOR_GREY; + break; + case 11: // Eggman Monitor + mobj->tracer->frame = KITEM_EGGMAN; + //localcolor = SKINCOLOR_ROSE; + break; + case 12: // Ballhog + mobj->tracer->frame = KITEM_BALLHOG; + //localcolor = SKINCOLOR_LILAC; + break; + case 13: // Thunder Shield + mobj->tracer->frame = KITEM_THUNDERSHIELD; + //localcolor = SKINCOLOR_CYAN; + break; + case 14: // Super Ring + mobj->tracer->frame = KITEM_SUPERRING; + //localcolor = SKINCOLOR_GOLD; + break; + case 15: // Land Mine + mobj->tracer->frame = KITEM_LANDMINE; + //localcolor = SKINCOLOR_JET; + break; + case 16: // Drop Target + mobj->tracer->frame = KITEM_DROPTARGET; + //localcolor = SKINCOLOR_LIME; + break; + } + mobj->tracer->frame |= FF_FULLBRIGHT; mobj->tracer->renderflags &= ~RF_DONTDRAW; } else if (mobj->target->player->stealingtimer < 0) @@ -10361,6 +10434,7 @@ void P_RemoveMobj(mobj_t *mobj) || mobj->type == MT_BLUESPHERE) && !(mobj->flags2 & MF2_DONTRESPAWN)) { + //CONS_Printf("added to queue at tic %d\n", leveltime); itemrespawnque[iquehead] = mobj->spawnpoint; itemrespawntime[iquehead] = leveltime; iquehead = (iquehead+1)&(ITEMQUESIZE-1); @@ -10848,6 +10922,8 @@ void P_RespawnSpecials(void) if (mthing) P_SpawnMapThing(mthing); + //CONS_Printf("respawn happened on tic %d, irt %d, t %d\n", leveltime, itemrespawntime[iquetail], time); + // pull it from the que iquetail = (iquetail+1)&(ITEMQUESIZE-1); } diff --git a/src/p_saveg.c b/src/p_saveg.c index d1ca860de..18cef0e94 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -332,7 +332,7 @@ static void P_NetArchivePlayers(void) WRITEINT16(save_p, players[i].karmadelay); WRITEUINT32(save_p, players[i].overtimekarma); WRITEINT16(save_p, players[i].spheres); - WRITEINT16(save_p, players[i].spheredigestion); + WRITEUINT32(save_p, players[i].spheredigestion); WRITESINT8(save_p, players[i].glanceDir); WRITEUINT8(save_p, players[i].tripWireState); @@ -598,7 +598,7 @@ static void P_NetUnArchivePlayers(void) players[i].karmadelay = READINT16(save_p); players[i].overtimekarma = READUINT32(save_p); players[i].spheres = READINT16(save_p); - players[i].spheredigestion = READINT16(save_p); + players[i].spheredigestion = READUINT32(save_p); players[i].glanceDir = READSINT8(save_p); players[i].tripWireState = READUINT8(save_p); @@ -1662,7 +1662,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) if (mobj->type == MT_HOOPCENTER && mobj->threshold == 4242) return; - if (mobj->spawnpoint && mobj->info->doomednum != -1) + if (mobj->spawnpoint) { // spawnpoint is not modified but we must save it since it is an identifier diff = MD_SPAWNPOINT; @@ -1847,8 +1847,12 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) size_t z; for (z = 0; z < nummapthings; z++) - if (&mapthings[z] == mobj->spawnpoint) - WRITEUINT16(save_p, z); + { + if (&mapthings[z] != mobj->spawnpoint) + continue; + WRITEUINT16(save_p, z); + break; + } if (mobj->type == MT_HOOPCENTER) return; }