diff --git a/src/d_clisrv.c b/src/d_clisrv.c index ceaaa4aa9..f7387031f 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -795,10 +795,14 @@ static boolean CL_SendJoin(void) sizeof netbuffer->u.clientcfg.application); for (i = 0; i <= splitscreen; i++) - CleanupPlayerName(g_localplayers[i], cv_playername[i].zstring); - - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + // the MAXPLAYERS addition is necessary to communicate that g_localplayers is not yet safe to reference + CleanupPlayerName(MAXPLAYERS+i, cv_playername[i].zstring); strncpy(netbuffer->u.clientcfg.names[i], cv_playername[i].zstring, MAXPLAYERNAME); + } + // privacy shield for the local players not joining this session + for (; i < MAXSPLITSCREENPLAYERS; i++) + strncpy(netbuffer->u.clientcfg.names[i], va("Player %c", 'A' + i), MAXPLAYERNAME); return HSendPacket(servernode, false, 0, sizeof (clientconfig_pak)); } @@ -1334,7 +1338,7 @@ static void CL_ReloadReceivedSavegame(void) for (i = 0; i < MAXPLAYERS; i++) { LUA_InvalidatePlayer(&players[i]); - sprintf(player_names[i], "Player %d", i + 1); + sprintf(player_names[i], "Player %c", 'A' + i); } CL_LoadReceivedSavegame(true); @@ -2421,7 +2425,7 @@ void CL_RemovePlayer(INT32 playernum, kickreason_t reason) doomcom->numslots--; // Reset the name - sprintf(player_names[playernum], "Player %d", playernum+1); + sprintf(player_names[playernum], "Player %c", 'A' + playernum); player_name_changes[playernum] = 0; @@ -3233,7 +3237,7 @@ void SV_ResetServer(void) playeringame[i] = false; playernode[i] = UINT8_MAX; memset(playeraddress[i], 0, sizeof(*playeraddress)); - sprintf(player_names[i], "Player %d", i + 1); + sprintf(player_names[i], "Player %c", 'A' + i); adminplayers[i] = -1; // Populate the entire adminplayers array with -1. K_ClearClientPowerLevels(); splitscreen_invitations[i] = -1; @@ -3432,7 +3436,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) P_ForceLocalAngle(newplayer, newplayer->angleturn); - D_SendPlayerConfig(); + D_SendPlayerConfig(splitscreenplayer); addedtogame = true; if (rejoined) @@ -3578,14 +3582,13 @@ static void Got_AddBot(UINT8 **p, INT32 playernum) LUAh_PlayerJoin(newplayernum); } -static boolean SV_AddWaitingPlayers(const char *name, const char *name2, const char *name3, const char *name4) +static boolean SV_AddWaitingPlayers(SINT8 node, const char *name, const char *name2, const char *name3, const char *name4) { - INT32 node, n, newplayer = false; + INT32 n, newplayernum; UINT8 buf[4 + MAXPLAYERNAME]; UINT8 *buf_p = buf; - INT32 newplayernum; + boolean newplayer = false; - for (node = 0; node < MAXNETNODES; node++) { // splitscreen can allow 2+ players in one node for (; nodewaiting[node] > 0; nodewaiting[node]--) @@ -3704,6 +3707,7 @@ boolean SV_SpawnServer(void) I_Error("What do you think you're doing?"); return false; #else + boolean result = false; if (demo.playback) G_StopDemo(); // reset engine parameter if (metalplayback) @@ -3730,7 +3734,14 @@ boolean SV_SpawnServer(void) else doomcom->numslots = 1; } - return SV_AddWaitingPlayers(cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring); + // strictly speaking, i'm not convinced the following is necessary + // but I'm not confident enough to remove it entirely in case it breaks something + { + SINT8 node = 0; + for (; node < MAXNETNODES; node++) + result |= SV_AddWaitingPlayers(node, cv_playername[0].zstring, cv_playername[1].zstring, cv_playername[2].zstring, cv_playername[3].zstring); + } + return result; #endif } @@ -3903,7 +3914,7 @@ static void HandleConnect(SINT8 node) SV_SendSaveGame(node, false); // send a complete game state DEBFILE("send savegame\n"); } - SV_AddWaitingPlayers(names[0], names[1], names[2], names[3]); + SV_AddWaitingPlayers(node, names[0], names[1], names[2], names[3]); joindelay += cv_joindelay.value * TICRATE; player_joining = true; } diff --git a/src/d_main.c b/src/d_main.c index a53627b41..3c210be9f 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1418,6 +1418,9 @@ void D_SRB2Main(void) // setup loading screen SCR_Startup(); + // Do this in background; lots of number crunching + R_InitTranslucencyTables(); + CON_SetLoadingProgress(LOADED_ISTARTUPGRAPHICS); CONS_Printf("HU_Init()...\n"); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index aa5616538..cdc5c0091 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -222,8 +222,6 @@ static void Command_KartGiveItem_f(void); // CLIENT VARIABLES // ========================================================================= -void SendWeaponPref(UINT8 n); - static CV_PossibleValue_t usemouse_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Force"}, {0, NULL}}; #ifdef LJOYSTICK @@ -1110,6 +1108,7 @@ boolean EnsurePlayerNameIsGood(char *name, INT32 playernum) * is restored to what it was before. * * We assume that if playernum is in ::g_localplayers + * (unless clientjoin is true, a necessary evil) * the console variable ::cv_playername[n] is * already set to newname. However, the player name table is assumed to * contain the old name. @@ -1128,6 +1127,10 @@ void CleanupPlayerName(INT32 playernum, const char *newname) char *tmpname = NULL; INT32 i; boolean namefailed = true; + boolean clientjoin = !!(playernum >= MAXPLAYERS); + + if (clientjoin) + playernum -= MAXPLAYERS; buf = Z_StrDup(newname); @@ -1185,17 +1188,20 @@ void CleanupPlayerName(INT32 playernum, const char *newname) } // no stealing another player's name - for (i = 0; i < MAXPLAYERS; i++) + if (!clientjoin) { - if (i != playernum && playeringame[i] - && strcasecmp(tmpname, player_names[i]) == 0) + for (i = 0; i < MAXPLAYERS; i++) { - break; + if (i != playernum && playeringame[i] + && strcasecmp(tmpname, player_names[i]) == 0) + { + break; + } } - } - if (i < MAXPLAYERS) - break; + if (i < MAXPLAYERS) + break; + } // name is okay then namefailed = false; @@ -1206,18 +1212,23 @@ void CleanupPlayerName(INT32 playernum, const char *newname) // set consvars whether namefailed or not, because even if it succeeded, // spaces may have been removed - for (i = 0; i <= splitscreen; i++) + if (clientjoin) + CV_StealthSet(&cv_playername[playernum], tmpname); + else { - if (playernum == g_localplayers[i]) + for (i = 0; i <= splitscreen; i++) { - CV_StealthSet(&cv_playername[i], tmpname); - break; + if (playernum == g_localplayers[i]) + { + CV_StealthSet(&cv_playername[i], tmpname); + break; + } } - } - if (i > splitscreen) - { - I_Assert(((void)"CleanupPlayerName used on non-local player", 0)); + if (i > splitscreen) + { + I_Assert(((void)"CleanupPlayerName used on non-local player", 0)); + } } Z_Free(buf); @@ -1813,33 +1824,28 @@ static void Got_LeaveParty(UINT8 **cp,INT32 playernum) } } -void D_SendPlayerConfig(void) +void D_SendPlayerConfig(UINT8 n) { - UINT8 i; + UINT8 buf[4]; + UINT8 *p = buf; - for (i = 0; i <= splitscreen; i++) + SendNameAndColor(n); + SendWeaponPref(n); + + if (n == 0) { - UINT8 buf[4]; - UINT8 *p = buf; - - SendNameAndColor(i); - SendWeaponPref(i); - - if (i == 0) - { - // Send it over - WRITEUINT16(p, vspowerlevel[PWRLV_RACE]); - WRITEUINT16(p, vspowerlevel[PWRLV_BATTLE]); - } - else - { - // Splitscreen players have invalid powerlevel - WRITEUINT16(p, 0); - WRITEUINT16(p, 0); - } - - SendNetXCmdForPlayer(i, XD_POWERLEVEL, buf, p-buf); + // Send it over + WRITEUINT16(p, vspowerlevel[PWRLV_RACE]); + WRITEUINT16(p, vspowerlevel[PWRLV_BATTLE]); } + else + { + // Splitscreen players have invalid powerlevel + WRITEUINT16(p, 0); + WRITEUINT16(p, 0); + } + + SendNetXCmdForPlayer(n, XD_POWERLEVEL, buf, p-buf); } // Only works for displayplayer, sorry! diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 41dc3fdf7..0e8ef25e2 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -207,7 +207,8 @@ void D_RegisterServerCommands(void); void D_RegisterClientCommands(void); void CleanupPlayerName(INT32 playernum, const char *newname); boolean EnsurePlayerNameIsGood(char *name, INT32 playernum); -void D_SendPlayerConfig(void); +void SendWeaponPref(UINT8 n); +void D_SendPlayerConfig(UINT8 n); void Command_ExitGame_f(void); void Command_Retry_f(void); void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore diff --git a/src/d_netfil.c b/src/d_netfil.c index b3c93efa2..930662cfa 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -161,10 +161,16 @@ UINT8 *PutFileNeeded(UINT16 firstfile) char wadfilename[MAX_WADPATH] = ""; UINT8 filestatus; - for (i = mainwads+1; i < numwadfiles; i++) //mainwads+1, otherwise we start on the first mainwad +#ifdef DEVELOP + i = 0; +#else + i = mainwads + 1; +#endif + + for (; i < numwadfiles; i++) //mainwads+1, otherwise we start on the first mainwad { // If it has only music/sound lumps, don't put it in the list - if (!wadfiles[i]->important) + if (i > mainwads && !wadfiles[i]->important) continue; if (firstfile) @@ -276,11 +282,16 @@ boolean CL_CheckDownloadable(void) } // Downloading locally disabled +#if 0 if (!dlstatus && M_CheckParm("-nodownload")) dlstatus = 3; if (!dlstatus) return true; +#else + if (!dlstatus) + dlstatus = 3; +#endif // not downloadable, put reason in console CONS_Alert(CONS_NOTICE, M_GetText("You need additional files to connect to this server:\n")); @@ -489,7 +500,12 @@ INT32 CL_CheckFiles(void) CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename); // Check in already loaded files - for (j = mainwads+1; wadfiles[j]; j++) +#ifdef DEVELOP + j = 0; +#else + j = mainwads + 1; +#endif + for (; wadfiles[j]; j++) { nameonly(strcpy(wadfilename, wadfiles[j]->filename)); if (!stricmp(wadfilename, fileneeded[i].filename) && diff --git a/src/deh_tables.c b/src/deh_tables.c index 960cf4431..a1b7fb844 100644 --- a/src/deh_tables.c +++ b/src/deh_tables.c @@ -3476,6 +3476,9 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi "S_RANDOMITEM12", "S_DEADRANDOMITEM", + // Sphere Box (for Battle) + "S_SPHEREBOX", + // Random Item Pop "S_RANDOMITEMPOP1", "S_RANDOMITEMPOP2", diff --git a/src/g_demo.c b/src/g_demo.c index 1cf9ff371..545080242 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2793,6 +2793,7 @@ void G_DoPlayDemo(char *defdemoname) demoflags = READUINT8(demo_p); gametype = READUINT8(demo_p); + G_SetGametype(gametype); if (demo.title) // Titledemos should always play and ought to always be compatible with whatever wadlist is running. G_SkipDemoExtraFiles(&demo_p); diff --git a/src/g_game.c b/src/g_game.c index 49b9a31f3..f3c50cbbe 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -346,7 +346,6 @@ static void kickstartaccel_OnChange(void); static void kickstartaccel2_OnChange(void); static void kickstartaccel3_OnChange(void); static void kickstartaccel4_OnChange(void); -void SendWeaponPref(UINT8 n); static CV_PossibleValue_t joyaxis_cons_t[] = {{0, "None"}, {1, "X-Axis"}, {2, "Y-Axis"}, {-1, "X-Axis-"}, {-2, "Y-Axis-"}, diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h index b4dc1accb..2cdbd02c3 100644 --- a/src/hardware/hw_defs.h +++ b/src/hardware/hw_defs.h @@ -113,7 +113,7 @@ typedef struct UINT8 splitscreen; boolean flip; // screenflip boolean shearing; // 14042019 - angle_t viewaiming; // 17052019 + float viewaiming; // 17052019 boolean roll; FLOAT rollangle; // done to not override USE_FTRANSFORM_ANGLEZ FLOAT centerx, centery; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 259770fee..95077a54a 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1650,7 +1650,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) } { - fixed_t anglef = AngleFixed(R_SpriteRotationAngle(spr->mobj, NULL)); + fixed_t anglef = AngleFixed(R_ModelRotationAngle(spr->mobj, NULL)); p.rollangle = 0.0f; diff --git a/src/info.c b/src/info.c index 5d506d98d..062d1fbb2 100644 --- a/src/info.c +++ b/src/info.c @@ -526,6 +526,7 @@ char sprnames[NUMSPRITES + 1][5] = //SRB2kart Sprites (sort later) "RNDM", // Random Item Box + "SBOX", // Sphere Box (for Battle) "RPOP", // Random Item Box Pop "SGNS", // Signpost sparkle "FAST", // Speed boost trail @@ -4061,6 +4062,20 @@ state_t states[NUMSTATES] = {SPR_RNDM, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_RANDOMITEM1}, // S_RANDOMITEM12 {SPR_NULL, 0, 0, {A_ItemPop}, 0, 0, S_NULL}, // S_DEADRANDOMITEM + {SPR_SBOX, FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX2}, // S_SPHEREBOX1 + {SPR_SBOX, 2|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX3}, // S_SPHEREBOX2 + {SPR_SBOX, 4|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX4}, // S_SPHEREBOX3 + {SPR_SBOX, 6|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX5}, // S_SPHEREBOX4 + {SPR_SBOX, 8|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX6}, // S_SPHEREBOX5 + {SPR_SBOX, 10|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX7}, // S_SPHEREBOX6 + {SPR_SBOX, 12|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX8}, // S_SPHEREBOX7 + {SPR_SBOX, 14|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX9}, // S_SPHEREBOX8 + {SPR_SBOX, 16|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX10}, // S_SPHEREBOX9 + {SPR_SBOX, 18|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX11}, // S_SPHEREBOX10 + {SPR_SBOX, 20|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX12}, // S_SPHEREBOX11 + {SPR_SBOX, 22|FF_FULLBRIGHT|FF_ANIMATE|FF_GLOBALANIM, 4, {NULL}, 1, 1, S_SPHEREBOX1}, // S_SPHEREBOX12 + {SPR_NULL, 0, 0, {A_ItemPop}, 1, 0, S_NULL}, // S_DEADSPHEREBOX + {SPR_RPOP, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_RANDOMITEMPOP2}, // S_RANDOMITEMPOP1 {SPR_RPOP, FF_FULLBRIGHT|1, 5, {NULL}, 0, 0, S_RANDOMITEMPOP3}, // S_RANDOMITEMPOP2 {SPR_RPOP, FF_FULLBRIGHT|2, 5, {NULL}, 0, 0, S_RANDOMITEMPOP4}, // S_RANDOMITEMPOP3 @@ -23017,6 +23032,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_SPHEREBOX + -1, // doomednum + S_SPHEREBOX1, // spawnstate + 1000, // spawnhealth + S_NULL, // seestate + sfx_None, // seesound + 0, // reactiontime + sfx_None, // attacksound + S_NULL, // painstate + 0, // painchance + sfx_None, // painsound + S_NULL, // meleestate + S_NULL, // missilestate + S_DEADSPHEREBOX, // deathstate + S_NULL, // xdeathstate + sfx_kc2e, // deathsound + 60*FRACUNIT, // speed + 48*FRACUNIT, // radius + 48*FRACUNIT, // height + 0, // display offset + 100, // mass + MT_RANDOMITEMPOP, // damage + sfx_None, // activesound + MF_SLIDEME|MF_SPECIAL|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_DONTENCOREMAP, // flags + S_NULL // raisestate + }, + { // MT_RANDOMITEMPOP -1, // doomednum S_INVISIBLE, // spawnstate diff --git a/src/info.h b/src/info.h index 115948905..dfb0d3c43 100644 --- a/src/info.h +++ b/src/info.h @@ -1070,6 +1070,7 @@ typedef enum sprite // SRB2Kart SPR_RNDM, // Random Item Box + SPR_SBOX, // Sphere Box (for Battle) SPR_RPOP, // Random Item Box Pop SPR_SGNS, // Signpost sparkle SPR_FAST, // Speed boost trail @@ -4453,6 +4454,21 @@ typedef enum state S_RANDOMITEM12, S_DEADRANDOMITEM, + // Sphere Box (for Battle) + S_SPHEREBOX1, + S_SPHEREBOX2, + S_SPHEREBOX3, + S_SPHEREBOX4, + S_SPHEREBOX5, + S_SPHEREBOX6, + S_SPHEREBOX7, + S_SPHEREBOX8, + S_SPHEREBOX9, + S_SPHEREBOX10, + S_SPHEREBOX11, + S_SPHEREBOX12, + S_DEADSPHEREBOX, + // Random Item Pop S_RANDOMITEMPOP1, S_RANDOMITEMPOP2, @@ -6465,6 +6481,7 @@ typedef enum mobj_type // SRB2kart MT_RANDOMITEM, + MT_SPHEREBOX, MT_RANDOMITEMPOP, MT_FLOATINGITEM, MT_ITEMCAPSULE, diff --git a/src/k_battle.c b/src/k_battle.c index c33ea6bcc..8a5257206 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -229,6 +229,28 @@ mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT return emerald; } +mobj_t *K_SpawnSphereBox(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 amount) +{ + mobj_t *drop = P_SpawnMobj(x, y, z, MT_SPHEREBOX); + + (void)amount; + + drop->angle = angle; + P_Thrust(drop, + FixedAngle(P_RandomFixed() * 180) + angle, + P_RandomRange(4, 12) * mapobjectscale); + + drop->momz = flip * 12 * mapobjectscale; + if (drop->eflags & MFE_UNDERWATER) + drop->momz = (117 * drop->momz) / 200; + + drop->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); + + drop->extravalue1 = amount; + + return drop; +} + void K_DropEmeraldsFromPlayer(player_t *player, UINT32 emeraldType) { UINT8 i; @@ -359,6 +381,12 @@ void K_RunPaperItemSpawners(void) FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, 0, 0 ); + + K_SpawnSphereBox( + battleovertime.x, battleovertime.y, battleovertime.z + (128 * mapobjectscale * flip), + FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, + 10 + ); } } else @@ -441,6 +469,14 @@ void K_RunPaperItemSpawners(void) firstUnspawnedEmerald ); } + else if (P_RandomChance(FRACUNIT/3)) + { + drop = K_SpawnSphereBox( + spotList[r]->x, spotList[r]->y, spotList[r]->z + (128 * mapobjectscale * flip), + FixedAngle(P_RandomRange(0, 359) * FRACUNIT), flip, + 10 + ); + } else { drop = K_CreatePaperItem( diff --git a/src/k_battle.h b/src/k_battle.h index 221873b04..c56c576d6 100644 --- a/src/k_battle.h +++ b/src/k_battle.h @@ -22,6 +22,7 @@ void K_SpawnBattlePoints(player_t *source, player_t *victim, UINT8 amount); void K_CheckBumpers(void); void K_CheckEmeralds(player_t *player); mobj_t *K_SpawnChaosEmerald(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT32 emeraldType); +mobj_t *K_SpawnSphereBox(fixed_t x, fixed_t y, fixed_t z, angle_t angle, SINT8 flip, UINT8 amount); void K_DropEmeraldsFromPlayer(player_t *player, UINT32 emeraldType); UINT8 K_NumEmeralds(player_t *player); void K_RunPaperItemSpawners(void); diff --git a/src/k_bot.c b/src/k_bot.c index 29aa4f177..24f3517a2 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -273,16 +273,94 @@ boolean K_PlayerUsesBotMovement(player_t *player) --------------------------------------------------*/ boolean K_BotCanTakeCut(player_t *player) { - if (!K_ApplyOffroad(player) + if ( +#if 1 + K_TripwirePass(player) == true +#else + K_ApplyOffroad(player) == false +#endif || player->itemtype == KITEM_SNEAKER || player->itemtype == KITEM_ROCKETSNEAKER || player->itemtype == KITEM_INVINCIBILITY - || player->itemtype == KITEM_HYUDORO) + ) + { return true; + } return false; } +/*-------------------------------------------------- + static line_t *K_FindBotController(mobj_t *mo) + + Finds if any bot controller linedefs are tagged to the bot's sector. + + Input Arguments:- + mo - The bot player's mobj. + + Return:- + Linedef of the bot controller. NULL if it doesn't exist. +--------------------------------------------------*/ +static line_t *K_FindBotController(mobj_t *mo) +{ + msecnode_t *node; + ffloor_t *rover; + INT16 lineNum = -1; + mtag_t tag; + + I_Assert(mo != NULL); + I_Assert(!P_MobjWasRemoved(mo)); + + for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) + { + if (!node->m_sector) + { + continue; + } + + tag = Tag_FGet(&node->m_sector->tags); + lineNum = P_FindSpecialLineFromTag(2004, tag, -1); // todo: needs to not use P_FindSpecialLineFromTag + + if (lineNum != -1) + { + break; + } + + for (rover = node->m_sector->ffloors; rover; rover = rover->next) + { + sector_t *rs = NULL; + + if (!(rover->flags & FF_EXISTS)) + { + continue; + } + + if (mo->z > *rover->topheight || mo->z + mo->height < *rover->bottomheight) + { + continue; + } + + rs = §ors[rover->secnum]; + tag = Tag_FGet(&rs->tags); + lineNum = P_FindSpecialLineFromTag(2004, tag, -1); + + if (lineNum != -1) + { + break; + } + } + } + + if (lineNum != -1) + { + return &lines[lineNum]; + } + else + { + return NULL; + } +} + /*-------------------------------------------------- static UINT32 K_BotRubberbandDistance(player_t *player) @@ -346,6 +424,7 @@ fixed_t K_BotRubberband(player_t *player) fixed_t rubberband = FRACUNIT; fixed_t max, min; player_t *firstplace = NULL; + line_t *botController = NULL; UINT8 i; if (player->exiting) @@ -354,6 +433,17 @@ fixed_t K_BotRubberband(player_t *player) return FRACUNIT; } + botController = K_FindBotController(player->mo); + + if (botController != NULL) + { + // No Climb Flag: Disable rubberbanding + if (botController->flags & ML_NOCLIMB) + { + return FRACUNIT; + } + } + for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator) @@ -430,8 +520,8 @@ fixed_t K_BotTopSpeedRubberband(player_t *player) } else { - // Max at +10% for level 9 bots - rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 10); + // Max at +20% for level 9 bots + rubberband = FRACUNIT + ((rubberband - FRACUNIT) / 5); } // Only allow you to go faster than your regular top speed if you're facing the right direction @@ -488,6 +578,14 @@ fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) return frict; } + if (player->tiregrease > 0) + { + // This isn't great -- it means rubberbanding will slow down when they hit a spring + // But it's better than the opposite where they accelerate into hyperspace :V + // (would appreciate an actual fix though ... could try being additive instead of multiplicative) + return frict; + } + origFrict = FixedDiv(ORIG_FRICTION, FRACUNIT + (rubberband / 2)); if (frict == ORIG_FRICTION) @@ -567,14 +665,11 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) const INT16 handling = K_GetKartTurnValue(player, KART_FULLTURN); // Reduce prediction based on how fast you can turn const INT16 normal = KART_FULLTURN; // "Standard" handling to compare to - const fixed_t distreduce = K_BotReducePrediction(player); - const fixed_t radreduce = min(distreduce + FRACUNIT/4, FRACUNIT); - const tic_t futuresight = (TICRATE * normal) / max(1, handling); // How far ahead into the future to try and predict - const fixed_t speed = max(P_AproxDistance(player->mo->momx, player->mo->momy), K_GetKartSpeed(player, false) / 4); + const fixed_t speed = P_AproxDistance(player->mo->momx, player->mo->momy); const INT32 startDist = (768 * mapobjectscale) / FRACUNIT; - const INT32 distance = ((FixedMul(speed, distreduce) / FRACUNIT) * futuresight) + startDist; + const INT32 distance = ((speed / FRACUNIT) * futuresight) + startDist; botprediction_t *predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); waypoint_t *wp = player->nextwaypoint; @@ -583,6 +678,9 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) fixed_t smallestradius = INT32_MAX; angle_t angletonext = ANGLE_MAX; + // Halves radius when encountering a wall on your way to your destination. + fixed_t radreduce = FRACUNIT; + size_t nwp; size_t i; @@ -595,7 +693,7 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) { predict->x = wp->mobj->x; predict->y = wp->mobj->y; - predict->radius = FixedMul(wp->mobj->radius, radreduce); + predict->radius = wp->mobj->radius; return predict; } @@ -635,12 +733,12 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) for (i = 0; i < wp->numnextwaypoints; i++) { - if (!K_GetWaypointIsEnabled(wp->nextwaypoints[i])) + if (K_GetWaypointIsEnabled(wp->nextwaypoints[i]) == false) { continue; } - if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) && !K_BotCanTakeCut(player)) + if (K_GetWaypointIsShortcut(wp->nextwaypoints[i]) == true && K_BotCanTakeCut(player) == false) { continue; } @@ -673,6 +771,13 @@ static botprediction_t *K_CreateBotPrediction(player_t *player) disttonext = (INT32)wp->nextwaypointdistances[nwp]; + if (P_TraceBotTraversal(player->mo, wp->nextwaypoints[nwp]->mobj) == false) + { + // If we can't get a direct path to this waypoint, we don't want to check much further... + disttonext *= 2; + radreduce = FRACUNIT/2; + } + if (disttonext > distanceleft) { break; @@ -761,7 +866,7 @@ static UINT8 K_TrySpindash(player_t *player) return 0; } - if (speedDiff < (3 * baseAccel / 4)) + if (speedDiff < (baseAccel / 4)) { if (player->botvars.spindashconfirm < BOTSPINDASHCONFIRM) { @@ -797,70 +902,6 @@ static UINT8 K_TrySpindash(player_t *player) return 0; } -/*-------------------------------------------------- - static INT16 K_FindBotController(mobj_t *mo) - - Finds if any bot controller linedefs are tagged to the bot's sector. - - Input Arguments:- - mo - The bot player's mobj. - - Return:- - Line number of the bot controller. -1 if it doesn't exist. ---------------------------------------------------*/ -static INT16 K_FindBotController(mobj_t *mo) -{ - msecnode_t *node; - ffloor_t *rover; - INT16 lineNum = -1; - mtag_t tag; - - I_Assert(mo != NULL); - I_Assert(!P_MobjWasRemoved(mo)); - - for (node = mo->touching_sectorlist; node; node = node->m_sectorlist_next) - { - if (!node->m_sector) - { - continue; - } - - tag = Tag_FGet(&node->m_sector->tags); - lineNum = P_FindSpecialLineFromTag(2004, tag, -1); // todo: needs to not use P_FindSpecialLineFromTag - - if (lineNum != -1) - { - return lineNum; - } - - for (rover = node->m_sector->ffloors; rover; rover = rover->next) - { - sector_t *rs = NULL; - - if (!(rover->flags & FF_EXISTS)) - { - continue; - } - - if (mo->z > *rover->topheight || mo->z + mo->height < *rover->bottomheight) - { - continue; - } - - rs = §ors[rover->secnum]; - tag = Tag_FGet(&rs->tags); - lineNum = P_FindSpecialLineFromTag(2004, tag, -1); - - if (lineNum != -1) - { - return lineNum; - } - } - } - - return -1; -} - /*-------------------------------------------------- static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player) @@ -925,6 +966,50 @@ static void K_DrawPredictionDebug(botprediction_t *predict, player_t *player) } } +/*-------------------------------------------------- + static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController) + + Determines inputs for trick panels. + + Input Arguments:- + player - Player to generate the ticcmd for. + cmd - The player's ticcmd to modify. + botController - Linedef for the bot controller. + + Return:- + None +--------------------------------------------------*/ +static void K_BotTrick(player_t *player, ticcmd_t *cmd, line_t *botController) +{ + // Trick panel state -- do nothing until a controller line is found, in which case do a trick. + if (botController == NULL) + { + return; + } + + if (player->trickpanel == 1) + { + INT32 type = (sides[botController->sidenum[0]].rowoffset / FRACUNIT); + + // Y Offset: Trick type + switch (type) + { + case 1: + cmd->turning = KART_FULLTURN; + break; + case 2: + cmd->turning = -KART_FULLTURN; + break; + case 3: + cmd->buttons |= BT_FORWARD; + break; + case 4: + cmd->buttons |= BT_BACKWARD; + break; + } + } +} + /*-------------------------------------------------- void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) @@ -936,7 +1021,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) boolean trySpindash = true; UINT8 spindash = 0; INT32 turnamt = 0; - INT16 botController = -1; + line_t *botController = NULL; // Can't build a ticcmd if we aren't spawned... if (!player->mo) @@ -959,7 +1044,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) } // Complete override of all ticcmd functionality - if (LUAh_BotTiccmd(player, cmd)) + if (LUAh_BotTiccmd(player, cmd) == true) { return; } @@ -968,30 +1053,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if (player->trickpanel != 0) { - // Trick panel state -- do nothing until a controller line is found, in which case do a trick. - - if (player->trickpanel == 1 && botController != -1) - { - line_t *controllerLine = &lines[botController]; - INT32 type = (sides[controllerLine->sidenum[0]].rowoffset / FRACUNIT); - - // Y Offset: Trick type - switch (type) - { - case 1: - cmd->turning = KART_FULLTURN; - break; - case 2: - cmd->turning = -KART_FULLTURN; - break; - case 3: - cmd->buttons |= BT_FORWARD; - break; - case 4: - cmd->buttons |= BT_BACKWARD; - break; - } - } + K_BotTrick(player, cmd, botController); // Don't do anything else. return; @@ -1000,20 +1062,19 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) if ((player->nextwaypoint != NULL && player->nextwaypoint->mobj != NULL && !P_MobjWasRemoved(player->nextwaypoint->mobj)) - || (botController != -1)) + || (botController != NULL)) { // Handle steering towards waypoints! SINT8 turnsign = 0; angle_t destangle, moveangle, angle; INT16 anglediff; - if (botController != -1) + if (botController != NULL && (botController->flags & ML_EFFECT1)) { const fixed_t dist = (player->mo->radius * 4); - line_t *controllerLine = &lines[botController]; // X Offset: Movement direction - destangle = FixedAngle(sides[controllerLine->sidenum[0]].textureoffset); + destangle = FixedAngle(sides[botController->sidenum[0]].textureoffset); // Overwritten prediction predict = Z_Calloc(sizeof(botprediction_t), PU_STATIC, NULL); @@ -1112,18 +1173,6 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Don't turn at all turnamt = 0; } - else - { - // Make minor adjustments - turnamt /= 4; - } - } - - if (anglediff > 60) - { - // Actually, don't go too fast... - cmd->forwardmove /= 2; - cmd->buttons |= BT_BRAKE; } } } diff --git a/src/k_bot.h b/src/k_bot.h index 94694c2dd..a04d5c174 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -15,6 +15,7 @@ #include "k_waypoint.h" #include "d_player.h" +#include "r_defs.h" // Maximum value of botvars.difficulty #define MAXBOTDIFFICULTY 9 @@ -185,19 +186,22 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y); /*-------------------------------------------------- - fixed_t K_BotReducePrediction(player_t *player); + boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) - Finds walls nearby the specified player, to create a multiplier - to pull bot predictions back by. + Tells us if a bot will play more careful around + this sector. Checks FOFs in the sector, as well. Input Arguments:- - player - Player to compare. + player - Player to check against. + sec - Sector to check against. + x - Linedef cross X position, for slopes + y - Linedef cross Y position, for slopes Return:- - Multiplier in fixed point scale. + true if avoiding this sector, false otherwise. --------------------------------------------------*/ -fixed_t K_BotReducePrediction(player_t *player); +boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y); /*-------------------------------------------------- @@ -219,8 +223,8 @@ void K_NudgePredictionTowardsObjects(botprediction_t *predict, player_t *player) /*-------------------------------------------------- void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); - Gives a multiplier for a bot's rubberbanding. Meant to be used for top speed, - acceleration, and handling. + Creates a bot's ticcmd, looking at its surroundings to + try and figure out what it should do. Input Arguments:- player - Player to generate the ticcmd for. diff --git a/src/k_botitem.c b/src/k_botitem.c index 212a84929..4bb83931c 100644 --- a/src/k_botitem.c +++ b/src/k_botitem.c @@ -86,7 +86,7 @@ static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t r } /*-------------------------------------------------- - static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius) + static player_t *K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius) Looks for players around a specified x/y coordinate. @@ -97,9 +97,9 @@ static boolean K_BotUseItemNearPlayer(player_t *player, ticcmd_t *cmd, fixed_t r radius - The radius to look for players in. Return:- - true if a player was found around the coordinate, otherwise false. + The player we found, NULL if nothing was found. --------------------------------------------------*/ -static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius) +static player_t *K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t radius) { UINT8 i; @@ -129,15 +129,15 @@ static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t if (dist <= radius) { - return true; + return target; } } - return false; + return NULL; } /*-------------------------------------------------- - static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra) + static player_t *K_PlayerPredictThrow(player_t *player, UINT8 extra) Looks for players around the predicted coordinates of their thrown item. @@ -146,9 +146,9 @@ static boolean K_PlayerNearSpot(player_t *player, fixed_t x, fixed_t y, fixed_t extra - Extra throwing distance, for aim forward on mines. Return:- - true if a player was found around the coordinate, otherwise false. + The player we're trying to throw at, NULL if none was found. --------------------------------------------------*/ -static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra) +static player_t *K_PlayerPredictThrow(player_t *player, UINT8 extra) { const fixed_t dist = (30 + (extra * 10)) * player->mo->scale; const UINT32 airtime = FixedDiv(dist + player->mo->momz, gravity); @@ -159,7 +159,7 @@ static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra) } /*-------------------------------------------------- - static boolean K_PlayerInCone(player_t *player, UINT16 cone, boolean flip) + static player_t *K_PlayerInCone(player_t *player, UINT16 cone, boolean flip) Looks for players in the . @@ -172,7 +172,7 @@ static boolean K_PlayerPredictThrow(player_t *player, UINT8 extra) Return:- true if a player was found in the cone, otherwise false. --------------------------------------------------*/ -static boolean K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boolean flip) +static player_t *K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boolean flip) { UINT8 i; @@ -222,22 +222,96 @@ static boolean K_PlayerInCone(player_t *player, fixed_t radius, UINT16 cone, boo { if (ad >= 180-cone) { - return true; + return target; } } else { if (ad <= cone) { - return true; + return target; } } } } + return NULL; +} + +/*-------------------------------------------------- + static boolean K_RivalBotAggression(player_t *bot, player_t *target) + + Returns if a bot is a rival & wants to be aggressive to a player. + + Input Arguments:- + bot - Bot to check. + target - Who the bot wants to attack. + + Return:- + false if not the rival. false if the target is another bot. Otherwise, true. +--------------------------------------------------*/ +static boolean K_RivalBotAggression(player_t *bot, player_t *target) +{ + if (bot == NULL || target == NULL) + { + // Invalid. + return false; + } + + if (bot->bot == false) + { + // lol + return false; + } + + if (bot->botvars.rival == false) + { + // Not the rival, we aren't self-aware. + return false; + } + + if (target->bot == false) + { + // This bot knows that the real threat is the player. + return true; + } + + // Calling them your friends is misleading, but you'll at least spare them. return false; } +/*-------------------------------------------------- + static void K_ItemConfirmForTarget(player_t *bot, player_t *target, UINT16 amount) + + Handles updating item confirm values for offense items. + + Input Arguments:- + bot - Bot to check. + target - Who the bot wants to attack. + amount - Amount to increase item confirm time by. + + Return:- + None +--------------------------------------------------*/ +static void K_ItemConfirmForTarget(player_t *bot, player_t *target, UINT16 amount) +{ + if (bot == NULL || target == NULL) + { + return; + } + + if (K_RivalBotAggression(bot, target) == true) + { + // Double the rate when you're aggressive. + bot->botvars.itemconfirm += amount << 1; + } + else + { + // Do as normal. + bot->botvars.itemconfirm += amount; + } +} + /*-------------------------------------------------- static boolean K_BotGenericPressItem(player_t *player, ticcmd_t *cmd, SINT8 dir) @@ -316,21 +390,21 @@ static boolean K_BotRevealsGenericTrap(player_t *player, INT16 turnamt, boolean } // Check the predicted throws. - if (K_PlayerPredictThrow(player, 0)) + if (K_PlayerPredictThrow(player, 0) != NULL) { return true; } if (mine) { - if (K_PlayerPredictThrow(player, 1)) + if (K_PlayerPredictThrow(player, 1) != NULL) { return true; } } // Check your behind. - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + if (K_PlayerInCone(player, player->mo->radius * 16, 10, true) != NULL) { return true; } @@ -447,7 +521,6 @@ static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd) } } - /*-------------------------------------------------- static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) @@ -464,9 +537,17 @@ static void K_BotItemRocketSneaker(player_t *player, ticcmd_t *cmd) static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) { SINT8 throwdir = -1; + player_t *target = NULL; player->botvars.itemconfirm++; + target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + if (target != NULL) + { + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); + throwdir = -1; + } + if (abs(turnamt) >= KART_FULLTURN/2) { player->botvars.itemconfirm += player->botvars.difficulty / 2; @@ -474,19 +555,15 @@ static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) } else { - if (K_PlayerPredictThrow(player, 0)) + target = K_PlayerPredictThrow(player, 0); + + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty * 2; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2); throwdir = 1; } } - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) - { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; - } - if (player->botvars.itemconfirm > 2*TICRATE || player->bananadrag >= TICRATE) { K_BotGenericPressItem(player, cmd, throwdir); @@ -509,12 +586,14 @@ static void K_BotItemBanana(player_t *player, ticcmd_t *cmd, INT16 turnamt) static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) { SINT8 throwdir = 0; + player_t *target = NULL; player->botvars.itemconfirm++; - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); throwdir = -1; } @@ -525,27 +604,63 @@ static void K_BotItemMine(player_t *player, ticcmd_t *cmd, INT16 turnamt) } else { - if (K_PlayerPredictThrow(player, 0)) + target = K_PlayerPredictThrow(player, 0); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty * 2; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2); throwdir = 0; } - if (K_PlayerPredictThrow(player, 1)) + target = K_PlayerPredictThrow(player, 1); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty * 2; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * 2); throwdir = 1; } } - - if (player->botvars.itemconfirm > 2*TICRATE || player->bananadrag >= TICRATE) { K_BotGenericPressItem(player, cmd, throwdir); } } +/*-------------------------------------------------- + static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt) + + Item usage for landmine tossing. + + Input Arguments:- + player - Bot to do this for. + cmd - Bot's ticcmd to edit. + turnamt - How hard they currently are turning. + + Return:- + None +--------------------------------------------------*/ +static void K_BotItemLandmine(player_t *player, ticcmd_t *cmd, INT16 turnamt) +{ + player_t *target = NULL; + + player->botvars.itemconfirm++; + + if (abs(turnamt) >= KART_FULLTURN/2) + { + player->botvars.itemconfirm += player->botvars.difficulty / 2; + } + + target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + if (target != NULL) + { + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); + } + + if (player->botvars.itemconfirm > 2*TICRATE) + { + K_BotGenericPressItem(player, cmd, -1); + } +} + /*-------------------------------------------------- static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) @@ -562,18 +677,21 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) { const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); SINT8 throwdir = -1; + player_t *target = NULL; player->botvars.itemconfirm++; - if (K_PlayerPredictThrow(player, 0)) + target = K_PlayerPredictThrow(player, 0); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty / 2; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty / 2); throwdir = 1; } - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); throwdir = -1; } @@ -603,6 +721,7 @@ static void K_BotItemEggman(player_t *player, ticcmd_t *cmd) static boolean K_BotRevealsEggbox(player_t *player) { const UINT8 stealth = K_EggboxStealth(player->mo->x, player->mo->y); + player_t *target = NULL; // This is a stealthy spot for an eggbox, lets reveal it! if (stealth > 1) @@ -611,13 +730,15 @@ static boolean K_BotRevealsEggbox(player_t *player) } // Check the predicted throws. - if (K_PlayerPredictThrow(player, 0)) + target = K_PlayerPredictThrow(player, 0); + if (target != NULL) { return true; } // Check your behind. - if (K_PlayerInCone(player, player->mo->radius * 16, 10, true)) + target = K_PlayerInCone(player, player->mo->radius * 16, 10, true); + if (target != NULL) { return true; } @@ -644,7 +765,7 @@ static void K_BotItemEggmanShield(player_t *player, ticcmd_t *cmd) return; } - if (K_BotRevealsEggbox(player) || (player->botvars.itemconfirm++ > 20*TICRATE)) + if (K_BotRevealsEggbox(player) == true || (player->botvars.itemconfirm++ > 20*TICRATE)) { K_BotGenericPressItem(player, cmd, 0); } @@ -666,8 +787,9 @@ static void K_BotItemEggmanExplosion(player_t *player, ticcmd_t *cmd) { if (player->position == 1) { + // Hey, we aren't gonna find anyone up here... + // why don't we slow down a bit? :) cmd->forwardmove /= 2; - cmd->buttons |= BT_BRAKE; } K_BotUseItemNearPlayer(player, cmd, 128*player->mo->scale); @@ -690,23 +812,32 @@ static void K_BotItemOrbinaut(player_t *player, ticcmd_t *cmd) const fixed_t topspeed = K_GetKartSpeed(player, false); fixed_t radius = (player->mo->radius * 32); SINT8 throwdir = -1; + UINT8 snipeMul = 2; + player_t *target = NULL; if (player->speed > topspeed) { radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!! } player->botvars.itemconfirm++; - if (K_PlayerInCone(player, radius, 10, false)) + target = K_PlayerInCone(player, radius, 10, false); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty * 2; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * snipeMul); throwdir = 1; } else if (K_PlayerInCone(player, radius, 10, true)) { - player->botvars.itemconfirm += player->botvars.difficulty; - throwdir = -1; + target = K_PlayerInCone(player, radius, 10, true); + + if (target != NULL) + { + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); + throwdir = -1; + } } if (player->botvars.itemconfirm > 5*TICRATE) @@ -732,24 +863,54 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) const fixed_t topspeed = K_GetKartSpeed(player, false); fixed_t radius = (player->mo->radius * 32); SINT8 throwdir = 1; + UINT8 snipeMul = 2; + INT32 lastTarg = player->lastjawztarget; + player_t *target = NULL; if (player->speed > topspeed) { radius = FixedMul(radius, FixedDiv(player->speed, topspeed)); + snipeMul = 3; // Confirm faster when you'll throw it with a bunch of extra speed!! } player->botvars.itemconfirm++; - if (K_PlayerInCone(player, radius, 10, true)) + target = K_PlayerInCone(player, radius, 10, true); + if (target != NULL) { - player->botvars.itemconfirm += player->botvars.difficulty; + K_ItemConfirmForTarget(player, target, player->botvars.difficulty); throwdir = -1; } - if (player->lastjawztarget != -1) + if (lastTarg != -1 + && playeringame[lastTarg] == true + && players[lastTarg].spectator == false + && players[lastTarg].mo != NULL + && P_MobjWasRemoved(players[lastTarg].mo) == false) { - player->botvars.itemconfirm += player->botvars.difficulty * 2; - throwdir = 1; + mobj_t *targMo = players[lastTarg].mo; + mobj_t *mobj = NULL, *next = NULL; + boolean targettedAlready = false; + + target = &players[lastTarg]; + + // Make sure no other Jawz are targetting this player. + for (mobj = kitemcap; mobj; mobj = next) + { + next = mobj->itnext; + + if (mobj->type == MT_JAWZ && mobj->target == targMo) + { + targettedAlready = true; + break; + } + } + + if (targettedAlready == false) + { + K_ItemConfirmForTarget(player, target, player->botvars.difficulty * snipeMul); + throwdir = 1; + } } if (player->botvars.itemconfirm > 5*TICRATE) @@ -772,7 +933,7 @@ static void K_BotItemJawz(player_t *player, ticcmd_t *cmd) --------------------------------------------------*/ static void K_BotItemThunder(player_t *player, ticcmd_t *cmd) { - if (!K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale)) + if (K_BotUseItemNearPlayer(player, cmd, 192*player->mo->scale) == false) { if (player->botvars.itemconfirm > 10*TICRATE) { @@ -1036,7 +1197,6 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) K_BotItemSneaker(player, cmd); break; case KITEM_BANANA: - case KITEM_LANDMINE: if (!(player->pflags & PF_ITEMOUT)) { K_BotItemGenericTrapShield(player, cmd, turnamt, false); @@ -1081,6 +1241,9 @@ void K_BotItemUsage(player_t *player, ticcmd_t *cmd, INT16 turnamt) K_BotItemMine(player, cmd, turnamt); } break; + case KITEM_LANDMINE: + K_BotItemLandmine(player, cmd, turnamt); + break; case KITEM_THUNDERSHIELD: K_BotItemThunder(player, cmd); break; diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 55533d02a..846521e6f 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -122,7 +122,7 @@ UINT8 K_EggboxStealth(fixed_t x, fixed_t y) } } - return (globalsmuggle.randomitems * globalsmuggle.eggboxes); + return (globalsmuggle.randomitems * (globalsmuggle.eggboxes + 1)); } /*-------------------------------------------------- @@ -162,21 +162,11 @@ static boolean K_BotHatesThisSectorsSpecial(player_t *player, sector_t *sec) } /*-------------------------------------------------- - static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) + boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) - Tells us if a bot will play more careful around - this sector. Checks FOFs in the sector, as well. - - Input Arguments:- - player - Player to check against. - sec - Sector to check against. - x - Linedef cross X position, for slopes - y - Linedef cross Y position, for slopes - - Return:- - true if avoiding this sector, false otherwise. + See header file for description. --------------------------------------------------*/ -static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) +boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, fixed_t y) { const boolean flip = (player->mo->eflags & MFE_VERTICALFLIP); INT32 specialflag = 0; @@ -257,171 +247,6 @@ static boolean K_BotHatesThisSector(player_t *player, sector_t *sec, fixed_t x, return K_BotHatesThisSectorsSpecial(player, bestsector); } -/*-------------------------------------------------- - static boolean K_FindBlockingWalls(line_t *line) - - Blockmap search function. - Reels the bot prediction back in based on solid walls - or other obstacles surrounding the bot. - - Input Arguments:- - line - Linedef passed in from iteration. - - Return:- - true continues searching, false ends the search early. ---------------------------------------------------*/ -static boolean K_FindBlockingWalls(line_t *line) -{ - // Condensed version of PIT_CheckLine - const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); - fixed_t maxstep = maxstepmove; - fixed_t linedist = INT32_MAX; - INT32 lineside = 0; - vertex_t pos; - - if (!globalsmuggle.botmo || P_MobjWasRemoved(globalsmuggle.botmo) || !globalsmuggle.botmo->player) - { - return false; - } - - if (line->polyobj && !(line->polyobj->flags & POF_SOLID)) - { - return true; - } - - if (tmbbox[BOXRIGHT] <= line->bbox[BOXLEFT] || tmbbox[BOXLEFT] >= line->bbox[BOXRIGHT] - || tmbbox[BOXTOP] <= line->bbox[BOXBOTTOM] || tmbbox[BOXBOTTOM] >= line->bbox[BOXTOP]) - { - return true; - } - - if (P_BoxOnLineSide(tmbbox, line) != -1) - { - return true; - } - - lineside = P_PointOnLineSide(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line); - - // one sided line - if (!line->backsector) - { - if (lineside) - { - // don't hit the back side - return true; - } - - goto blocked; - } - - if ((line->flags & ML_IMPASSABLE) || (line->flags & ML_BLOCKPLAYERS)) - { - goto blocked; - } - - // set openrange, opentop, openbottom - P_LineOpening(line, globalsmuggle.botmo); - - if (globalsmuggle.botmo->player->waterskip) - maxstep += maxstepmove; - - if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 13, false)) - maxstep <<= 1; - else if (P_MobjTouchingSectorSpecial(globalsmuggle.botmo, 1, 12, false)) - maxstep = 0; - - if ((openrange < globalsmuggle.botmo->height) // doesn't fit - || (opentop - globalsmuggle.botmo->z < globalsmuggle.botmo->height) // mobj is too high - || (openbottom - globalsmuggle.botmo->z > maxstep)) // too big a step up - { - goto blocked; - } - - // Treat damage sectors like walls - P_ClosestPointOnLine(globalsmuggle.botmo->x, globalsmuggle.botmo->y, line, &pos); - - if (lineside) - { - if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->frontsector, pos.x, pos.y)) - goto blocked; - } - else - { - if (K_BotHatesThisSector(globalsmuggle.botmo->player, line->backsector, pos.x, pos.y)) - goto blocked; - } - - // We weren't blocked! - return true; - -blocked: - linedist = K_DistanceOfLineFromPoint(line->v1->x, line->v1->y, line->v2->x, line->v2->y, globalsmuggle.botmo->x, globalsmuggle.botmo->y); - linedist -= (globalsmuggle.botmo->radius * 8); // Maintain a reasonable distance away from it - - if (linedist > globalsmuggle.distancetocheck) - { - return true; - } - - if (linedist <= 0) - { - globalsmuggle.closestlinedist = 0; - return false; - } - - if (linedist < globalsmuggle.closestlinedist) - { - globalsmuggle.closestlinedist = linedist; - } - - return true; -} - -/*-------------------------------------------------- - fixed_t K_BotReducePrediction(player_t *player) - - See header file for description. ---------------------------------------------------*/ -fixed_t K_BotReducePrediction(player_t *player) -{ - INT32 xl, xh, yl, yh, bx, by; - - globalsmuggle.botmo = player->mo; - globalsmuggle.distancetocheck = (player->mo->radius * 32); - globalsmuggle.closestlinedist = INT32_MAX; - - tmx = player->mo->x; - tmy = player->mo->y; - - xl = (unsigned)(tmx - globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - xh = (unsigned)(tmx + globalsmuggle.distancetocheck - bmaporgx)>>MAPBLOCKSHIFT; - yl = (unsigned)(tmy - globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - yh = (unsigned)(tmy + globalsmuggle.distancetocheck - bmaporgy)>>MAPBLOCKSHIFT; - - BMBOUNDFIX(xl, xh, yl, yh); - - tmbbox[BOXTOP] = tmy + globalsmuggle.distancetocheck; - tmbbox[BOXBOTTOM] = tmy - globalsmuggle.distancetocheck; - tmbbox[BOXRIGHT] = tmx + globalsmuggle.distancetocheck; - tmbbox[BOXLEFT] = tmx - globalsmuggle.distancetocheck; - - // Check for lines that the bot might collide with - for (bx = xl; bx <= xh; bx++) - { - for (by = yl; by <= yh; by++) - { - P_BlockLinesIterator(bx, by, K_FindBlockingWalls); - } - } - - if (globalsmuggle.closestlinedist == INT32_MAX) - { - return FRACUNIT; - } - - return (FRACUNIT/2) + (FixedDiv(globalsmuggle.closestlinedist, globalsmuggle.distancetocheck) / 2); -} - /*-------------------------------------------------- static void K_AddAttackObject(mobj_t *thing, UINT8 side, UINT8 weight) diff --git a/src/k_kart.c b/src/k_kart.c index 6b4a3dfc0..822e103be 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2682,7 +2682,7 @@ boolean K_ApplyOffroad(player_t *player) boolean K_SlopeResistance(player_t *player) { - if (player->invincibilitytimer || player->sneakertimer || player->tiregrease) + if (player->invincibilitytimer || player->sneakertimer || player->tiregrease || player->flamedash) return true; return false; } @@ -8634,8 +8634,6 @@ void K_MoveKartPlayer(player_t *player, boolean onground) boolean HOLDING_ITEM = (player->pflags & (PF_ITEMOUT|PF_EGGMANOUT)); boolean NO_HYUDORO = (player->stealingtimer == 0); - player->pflags &= ~PF_HITFINISHLINE; - if (!player->exiting) { if (player->oldposition < player->position) // But first, if you lost a place, diff --git a/src/p_enemy.c b/src/p_enemy.c index 7d4ad7400..749d0dc09 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -13254,6 +13254,8 @@ void A_ChangeHeight(mobj_t *actor) void A_ItemPop(mobj_t *actor) { + INT32 locvar1 = var1; + mobj_t *remains; mobjtype_t explode; @@ -13308,7 +13310,9 @@ void A_ItemPop(mobj_t *actor) if (actor->info->deathsound) S_StartSound(remains, actor->info->deathsound); - if (!((gametyperules & GTR_BUMPERS) && actor->target->player->bumpers <= 0)) + if (locvar1 == 1) + P_GivePlayerSpheres(actor->target->player, actor->extravalue1); + else if (locvar1 == 0) actor->target->player->itemroulette = 1; remains->flags2 &= ~MF2_AMBUSH; @@ -14143,7 +14147,6 @@ void A_LandMineExplode(mobj_t *actor) INT32 colour = SKINCOLOR_KETCHUP; // we spell words properly here INT32 i; mobj_t *smoldering; - mobj_t *dust; if (LUA_CallAction(A_LANDMINEEXPLODE, actor)) return; @@ -14161,18 +14164,6 @@ void A_LandMineExplode(mobj_t *actor) P_SetScale(smoldering, actor->scale); smoldering->tics = TICRATE*3; - // Spawn a ring: - for (i = 0; i < 32; i++) - { - dust = P_SpawnMobj(actor->x, actor->y, actor->z, MT_SMOKE); - P_SetMobjState(dust, S_OPAQUESMOKE1); - dust->angle = (ANGLE_180/16) * i; - P_SetScale(dust, actor->scale); - dust->destscale = actor->scale*4; - dust->scalespeed = actor->scale/4; - P_InstaThrust(dust, dust->angle, FixedMul(20*FRACUNIT, actor->scale)); - } - actor->fuse = actor->tics; // disappear when this state ends. // spawn a few physics explosions diff --git a/src/p_inter.c b/src/p_inter.c index 0a2bb4e13..4dafa91c2 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -268,12 +268,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!P_CanPickupItem(player, 1)) return; - if ((gametyperules & GTR_BUMPERS) && player->bumpers <= 0) - { - return; - } - special->momx = special->momy = special->momz = 0; + P_SetTarget(&special->target, toucher); + P_KillMobj(special, toucher, toucher, DMG_NORMAL); + break; + case MT_SPHEREBOX: + if (player->bumpers <= 0) + return; + P_SetTarget(&special->target, toucher); P_KillMobj(special, toucher, toucher, DMG_NORMAL); break; @@ -510,16 +512,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!(P_CanPickupItem(player, 0))) return; - // Reached the cap, don't waste 'em! - if (player->spheres >= 40) - return; - - // Not alive - if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0)) - return; - - special->momx = special->momy = special->momz = 0; - player->spheres++; + P_GivePlayerSpheres(player, 1); break; // Secret emblem thingy diff --git a/src/p_local.h b/src/p_local.h index db9e9e6dd..227d915f8 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -175,7 +175,7 @@ void P_RestoreMusic(player_t *player); boolean P_EndingMusic(player_t *player); mobj_t *P_SpawnGhostMobj(mobj_t *mobj); INT32 P_GivePlayerRings(player_t *player, INT32 num_rings); -void P_GivePlayerSpheres(player_t *player, INT32 num_spheres); +INT32 P_GivePlayerSpheres(player_t *player, INT32 num_spheres); void P_GivePlayerLives(player_t *player, INT32 numlives); UINT8 P_GetNextEmerald(void); void P_GiveEmerald(boolean spawnObj); @@ -404,6 +404,8 @@ boolean P_IsLineBlocking(const line_t *ld, const mobj_t *thing); boolean P_IsLineTripWire(const line_t *ld); boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y); boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam); +fixed_t P_BaseStepUp(void); +fixed_t P_GetThingStepUp(mobj_t *thing); boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff); boolean P_Move(mobj_t *actor, fixed_t speed); boolean P_SetOrigin(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z); @@ -413,6 +415,7 @@ void P_BouncePlayerMove(mobj_t *mo); void P_BounceMove(mobj_t *mo); boolean P_CheckSight(mobj_t *t1, mobj_t *t2); boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2); +boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2); void P_CheckHoopPosition(mobj_t *hoopthing, fixed_t x, fixed_t y, fixed_t z, fixed_t radius); boolean P_CheckSector(sector_t *sector, boolean crunch); diff --git a/src/p_map.c b/src/p_map.c index dc2987f31..28487e2e3 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -2431,6 +2431,42 @@ static boolean P_WaterStepUp(mobj_t *thing) P_WaterRunning(thing); } +fixed_t P_BaseStepUp(void) +{ + return FixedMul(MAXSTEPMOVE, mapobjectscale); +} + +fixed_t P_GetThingStepUp(mobj_t *thing) +{ + const fixed_t maxstepmove = P_BaseStepUp(); + fixed_t maxstep = maxstepmove; + + if (thing->type == MT_SKIM) + { + // Skim special (not needed for kart?) + return 0; + } + + if (P_WaterStepUp(thing) == true) + { + // Add some extra stepmove when waterskipping + maxstep += maxstepmove; + } + + if (P_MobjTouchingSectorSpecial(thing, 1, 13, false)) + { + // If using type Section1:13, double the maxstep. + maxstep <<= 1; + } + else if (P_MobjTouchingSectorSpecial(thing, 1, 12, false)) + { + // If using type Section1:12, no maxstep. For short walls, like Egg Zeppelin + maxstep = 0; + } + + return maxstep; +} + // // P_TryMove // Attempt to move to a new position. @@ -2492,21 +2528,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (!(thing->flags & MF_NOCLIP)) { //All things are affected by their scale. - const fixed_t maxstepmove = FixedMul(MAXSTEPMOVE, mapobjectscale); - fixed_t maxstep = maxstepmove; - - if (thing->player && P_WaterStepUp(thing)) - maxstep += maxstepmove; // Add some extra stepmove when waterskipping - - // If using type Section1:13, double the maxstep. - if (P_MobjTouchingSectorSpecial(thing, 1, 13, false)) - maxstep <<= 1; - // If using type Section1:12, no maxstep. For short walls, like Egg Zeppelin - else if (P_MobjTouchingSectorSpecial(thing, 1, 12, false)) - maxstep = 0; - - if (thing->type == MT_SKIM) - maxstep = 0; + fixed_t maxstep = P_GetThingStepUp(thing); if (tmceilingz - tmfloorz < thing->height) { @@ -2743,7 +2765,7 @@ boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y) if (!(thing->flags & MF_NOCLIP)) { - const fixed_t maxstep = FixedMul(MAXSTEPMOVE, mapobjectscale); + const fixed_t maxstep = P_BaseStepUp(); if (tmceilingz - tmfloorz < thing->height) return false; // doesn't fit @@ -3205,7 +3227,7 @@ static boolean PTR_LineIsBlocking(line_t *li) if (opentop - slidemo->z < slidemo->height) return true; // mobj is too high - if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, mapobjectscale)) + if (openbottom - slidemo->z > P_GetThingStepUp(slidemo)) return true; // too big a step up return false; diff --git a/src/p_mobj.c b/src/p_mobj.c index 5a9f0925e..afcc87c3e 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3059,14 +3059,76 @@ void P_MobjCheckWater(mobj_t *mobj) return; // The rest of this code only executes on a water state change. - if (waterwasnotset || !!(mobj->eflags & MFE_UNDERWATER) == wasinwater) + if (!!(mobj->eflags & MFE_UNDERWATER) == wasinwater) return; + if (p && !p->waterskip && + p->curshield != KSHIELD_BUBBLE && wasinwater) + { + S_StartSound(mobj, sfx_s3k38); + } + if ((p) // Players || (mobj->flags & MF_PUSHABLE) // Pushables || ((mobj->info->flags & MF_PUSHABLE) && mobj->fuse) // Previously pushable, might be moving still ) { + // Time to spawn the bubbles! + { + INT32 i; + INT32 bubblecount; + UINT8 prandom[4]; + mobj_t *bubble; + mobjtype_t bubbletype; + + if (mobj->eflags & MFE_GOOWATER || wasingoo) + S_StartSound(mobj, sfx_ghit); + else if (mobj->eflags & MFE_TOUCHLAVA) + S_StartSound(mobj, sfx_splash); + else + S_StartSound(mobj, sfx_splish); // And make a sound! + + bubblecount = FixedDiv(abs(mobj->momz), mobj->scale)>>(FRACBITS-1); + // Max bubble count + if (bubblecount > 128) + bubblecount = 128; + + // Create tons of bubbles + for (i = 0; i < bubblecount; i++) + { + // P_RandomByte()s are called individually to allow consistency + // across various compilers, since the order of function calls + // in C is not part of the ANSI specification. + prandom[0] = P_RandomByte(); + prandom[1] = P_RandomByte(); + prandom[2] = P_RandomByte(); + prandom[3] = P_RandomByte(); + + bubbletype = MT_SMALLBUBBLE; + if (!(prandom[0] & 0x3)) // medium bubble chance up to 64 from 32 + bubbletype = MT_MEDIUMBUBBLE; + + bubble = P_SpawnMobj( + mobj->x + FixedMul((prandom[1]<<(FRACBITS-3)) * (prandom[0]&0x80 ? 1 : -1), mobj->scale), + mobj->y + FixedMul((prandom[2]<<(FRACBITS-3)) * (prandom[0]&0x40 ? 1 : -1), mobj->scale), + mobj->z + FixedMul((prandom[3]<<(FRACBITS-2)), mobj->scale), bubbletype); + + if (bubble) + { + if (P_MobjFlip(mobj)*mobj->momz < 0) + bubble->momz = mobj->momz >> 4; + else + bubble->momz = 0; + + bubble->destscale = mobj->scale; + P_SetScale(bubble, mobj->scale); + } + } + } + + if (waterwasnotset) + return; + // Check to make sure you didn't just cross into a sector to jump out of // that has shallower water than the block you were originally in. if ((!(mobj->eflags & MFE_VERTICALFLIP) && mobj->watertop-mobj->floorz <= height>>1) @@ -3151,59 +3213,6 @@ void P_MobjCheckWater(mobj_t *mobj) P_SetScale(splish, mobj->scale); } } - - // Time to spawn the bubbles! - { - INT32 i; - INT32 bubblecount; - UINT8 prandom[4]; - mobj_t *bubble; - mobjtype_t bubbletype; - - if (mobj->eflags & MFE_GOOWATER || wasingoo) - S_StartSound(mobj, sfx_ghit); - else if (mobj->eflags & MFE_TOUCHLAVA) - S_StartSound(mobj, sfx_splash); - else - S_StartSound(mobj, sfx_splish); // And make a sound! - - bubblecount = FixedDiv(abs(mobj->momz), mobj->scale)>>(FRACBITS-1); - // Max bubble count - if (bubblecount > 128) - bubblecount = 128; - - // Create tons of bubbles - for (i = 0; i < bubblecount; i++) - { - // P_RandomByte()s are called individually to allow consistency - // across various compilers, since the order of function calls - // in C is not part of the ANSI specification. - prandom[0] = P_RandomByte(); - prandom[1] = P_RandomByte(); - prandom[2] = P_RandomByte(); - prandom[3] = P_RandomByte(); - - bubbletype = MT_SMALLBUBBLE; - if (!(prandom[0] & 0x3)) // medium bubble chance up to 64 from 32 - bubbletype = MT_MEDIUMBUBBLE; - - bubble = P_SpawnMobj( - mobj->x + FixedMul((prandom[1]<<(FRACBITS-3)) * (prandom[0]&0x80 ? 1 : -1), mobj->scale), - mobj->y + FixedMul((prandom[2]<<(FRACBITS-3)) * (prandom[0]&0x40 ? 1 : -1), mobj->scale), - mobj->z + FixedMul((prandom[3]<<(FRACBITS-2)), mobj->scale), bubbletype); - - if (bubble) - { - if (P_MobjFlip(mobj)*mobj->momz < 0) - bubble->momz = mobj->momz >> 4; - else - bubble->momz = 0; - - bubble->destscale = mobj->scale; - P_SetScale(bubble, mobj->scale); - } - } - } } } @@ -4095,7 +4104,7 @@ boolean P_BossTargetPlayer(mobj_t *actor, boolean closest) player = &players[actor->lastlook]; - if (player->bot || player->spectator) + if (player->spectator) continue; // ignore notarget if (!player->mo || P_MobjWasRemoved(player->mo)) @@ -8597,6 +8606,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) } break; case MT_RANDOMITEM: + case MT_SPHEREBOX: if (gametype == GT_BATTLE && mobj->threshold == 70) { mobj->color = K_RainbowColor(leveltime); @@ -9377,6 +9387,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing) thing->shadowscale = 12*FRACUNIT/5; break; case MT_RANDOMITEM: + case MT_SPHEREBOX: thing->shadowscale = FRACUNIT/2; thing->whiteshadow = false; break; diff --git a/src/p_sight.c b/src/p_sight.c index 38a50df36..cdc8df864 100644 --- a/src/p_sight.c +++ b/src/p_sight.c @@ -18,6 +18,9 @@ #include "r_main.h" #include "r_state.h" +#include "k_bot.h" // K_BotHatesThisSector +#include "k_kart.h" // K_TripwirePass + // // P_CheckSight // @@ -572,7 +575,7 @@ static boolean P_CrossBlockingSubsector(size_t num, register traceblocking_t *tb if (P_IsLineBlocking(line, tb->compareThing) == true) { - // This line will block us + // This line will always block us return false; } } @@ -656,3 +659,192 @@ boolean P_TraceBlockingLines(mobj_t *t1, mobj_t *t2) // the head node is the last node output return P_CrossBSPNodeBlocking((INT32)numnodes - 1, &tb); } + +// +// ANOTHER version, this time for bot traversal. +// (TODO: since we have so many versions of this function, the differences +// should maybe just be a function var that gets called?) +// + +static boolean P_CrossBotTraversalSubsector(size_t num, register traceblocking_t *tb) +{ + seg_t *seg; + INT32 count; + +#ifdef RANGECHECK + if (num >= numsubsectors) + I_Error("P_CrossBotTraversalSubsector: ss %s with numss = %s\n", sizeu1(num), sizeu2(numsubsectors)); +#endif + + // haleyjd 02/23/06: this assignment should be after the above check + seg = segs + subsectors[num].firstline; + + for (count = subsectors[num].numlines; --count >= 0; seg++) // check lines + { + line_t *line = seg->linedef; + divline_t divl; + const vertex_t *v1,*v2; + fixed_t maxstep = INT32_MAX; + + if (seg->glseg) + continue; + + // already checked other side? + if (line->validcount == validcount) + continue; + + line->validcount = validcount; + + // OPTIMIZE: killough 4/20/98: Added quick bounding-box rejection test + if (line->bbox[BOXLEFT ] > tb->bbox[BOXRIGHT ] || + line->bbox[BOXRIGHT ] < tb->bbox[BOXLEFT ] || + line->bbox[BOXBOTTOM] > tb->bbox[BOXTOP ] || + line->bbox[BOXTOP] < tb->bbox[BOXBOTTOM]) + continue; + + v1 = line->v1; + v2 = line->v2; + + // line isn't crossed? + if (P_DivlineSide(v1->x, v1->y, &tb->strace) == + P_DivlineSide(v2->x, v2->y, &tb->strace)) + continue; + + // stop because it is not two sided anyway + if (!(line->flags & ML_TWOSIDED)) + return false; + + divl.dx = v2->x - (divl.x = v1->x); + divl.dy = v2->y - (divl.y = v1->y); + + // line isn't crossed? + if (P_DivlineSide(tb->strace.x, tb->strace.y, &divl) == + P_DivlineSide(tb->t2x, tb->t2y, &divl)) + continue; + + if (P_IsLineBlocking(line, tb->compareThing) == true) + { + // This line will always block us + return false; + } + + // set openrange, opentop, openbottom + P_LineOpening(line, tb->compareThing); + maxstep = P_GetThingStepUp(tb->compareThing); + + if ((openrange < tb->compareThing->height) // doesn't fit + || (opentop - tb->compareThing->z < tb->compareThing->height) // mobj is too high + || (openbottom - tb->compareThing->z > maxstep)) // too big a step up + { + // This line situationally blocks us + return false; + } + + // Treat damage sectors like walls + if (tb->compareThing->player != NULL) + { + boolean alreadyHates = K_BotHatesThisSector(tb->compareThing->player, tb->compareThing->subsector->sector, tb->compareThing->x, tb->compareThing->y); + + if (alreadyHates == false) + { + INT32 lineside = 0; + vertex_t pos; + + P_ClosestPointOnLine(tb->compareThing->x, tb->compareThing->y, line, &pos); + lineside = P_PointOnLineSide(tb->compareThing->x, tb->compareThing->y, line); + + if (K_BotHatesThisSector(tb->compareThing->player, ((lineside == 1) ? line->frontsector : line->backsector), pos.x, pos.y)) + { + // This line does not block us, but we don't want to be in it. + return false; + } + } + + if (P_IsLineTripWire(line) == true && K_TripwirePass(tb->compareThing->player) == false) + { + // Can't go through trip wire. + return false; + } + } + } + + // passed the subsector ok + return true; +} + +static boolean P_CrossBSPNodeBotTraversal(INT32 bspnum, register traceblocking_t *tb) +{ + while (!(bspnum & NF_SUBSECTOR)) + { + register node_t *bsp = nodes + bspnum; + INT32 side = P_DivlineSide(tb->strace.x,tb->strace.y,(divline_t *)bsp)&1; + if (side == P_DivlineSide(tb->t2x, tb->t2y, (divline_t *) bsp)) + bspnum = bsp->children[side]; // doesn't touch the other side + else // the partition plane is crossed here + { + if (!P_CrossBSPNodeBotTraversal(bsp->children[side], tb)) + return false; // cross the starting side + else + bspnum = bsp->children[side^1]; // cross the ending side + } + } + + return P_CrossBotTraversalSubsector((bspnum == -1 ? 0 : bspnum & ~NF_SUBSECTOR), tb); +} + +boolean P_TraceBotTraversal(mobj_t *t1, mobj_t *t2) +{ + const sector_t *s1, *s2; + size_t pnum; + traceblocking_t tb; + + // First check for trivial rejection. + if (!t1 || !t2) + return false; + + I_Assert(!P_MobjWasRemoved(t1)); + I_Assert(!P_MobjWasRemoved(t2)); + + if (!t1->subsector || !t2->subsector + || !t1->subsector->sector || !t2->subsector->sector) + return false; + + s1 = t1->subsector->sector; + s2 = t2->subsector->sector; + pnum = (s1-sectors)*numsectors + (s2-sectors); + + if (rejectmatrix != NULL) + { + // Check in REJECT table. + if (rejectmatrix[pnum>>3] & (1 << (pnum&7))) // can't possibly be connected + return false; + } + + // killough 11/98: shortcut for melee situations + // same subsector? obviously visible + // haleyjd 02/23/06: can't do this if there are polyobjects in the subsec + if (!t1->subsector->polyList && + t1->subsector == t2->subsector) + return true; + + validcount++; + + tb.strace.dx = (tb.t2x = t2->x) - (tb.strace.x = t1->x); + tb.strace.dy = (tb.t2y = t2->y) - (tb.strace.y = t1->y); + + if (t1->x > t2->x) + tb.bbox[BOXRIGHT] = t1->x, tb.bbox[BOXLEFT] = t2->x; + else + tb.bbox[BOXRIGHT] = t2->x, tb.bbox[BOXLEFT] = t1->x; + + if (t1->y > t2->y) + tb.bbox[BOXTOP] = t1->y, tb.bbox[BOXBOTTOM] = t2->y; + else + tb.bbox[BOXTOP] = t2->y, tb.bbox[BOXBOTTOM] = t1->y; + + tb.compareThing = t1; + + // the head node is the last node output + return P_CrossBSPNodeBotTraversal((INT32)numnodes - 1, &tb); +} + diff --git a/src/p_user.c b/src/p_user.c index 5f5242e66..4837022e2 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -500,6 +500,26 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings) return num_rings; } +INT32 P_GivePlayerSpheres(player_t *player, INT32 num_spheres) +{ + num_spheres += player->spheres; + + // Not alive + if ((gametyperules & GTR_BUMPERS) && (player->bumpers <= 0)) + return 0; + + if (num_spheres > 40) // Reached the cap, don't waste 'em! + num_spheres = 40; + else if (num_spheres < 0) + num_spheres = 0; + + num_spheres -= player->spheres; + + player->spheres += num_spheres; + + return num_spheres; +} + // // P_GivePlayerLives // @@ -2502,6 +2522,8 @@ static void P_ConsiderAllGone(void) // static void P_DeathThink(player_t *player) { + boolean playerGone = false; + player->deltaviewheight = 0; if (player->deadtimer < INT32_MAX) @@ -2522,7 +2544,19 @@ static void P_DeathThink(player_t *player) K_KartPlayerHUDUpdate(player); - if (player->lives > 0 && !(player->pflags & PF_NOCONTEST) && player->deadtimer > TICRATE) + if (player->pflags & PF_NOCONTEST) + { + playerGone = true; + } + else if (player->bot == false) + { + if (G_GametypeUsesLives() == true && player->lives == 0) + { + playerGone = true; + } + } + + if (playerGone == false && player->deadtimer > TICRATE) { player->playerstate = PST_REBORN; } @@ -4222,9 +4256,6 @@ void P_PlayerThink(player_t *player) ticcmd_t *cmd; const size_t playeri = (size_t)(player - players); - player->old_drawangle = player->drawangle; - player->old_viewrollangle = player->viewrollangle; - #ifdef PARANOIA if (!player->mo) I_Error("p_playerthink: players[%s].mo == NULL", sizeu1(playeri)); @@ -4237,6 +4268,11 @@ void P_PlayerThink(player_t *player) player->playerstate = PST_DEAD; } + player->old_drawangle = player->drawangle; + player->old_viewrollangle = player->viewrollangle; + + player->pflags &= ~PF_HITFINISHLINE; + if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj)) { P_SetTarget(&player->awayviewmobj, NULL); // remove awayviewmobj asap if invalid diff --git a/src/r_draw.c b/src/r_draw.c index 4adfb6663..377cca156 100644 --- a/src/r_draw.c +++ b/src/r_draw.c @@ -26,6 +26,7 @@ #include "z_zone.h" #include "console.h" // Until buffering gets finished #include "k_color.h" // SRB2kart +#include "i_threads.h" #ifdef HWRENDER #include "hardware/hw_main.h" @@ -192,6 +193,29 @@ CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for " #define TRANSTAB_AMTMUL10 (255.0f / 10.0f) +static void R_AllocateBlendTables(void) +{ + INT32 i; + + for (i = 0; i < NUMBLENDMAPS; i++) + { + if (i == blendtab_modulate) + continue; + blendtables[i] = Z_MallocAlign((NUMTRANSTABLES + 1) * 0x10000, PU_STATIC, NULL, 16); + } + + // Modulation blending only requires a single table + blendtables[blendtab_modulate] = Z_MallocAlign(0x10000, PU_STATIC, NULL, 16); +} + +#ifdef HAVE_THREADS +static void R_GenerateBlendTables_Thread(void *userdata) +{ + (void)userdata; + R_GenerateBlendTables(); +} +#endif + /** \brief Initializes the translucency tables used by the Software renderer. */ void R_InitTranslucencyTables(void) @@ -212,20 +236,20 @@ void R_InitTranslucencyTables(void) W_ReadLump(W_GetNumForName("TRANS80"), transtables+0x70000); W_ReadLump(W_GetNumForName("TRANS90"), transtables+0x80000); + R_AllocateBlendTables(); + +#ifdef HAVE_THREADS + I_spawn_thread("blend-tables", + R_GenerateBlendTables_Thread, NULL); +#else R_GenerateBlendTables(); +#endif } void R_GenerateBlendTables(void) { INT32 i; - for (i = 0; i < NUMBLENDMAPS; i++) - { - if (i == blendtab_modulate) - continue; - blendtables[i] = Z_MallocAlign((NUMTRANSTABLES + 1) * 0x10000, PU_STATIC, NULL, 16); - } - for (i = 0; i <= 9; i++) { const size_t offs = (0x10000 * i); @@ -236,8 +260,6 @@ void R_GenerateBlendTables(void) R_GenerateTranslucencyTable(blendtables[blendtab_reversesubtract] + offs, AST_REVERSESUBTRACT, alpha); } - // Modulation blending only requires a single table - blendtables[blendtab_modulate] = Z_MallocAlign(0x10000, PU_STATIC, NULL, 16); R_GenerateTranslucencyTable(blendtables[blendtab_modulate], AST_MODULATE, 0); } diff --git a/src/r_main.c b/src/r_main.c index 918c9360d..0803f5a9e 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -149,10 +149,6 @@ static void ChaseCam_OnChange(void); static void ChaseCam2_OnChange(void); static void ChaseCam3_OnChange(void); static void ChaseCam4_OnChange(void); -void SendWeaponPref(void); -void SendWeaponPref2(void); -void SendWeaponPref3(void); -void SendWeaponPref4(void); consvar_t cv_tailspickup = CVAR_INIT ("tailspickup", "On", CV_NETVAR|CV_NOSHOWHELP, CV_OnOff, NULL); consvar_t cv_chasecam[MAXSPLITSCREENPLAYERS] = { @@ -1141,7 +1137,7 @@ void R_Init(void) R_InitLightTables(); //I_OutputMsg("\nR_InitTranslucencyTables\n"); - R_InitTranslucencyTables(); + //R_InitTranslucencyTables(); R_InitDrawNodes(); diff --git a/src/r_patch.h b/src/r_patch.h index 601144a1d..b86908281 100644 --- a/src/r_patch.h +++ b/src/r_patch.h @@ -42,6 +42,7 @@ patch_t *Patch_GetRotatedSprite( INT32 R_GetRollAngle(angle_t rollangle); angle_t R_GetPitchRollAngle(mobj_t *mobj, player_t *viewPlayer); +angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer); angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer); #endif diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c index 24c1de5d2..74afeb396 100644 --- a/src/r_patchrotation.c +++ b/src/r_patchrotation.c @@ -64,10 +64,9 @@ static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer) return rollAngle; } -angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer) +angle_t R_ModelRotationAngle(mobj_t *mobj, player_t *viewPlayer) { - angle_t rollOrPitch = R_GetPitchRollAngle(mobj, viewPlayer); - angle_t rollAngle = (rollOrPitch + mobj->rollangle); + angle_t rollAngle = mobj->rollangle; if (mobj->player) { @@ -77,6 +76,12 @@ angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer) return rollAngle; } +angle_t R_SpriteRotationAngle(mobj_t *mobj, player_t *viewPlayer) +{ + angle_t rollOrPitch = R_GetPitchRollAngle(mobj, viewPlayer); + return (rollOrPitch + R_ModelRotationAngle(mobj, viewPlayer)); +} + INT32 R_GetRollAngle(angle_t rollangle) { INT32 ra = AngleFixed(rollangle)>>FRACBITS; diff --git a/src/r_picformats.h b/src/r_picformats.h index 8d3999013..0a822cc48 100644 --- a/src/r_picformats.h +++ b/src/r_picformats.h @@ -105,6 +105,7 @@ typedef struct } spriteinfo_t; // Portable Network Graphics +#define PNG_HEADER_SIZE (8) boolean Picture_IsLumpPNG(const UINT8 *d, size_t s); #define Picture_ThrowPNGError(lumpname, wadfilename) I_Error("W_Wad: Lump \"%s\" in file \"%s\" is a .png - please convert to either Doom or Flat (raw) image format.", lumpname, wadfilename); // Fears Of LJ Sonic diff --git a/src/r_skins.c b/src/r_skins.c index 1e80e18ce..ba7ed0811 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -311,11 +311,6 @@ void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) } player->skincolor = newcolor = skin->prefcolor; - if (player->bot && botingame) - { - botskin = (UINT8)(skinnum + 1); - botcolor = skin->prefcolor; - } } #endif diff --git a/src/r_textures.c b/src/r_textures.c index 9716724b5..4ec110556 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -725,6 +725,7 @@ Rloadflats (INT32 i, INT32 w) UINT16 texstart, texend; texture_t *texture; texpatch_t *patch; + UINT8 header[PNG_HEADER_SIZE]; // Yes if (wadfiles[w]->type == RET_PK3) @@ -743,7 +744,6 @@ Rloadflats (INT32 i, INT32 w) // Work through each lump between the markers in the WAD. for (j = 0; j < (texend - texstart); j++) { - UINT8 *flatlump; UINT16 wadnum = (UINT16)w; lumpnum_t lumpnum = texstart + j; size_t lumplength; @@ -755,7 +755,7 @@ Rloadflats (INT32 i, INT32 w) continue; // If it is then SKIP IT } - flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + W_ReadLumpHeaderPwad(wadnum, lumpnum, header, sizeof header, 0); lumplength = W_LumpLengthPwad(wadnum, lumpnum); switch (lumplength) @@ -790,12 +790,14 @@ Rloadflats (INT32 i, INT32 w) M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); #ifndef NO_PNG_LUMPS - if (Picture_IsLumpPNG((UINT8 *)flatlump, lumplength)) + if (Picture_IsLumpPNG(header, lumplength)) { + UINT8 *flatlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); INT32 width, height; Picture_PNGDimensions((UINT8 *)flatlump, &width, &height, NULL, NULL, lumplength); texture->width = (INT16)width; texture->height = (INT16)height; + Z_Free(flatlump); } else #endif @@ -814,8 +816,6 @@ Rloadflats (INT32 i, INT32 w) patch->lump = texstart + j; patch->flip = 0; - Z_Unlock(flatlump); - texturewidth[i] = texture->width; textureheight[i] = texture->height << FRACBITS; i++; @@ -835,8 +835,8 @@ Rloadtextures (INT32 i, INT32 w) UINT16 j; UINT16 texstart, texend, texturesLumpPos; texture_t *texture; - softwarepatch_t *patchlump; texpatch_t *patch; + softwarepatch_t patchlump; // Get the lump numbers for the markers in the WAD, if they exist. if (wadfiles[w]->type == RET_PK3) @@ -876,7 +876,7 @@ Rloadtextures (INT32 i, INT32 w) continue; // If it is then SKIP IT } - patchlump = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); + W_ReadLumpHeaderPwad(wadnum, lumpnum, &patchlump, PNG_HEADER_SIZE, 0); #ifndef NO_PNG_LUMPS lumplength = W_LumpLengthPwad(wadnum, lumpnum); #endif @@ -888,18 +888,20 @@ Rloadtextures (INT32 i, INT32 w) M_Memcpy(texture->name, W_CheckNameForNumPwad(wadnum, lumpnum), sizeof(texture->name)); #ifndef NO_PNG_LUMPS - if (Picture_IsLumpPNG((UINT8 *)patchlump, lumplength)) + if (Picture_IsLumpPNG((UINT8 *)&patchlump, lumplength)) { + UINT8 *png = W_CacheLumpNumPwad(wadnum, lumpnum, PU_CACHE); INT32 width, height; - Picture_PNGDimensions((UINT8 *)patchlump, &width, &height, NULL, NULL, lumplength); + Picture_PNGDimensions(png, &width, &height, NULL, NULL, lumplength); texture->width = (INT16)width; texture->height = (INT16)height; + Z_Free(png); } else #endif { - texture->width = SHORT(patchlump->width); - texture->height = SHORT(patchlump->height); + texture->width = SHORT(patchlump.width); + texture->height = SHORT(patchlump.height); } texture->type = TEXTURETYPE_SINGLEPATCH; @@ -915,8 +917,6 @@ Rloadtextures (INT32 i, INT32 w) patch->lump = texstart + j; patch->flip = 0; - Z_Unlock(patchlump); - texturewidth[i] = texture->width; textureheight[i] = texture->height << FRACBITS; i++; diff --git a/src/r_things.c b/src/r_things.c index c861dec2b..43feeff90 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -286,16 +286,18 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 #ifndef NO_PNG_LUMPS { - softwarepatch_t *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC); + UINT8 header[PNG_HEADER_SIZE]; size_t len = W_LumpLengthPwad(wadnum, l); - if (Picture_IsLumpPNG((UINT8 *)png, len)) + W_ReadLumpHeaderPwad(wadnum, l, header, sizeof header, 0); + + if (Picture_IsLumpPNG(header, len)) { + UINT8 *png = W_CacheLumpNumPwad(wadnum, l, PU_STATIC); Picture_PNGDimensions((UINT8 *)png, &width, &height, &topoffset, &leftoffset, len); isPNG = true; + Z_Free(png); } - - Z_Free(png); } if (!isPNG) diff --git a/src/w_wad.c b/src/w_wad.c index 4e7e97dbb..60339c426 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -1480,10 +1480,10 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si int zErr; // Helper var. z_stream strm; unsigned long rawSize = l->disksize; - unsigned long decSize = l->size; + unsigned long decSize = size; rawData = Z_Malloc(rawSize, PU_STATIC, NULL); - decData = Z_Malloc(decSize, PU_STATIC, NULL); + decData = dest; if (fread(rawData, 1, rawSize, handle) < rawSize) I_Error("wad %d, lump %d: cannot read compressed data", wad, lump); @@ -1501,12 +1501,8 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si zErr = inflateInit2(&strm, -15); if (zErr == Z_OK) { - zErr = inflate(&strm, Z_FINISH); - if (zErr == Z_STREAM_END) - { - M_Memcpy(dest, decData, size); - } - else + zErr = inflate(&strm, Z_SYNC_FLUSH); + if (zErr != Z_OK && zErr != Z_STREAM_END) { size = 0; zerr(zErr); @@ -1520,7 +1516,6 @@ size_t W_ReadLumpHeaderPwad(UINT16 wad, UINT16 lump, void *dest, size_t size, si } Z_Free(rawData); - Z_Free(decData); #ifdef NO_PNG_LUMPS if (Picture_IsLumpPNG((UINT8 *)dest, size)) diff --git a/src/y_inter.c b/src/y_inter.c index 70137f242..e9923caac 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1322,7 +1322,7 @@ static void Y_VoteStops(SINT8 pick, SINT8 level) if (gametype != votelevels[level][1]) { INT16 lastgametype = gametype; - gametype = votelevels[level][1]; + G_SetGametype(votelevels[level][1]); D_GameTypeChanged(lastgametype); forceresetplayers = true; }