BATTLE SYNC

* Fix majority of Battle resyncs.
    * Change how Paperitem spawners work. Instead of rerolling the RNG if a map spot is already used, maintain a list of available spots...
    * Correct over-the-network type of player->spheredigestion.
    * Remove the condition that prevented the spheres' spawnpoints from being sent over the network, which meant they were only respawning on the host's end...
* Fix itemroulette on Battle playerarrow. (Rarely seen these days but still possible to observe as spectator for Break the Targets)
* Change draw order of Position Faces and Tab Rankings to allow for players' numbers to draw on top of the big out-of-bumpers X.
This commit is contained in:
toaster 2022-03-23 00:34:23 +00:00
parent 982c6d9a2e
commit b9864c9294
6 changed files with 183 additions and 63 deletions

View file

@ -439,14 +439,12 @@ void K_RunPaperItemSpawners(void)
if (pcount > 0) if (pcount > 0)
{ {
#define MAXITEM 64 #define MAXITEM 64
UINT8 item = 0;
mobj_t *spotList[MAXITEM]; mobj_t *spotList[MAXITEM];
boolean spotUsed[MAXITEM]; UINT8 spotMap[MAXITEM];
UINT8 spotCount = 0, spotBackup = 0;
INT16 starti = 0; INT16 starti = 0;
memset(spotUsed, false, sizeof(spotUsed));
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed)
@ -454,21 +452,23 @@ void K_RunPaperItemSpawners(void)
mo = (mobj_t *)th; mo = (mobj_t *)th;
if (mo->type == MT_PAPERITEMSPOT) if (mo->type == MT_EMERALD)
{
if (item >= MAXITEM)
continue;
spotList[item] = mo;
item++;
}
else if (mo->type == MT_EMERALD)
{ {
emeraldsSpawned |= mo->extravalue1; 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; 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 r = 0, key = 0;
UINT8 recursion = 0;
mobj_t *drop = NULL; mobj_t *drop = NULL;
SINT8 flip = 1; SINT8 flip = 1;
while (spotUsed[r] == true) if (spotCount == 0)
{ {
r = P_RandomKey(item); // all are accessible again
spotCount = spotBackup;
if ((recursion++) > MAXITEM)
{
// roll with it anyway I guess
break;
}
} }
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]); flip = P_MobjFlip(spotList[r]);
// When -1, we're spawning a Chaos Emerald. // When -1, we're spawning a Chaos Emerald.
@ -537,8 +547,23 @@ void K_RunPaperItemSpawners(void)
} }
K_FlipFromObject(drop, spotList[r]); 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");
} }
} }
} }

View file

@ -1088,6 +1088,7 @@ static void K_initKartHUD(void)
} }
} }
// see also MT_PLAYERARROW mobjthinker in p_mobj.c
static void K_drawKartItem(void) static void K_drawKartItem(void)
{ {
// ITEM_X = BASEVIDWIDTH-50; // 270 // ITEM_X = BASEVIDWIDTH-50; // 270
@ -1181,15 +1182,15 @@ static void K_drawKartItem(void)
localpatch = kp_landmine[offset]; localpatch = kp_landmine[offset];
//localcolor = SKINCOLOR_JET; //localcolor = SKINCOLOR_JET;
break; break;
case 16: // Land Mine case 16: // Drop Target
localpatch = kp_droptarget[offset]; localpatch = kp_droptarget[offset];
//localcolor = SKINCOLOR_LIME; //localcolor = SKINCOLOR_LIME;
break; break;
/*case 15: // Pogo Spring /*case 17: // Pogo Spring
localpatch = kp_pogospring[offset]; localpatch = kp_pogospring[offset];
localcolor = SKINCOLOR_TANGERINE; localcolor = SKINCOLOR_TANGERINE;
break; break;
case 16: // Kitchen Sink case 18: // Kitchen Sink
localpatch = kp_kitchensink[offset]; localpatch = kp_kitchensink[offset];
localcolor = SKINCOLOR_STEEL; localcolor = SKINCOLOR_STEEL;
break;*/ break;*/
@ -1782,7 +1783,7 @@ static boolean K_drawKartPositionFaces(void)
// FACE_X = 15; // 15 // FACE_X = 15; // 15
// FACE_Y = 72; // 72 // 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; INT32 i, j, ranklines, strank = -1;
boolean completed[MAXPLAYERS]; boolean completed[MAXPLAYERS];
INT32 rankplayer[MAXPLAYERS]; INT32 rankplayer[MAXPLAYERS];
@ -1838,29 +1839,32 @@ static boolean K_drawKartPositionFaces(void)
} }
if (ranklines < 5) if (ranklines < 5)
Y -= (9*ranklines); Y += (9*ranklines);
else 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) 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 (i > 4) // could be both...
if (ranklines > 5) // could be both... i = 4;
ranklines = 5; 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; ranklines -= 4;
if (i < 0) if (ranklines < 0)
i = 0; ranklines = 0;
} }
else else
{ {
i = strank-2; i = strank+2;
ranklines = strank+3; ranklines = strank-2;
} }
for (; i < ranklines; i++) for (; i >= ranklines; i--)
{ {
if (!playeringame[rankplayer[i]]) continue; if (!playeringame[rankplayer[i]]) continue;
if (players[rankplayer[i]].spectator) 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]); V_DrawScaledPatch(FACE_X-5, Y+10, V_HUDTRANS|V_SLIDEIN|V_SNAPTOLEFT, kp_facenum[pos]);
} }
Y += 18; Y -= 18;
} }
return false; return false;
@ -2143,20 +2147,28 @@ void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, IN
INT32 i, rightoffset = 240; INT32 i, rightoffset = 240;
const UINT8 *colormap; const UINT8 *colormap;
INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; 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 //this function is designed for 9 or less score lines only
//I_Assert(scorelines <= 9); -- not today bitch, kart fixed it up //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! 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(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. V_DrawFill(1-duptweak, 173, dupadjust-2, 1, 0); // And a horizontal line near the bottom.
rightoffset = (BASEVIDWIDTH/2) - 4 - x; 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]; 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; 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); V_DrawThinString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime);
else else
V_DrawString(x + 20, y2, ((tab[i].num == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); 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) if (gametype == GT_RACE)
{ {
#define timestring(time) va("%i'%02i\"%02i", G_TicsToMinutes(time, true), G_TicsToSeconds(time), G_TicsToCentiseconds(time)) #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) if (players[tab[i].num].exiting)
V_DrawRightAlignedThinString(x+rightoffset, y-1, hilicol|V_6WIDTHSPACE, timestring(players[tab[i].num].realtime)); 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 else
V_DrawRightAlignedString(x+rightoffset, y, 0, va("%u", tab[i].count)); V_DrawRightAlignedString(x+rightoffset, y, 0, va("%u", tab[i].count));
y += 18; y -= 18;
if (i == 7) if (i == 8)
{ {
y = 33; y = basey + 7*18;
x = (BASEVIDWIDTH/2) + 4; x = basex;
} }
} }
} }

View file

@ -613,7 +613,7 @@ INT32 K_KartGetItemOdds(
if (gametype == GT_BATTLE) 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]; newodds = K_KartItemOddsBattle[item-1][pos];
} }
else else

View file

@ -112,10 +112,12 @@ boolean P_CanPickupItem(player_t *player, UINT8 weapon)
if (player->exiting || mapreset || (player->pflags & PF_ELIMINATED)) if (player->exiting || mapreset || (player->pflags & PF_ELIMINATED))
return false; return false;
if ((gametyperules & GTR_BUMPERS) // No bumpers in Match
#ifndef OTHERKARMAMODES #ifndef OTHERKARMAMODES
if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) // No bumpers in Match && !weapon
return false;
#endif #endif
&& player->bumpers <= 0)
return false;
if (weapon) if (weapon)
{ {
@ -274,9 +276,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
P_KillMobj(special, toucher, toucher, DMG_NORMAL); P_KillMobj(special, toucher, toucher, DMG_NORMAL);
break; break;
case MT_SPHEREBOX: case MT_SPHEREBOX:
if (player->bumpers <= 0) if (!P_CanPickupItem(player, 0))
return; return;
special->momx = special->momy = special->momz = 0;
P_SetTarget(&special->target, toucher); P_SetTarget(&special->target, toucher);
P_KillMobj(special, toucher, toucher, DMG_NORMAL); P_KillMobj(special, toucher, toucher, DMG_NORMAL);
break; break;

View file

@ -5778,6 +5778,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
} }
break; break;
// see also K_drawKartItem in k_hud.c
case MT_PLAYERARROW: case MT_PLAYERARROW:
if (mobj->target && mobj->target->health if (mobj->target && mobj->target->health
&& mobj->target->player && !mobj->target->player->spectator && mobj->target->player && !mobj->target->player->spectator
@ -5799,7 +5800,7 @@ static void P_MobjSceneryThink(mobj_t *mobj)
mobj->x = mobj->target->x; mobj->x = mobj->target->x;
mobj->y = mobj->target->y; 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) if (!r_splitscreen && players[displayplayers[0]].mo)
{ {
@ -5852,7 +5853,79 @@ static void P_MobjSceneryThink(mobj_t *mobj)
{ {
P_SetMobjState(mobj, S_PLAYERARROW_BOX); P_SetMobjState(mobj, S_PLAYERARROW_BOX);
mobj->tracer->sprite = SPR_ITEM; 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; mobj->tracer->renderflags &= ~RF_DONTDRAW;
} }
else if (mobj->target->player->stealingtimer < 0) else if (mobj->target->player->stealingtimer < 0)
@ -10361,6 +10434,7 @@ void P_RemoveMobj(mobj_t *mobj)
|| mobj->type == MT_BLUESPHERE) || mobj->type == MT_BLUESPHERE)
&& !(mobj->flags2 & MF2_DONTRESPAWN)) && !(mobj->flags2 & MF2_DONTRESPAWN))
{ {
//CONS_Printf("added to queue at tic %d\n", leveltime);
itemrespawnque[iquehead] = mobj->spawnpoint; itemrespawnque[iquehead] = mobj->spawnpoint;
itemrespawntime[iquehead] = leveltime; itemrespawntime[iquehead] = leveltime;
iquehead = (iquehead+1)&(ITEMQUESIZE-1); iquehead = (iquehead+1)&(ITEMQUESIZE-1);
@ -10848,6 +10922,8 @@ void P_RespawnSpecials(void)
if (mthing) if (mthing)
P_SpawnMapThing(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 // pull it from the que
iquetail = (iquetail+1)&(ITEMQUESIZE-1); iquetail = (iquetail+1)&(ITEMQUESIZE-1);
} }

View file

@ -332,7 +332,7 @@ static void P_NetArchivePlayers(void)
WRITEINT16(save_p, players[i].karmadelay); WRITEINT16(save_p, players[i].karmadelay);
WRITEUINT32(save_p, players[i].overtimekarma); WRITEUINT32(save_p, players[i].overtimekarma);
WRITEINT16(save_p, players[i].spheres); WRITEINT16(save_p, players[i].spheres);
WRITEINT16(save_p, players[i].spheredigestion); WRITEUINT32(save_p, players[i].spheredigestion);
WRITESINT8(save_p, players[i].glanceDir); WRITESINT8(save_p, players[i].glanceDir);
WRITEUINT8(save_p, players[i].tripWireState); WRITEUINT8(save_p, players[i].tripWireState);
@ -598,7 +598,7 @@ static void P_NetUnArchivePlayers(void)
players[i].karmadelay = READINT16(save_p); players[i].karmadelay = READINT16(save_p);
players[i].overtimekarma = READUINT32(save_p); players[i].overtimekarma = READUINT32(save_p);
players[i].spheres = READINT16(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].glanceDir = READSINT8(save_p);
players[i].tripWireState = READUINT8(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) if (mobj->type == MT_HOOPCENTER && mobj->threshold == 4242)
return; 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 // spawnpoint is not modified but we must save it since it is an identifier
diff = MD_SPAWNPOINT; diff = MD_SPAWNPOINT;
@ -1847,8 +1847,12 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
size_t z; size_t z;
for (z = 0; z < nummapthings; 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) if (mobj->type == MT_HOOPCENTER)
return; return;
} }