diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 603239ac4..d4e256c7c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -139,6 +139,7 @@ add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 k_botsearch.cpp k_cluster.cpp k_items.c + k_itemlist.cpp k_grandprix.c k_boss.c k_hud.c diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 3ceb00a64..0c6c5f92c 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -69,6 +69,7 @@ #include "g_party.h" #include "k_specialstage.h" #include "k_items.h" +#include "k_itemlist.hpp" #define CV_RESTRICT CV_NETVAR @@ -174,6 +175,7 @@ static void KartItemPush_OnChange(void); static void KartAntiBump_OnChange(void); static void KartItemBreaker_OnChange(void); static void KartBumpSpark_OnChange(void); +static void KartItemList_OnChange(void); static void Schedule_OnChange(void); @@ -605,6 +607,8 @@ consvar_t cv_kartlegacyspbdist = CVAR_INIT ("kartlegacyspbdist", "2216", CV_NETV // SPB Rush toggle; toggles on/off the more aggressive "pressure" odds when an SPB is active consvar_t cv_kartspbrush = CVAR_INIT ("kartspbrush", "On", CV_NETVAR, CV_OnOff, NULL); +consvar_t cv_itemlist = CVAR_INIT ("kartitemlist", "On", CV_NETVAR|CV_CALL|CV_NOINIT, CV_OnOff, KartItemList_OnChange); + // Time limit for Alt. Shrink static CV_PossibleValue_t altshrinktime_cons_t[] = {{0, "MIN"}, {(INT16_MAX / TICRATE), "MAX"}, {0, NULL}}; consvar_t cv_kartaltshrinktime = CVAR_INIT ("kartaltshrinktime", "14", CV_NETVAR|CV_CHEAT|CV_GUARD, altshrinktime_cons_t, NULL); @@ -1075,6 +1079,8 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_dummyconsvar); CV_RegisterVar(&cv_encorevotes); + CV_RegisterVar(&cv_itemlist); + #ifdef USE_STUN CV_RegisterVar(&cv_stunserver); #endif @@ -6527,6 +6533,8 @@ static void Got_Cheat(UINT8 **cp, INT32 playernum) } else { + K_AddItemRollToList((INT32)(player - players), item, amt); + CV_CheaterWarning(playernum, va("give item %s x%d", DEH_KartItemName(item), amt)); } break; @@ -8350,6 +8358,39 @@ static void KartBumpSpark_OnChange(void) } } +static void KartItemList_OnChange(void) +{ + if (K_CanChangeRules(false) == false) + { + return; + } + + if (!K_ItemListActive() && cv_itemlist.value) + { + if (leveltime < starttime) + { + itemlistactive = true; + CONS_Printf(M_GetText("Item rolls will be displayed.\n")); + } + else + { + CONS_Printf(M_GetText("Item rolls will be displayed next round.\n")); + } + } + else if (K_ItemListActive() && !cv_itemlist.value) + { + if (leveltime < starttime) + { + itemlistactive = false; + CONS_Printf(M_GetText("Item rolls will not be displayed.\n")); + } + else + { + CONS_Printf(M_GetText("Item rolls will not be displayed next round.\n")); + } + } +} + static void Schedule_OnChange(void) { size_t i; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index c569726cd..45de60434 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -221,6 +221,8 @@ extern consvar_t cv_kartlegacyspbdist; extern consvar_t cv_kartspbrush; +extern consvar_t cv_itemlist; + extern consvar_t cv_encorevotes; extern consvar_t cv_votetime; diff --git a/src/k_hud.c b/src/k_hud.c index e356b3bb7..bd4e97e85 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -136,7 +136,7 @@ static patch_t *kp_racefinish[6]; static patch_t *kp_positionnum[NUMPOSNUMS][NUMPOSFRAMES]; static patch_t *kp_winnernum[NUMPOSFRAMES]; -static patch_t *kp_facenum[MAXPLAYERS+1]; +patch_t *kp_facenum[MAXPLAYERS+1]; static patch_t *kp_facehighlight[8]; static patch_t *kp_nocontestminimap; diff --git a/src/k_hud.h b/src/k_hud.h index 65f4bf8b2..d1c34057c 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -66,6 +66,8 @@ extern consvar_t cv_colorizedhudcolor; extern consvar_t cv_newtabranking; +extern patch_t *kp_facenum[MAXPLAYERS+1]; + struct trackingResult_t { fixed_t x, y; diff --git a/src/k_itemlist.cpp b/src/k_itemlist.cpp new file mode 100644 index 000000000..2b17e31be --- /dev/null +++ b/src/k_itemlist.cpp @@ -0,0 +1,151 @@ +#include + +#include "byteptr.h" +#include "d_player.h" +#include "doomdef.h" +#include "k_items.h" +#include "p_saveg.h" +#include "v_video.h" + +extern "C" +{ + std::unordered_map itemcounter[MAXPLAYERS]; + + // + // Clears out all item lists for all players. Should only run during race setup. + // + void K_ClearItemRollLists(void) + { + INT32 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + itemcounter[i].clear(); + } + } + + // + // Adds an item roll to the item roll list. + // If an entry for the item already exists, this function adds to its roll count. + // + void K_AddItemRollToList(INT32 pid, kartitemtype_e item, INT32 amount) + { + if (auto search = itemcounter[pid].find(item); search != itemcounter[pid].end()) + itemcounter[pid][item] += amount; + else + itemcounter[pid][item] = amount; // If this roll didn't exist before, it does now :^) + } + + // + // Gets the size of the item roll list. Primarily used for syncing netgames. + // + UINT32 K_GetItemListSize(INT32 pid) + { + return static_cast(itemcounter[pid].size()); + } + + // + // Sets an entry in the item roll list to a specific value. + // + void K_SetItemListEntry(INT32 pid, kartitemtype_e item, INT32 num) + { + itemcounter[pid][item] = num; + } + + // + // Gets the value of an entry in the item roll list, given it exists. + // If nothing is found, this value returns 0. + // + UINT32 K_GetItemListEntry(INT32 pid, kartitemtype_e item) + { + if (auto search = itemcounter[pid].find(item); search != itemcounter[pid].end()) + { + return itemcounter[pid][item]; + } + + return 0; + } + + // + // C and C++ are like oil and water at points; we need to synch every entry in the unordered + // map by doing... this. + // + UINT32 K_SyncItemList(savebuffer_t* save, INT32 pid) + { + UINT32 buffer_size; + + if (save->write) + { + // Buffer size = list size * 2 (2 entries for each buffer element) + buffer_size = static_cast(itemcounter[pid].size()); + + // First, write the size of the buffer we're about to send over. + WRITEUINT32(save->p, buffer_size); + + // Then, comb through the map and write every entry in it to the save buffer. + for (const auto& n : itemcounter[pid]) + { + WRITEUINT32(save->p, static_cast(n.first)); + WRITEUINT32(save->p, static_cast(n.second)); + } + + return buffer_size; + } + else + { + // Get the buffer size. + buffer_size = READUINT32(save->p); + + UINT32 i, item, amt; + for (i = 0; i < buffer_size; i++) + { + // Loop through the save buffer and write everything we find into the given item + // list. + item = READUINT32(save->p); + amt = READUINT32(save->p); + + itemcounter[pid][static_cast(item)] = amt; + } + + return buffer_size; + } + + return 0; + } + +#define ITEMLOG_SPACE 1 + + // Draws an item in the player's item list. Draws nothing if the list is empty. + fixed_t K_DrawItemList(INT32 pid, fixed_t x, fixed_t y) + { + if (itemcounter[pid].size() < 1) + return 0; + + patch_t* localpatch; + fixed_t dx = x; + INT32 ir_length; + + for (const auto& n : itemcounter[pid]) + { + if (n.second < 1) + continue; + + localpatch = K_GetCachedItemPatch(n.first, true, 0); + ir_length = static_cast(strlen(va("%d", n.second))); + + V_DrawFixedPatch(dx, y - (25 * FRACUNIT / 6), FRACUNIT / 3, 0, localpatch, NULL); + + V_DrawSmallString((dx / FRACUNIT) + ((5 + (std::max(0, ir_length - 1) * 3))), + (y / FRACUNIT) + 2, + V_ALLOWLOWERCASE | V_6WIDTHSPACE, + va("%d", n.second)); + + dx += (6 + std::max(0, (ir_length * 3) - 3) + ITEMLOG_SPACE) * FRACUNIT; + } + + // Return the overall "width" of the list. + return dx - x; + } + +#undef ITEMLOG_SPACE +} diff --git a/src/k_itemlist.hpp b/src/k_itemlist.hpp new file mode 100644 index 000000000..4ab4aa2c8 --- /dev/null +++ b/src/k_itemlist.hpp @@ -0,0 +1,24 @@ +#include "doomdef.h" +#include "k_items.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + void K_ClearItemRollLists(void); + void K_AddItemRollToList(INT32 pid, kartitemtype_e item, INT32 amount); + fixed_t K_DrawItemList(INT32 pid, fixed_t x, fixed_t y); + UINT32 K_GetItemListSize(INT32 pid); + + // Getter and setter + + void K_SetItemListEntry(INT32 pid, kartitemtype_e item, INT32 num); + UINT32 K_GetItemListEntry(INT32 pid, kartitemtype_e item); + + // Netsynch; returns the size of the list buffer, always. + UINT32 K_SyncItemList(savebuffer_t *save, INT32 pid); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/src/k_items.c b/src/k_items.c index 2e2416178..62ecabf6e 100644 --- a/src/k_items.c +++ b/src/k_items.c @@ -38,6 +38,7 @@ #include "k_waypoint.h" #include "k_director.h" #include "k_cluster.hpp" +#include "k_itemlist.hpp" #include "k_items.h" #include "k_collide.h" @@ -379,6 +380,9 @@ void K_AwardPlayerItem(player_t *player, kartitemtype_e type, UINT8 amount, kart amount *= 2; player->itemamount = amount; + + if (itemlistactive) + K_AddItemRollToList((INT32)(player - players), type, amount); } static void K_AwardPlayerResult(player_t *player, kartresult_t *result, kartitemblink_e blink) diff --git a/src/k_kart.c b/src/k_kart.c index d27926797..7c21a3001 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10972,6 +10972,17 @@ boolean K_ItemLitterActive(void) return false; } +boolean K_ItemListActive(void) +{ + if (itemlistactive) + { + // Item listing is enabled! + return true; + } + + return false; +} + boolean K_ItemPushingActive(void) { return itempushing; diff --git a/src/k_kart.h b/src/k_kart.h index 9017f4a8f..9291108ba 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -357,6 +357,7 @@ boolean K_DraftingActive(void); boolean K_AirDropActive(void); boolean K_AirThrustActive(void); boolean K_ItemLitterActive(void); +boolean K_ItemListActive(void); boolean K_ItemPushingActive(void); INT32 K_GetBumpSpark(void); boolean K_BoostChain(player_t *player, INT32 timer, boolean chainsound); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 2a26b415b..263fbafb8 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -34,6 +34,7 @@ #include "k_hud.h" #include "k_waypoint.h" #include "k_items.h" +#include "k_itemlist.hpp" #include "d_netcmd.h" // IsPlayerAdmin #include "m_menu.h" // Player Setup menu color stuff #include "p_spec.h" // P_StartQuake @@ -4364,6 +4365,12 @@ static int lib_kItemLitterActive(lua_State *L) return 1; } +static int lib_kItemListActive(lua_State *L) +{ + lua_pushboolean(L, K_ItemListActive()); + return 1; +} + // Gets the currently active bumpspark type. static int lib_kGetBumpSpark(lua_State *L) { @@ -5244,6 +5251,75 @@ static int lib_kSetPlayerItemCooldown(lua_State *L) return 0; } +static int lib_kAddItemRollToList(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + kartitemtype_e item_id = (kartitemtype_e)luaL_checkinteger(L, 2); + INT32 amt = luaL_checkinteger(L, 3); + //HUDSAFE + + if (!player) + return LUA_ErrInvalid(L, "player_t"); + + if (item_id < 1 || item_id >= numkartitems) + return luaL_error(L, "item number %d out of range (1 - %d)", item_id, numkartitems-1); + + INT32 pid = (INT32)(player - players); + + K_AddItemRollToList(pid, item_id, amt); + return 0; +} + +static int lib_kGetItemListSize(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + //HUDSAFE + + if (!player) + return LUA_ErrInvalid(L, "player_t"); + + INT32 pid = (INT32)(player - players); + + lua_pushinteger(L, K_GetItemListSize(pid)); + return 1; +} + +static int lib_kSetItemListEntry(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + kartitemtype_e item_id = (kartitemtype_e)luaL_checkinteger(L, 2); + INT32 num = luaL_checkinteger(L, 3); + //HUDSAFE + + if (!player) + return LUA_ErrInvalid(L, "player_t"); + + if (item_id < 1 || item_id >= numkartitems) + return luaL_error(L, "item number %d out of range (1 - %d)", item_id, numkartitems-1); + + INT32 pid = (INT32)(player - players); + + K_SetItemListEntry(pid, item_id, num); + return 0; +} + +static int lib_kGetItemListEntry(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + kartitemtype_e item_id = (kartitemtype_e)luaL_checkinteger(L, 2); + //HUDSAFE + + if (!player) + return LUA_ErrInvalid(L, "player_t"); + + if (item_id < 1 || item_id >= numkartitems) + return luaL_error(L, "item number %d out of range (1 - %d)", item_id, numkartitems-1); + + INT32 pid = (INT32)(player - players); + lua_pushinteger(L, K_GetItemListEntry(pid, item_id)); + return 1; +} + // G_INPUT //////////// @@ -5618,6 +5694,7 @@ static luaL_Reg lib[] = { {"K_DraftingActive",lib_kDraftingActive}, {"K_AirDropActive",lib_kAirDropActive}, {"K_ItemLitterActive",lib_kItemLitterActive}, + {"K_ItemListActive", lib_kItemListActive}, {"K_GetBumpSpark",lib_kGetBumpSpark}, {"K_UsingLegacyCheckpoints",lib_kUsingLegacyCheckpoints}, {"K_DoBoost",lib_kDoBoost}, @@ -5706,6 +5783,12 @@ static luaL_Reg lib[] = { // k_items {"K_SetPlayerItemCooldown", lib_kSetPlayerItemCooldown}, + // k_itemlist + {"K_AddItemRollToList", lib_kAddItemRollToList}, + {"K_GetItemListSize", lib_kGetItemListSize}, + {"K_SetItemListEntry", lib_kSetItemListEntry}, + {"K_GetItemListEntry", lib_kGetItemListEntry}, + //g_input {"G_SetPlayerGamepadIndicatorColor",lib_gSetPlayerGamepadIndicatorColor}, {"G_PlayerDeviceRumble",lib_gPlayerDeviceRumble}, diff --git a/src/p_mobj.h b/src/p_mobj.h index 6f83f8912..4501df66b 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -622,6 +622,7 @@ extern boolean airthrustactive; extern boolean itemlittering; extern boolean itempushing; extern UINT8 bumpsparkactive; +extern boolean itemlistactive; extern UINT16 bossdisabled; extern boolean stoppedclock; diff --git a/src/p_saveg.c b/src/p_saveg.c index e89325d48..ea58d3f74 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -45,6 +45,7 @@ #include "acs/interface.h" #include "g_party.h" #include "k_waypoint.h" +#include "k_itemlist.hpp" #include @@ -404,6 +405,9 @@ static void P_RelinkWaypoint(savebuffer_t *save, waypoint_t **ptr) #define ARCHIVEBLOCK_SPECIALS 0x7F228378 #define ARCHIVEBLOCK_WAYPOINTS 0x7F46498F +// Specialized netsynch markers +#define PLYRSYNC_ITEMLIST (1) + static inline void P_ArchivePlayer(savebuffer_t *save) { const player_t *player = &players[consoleplayer]; @@ -457,6 +461,12 @@ static void P_NetSyncPlayers(savebuffer_t *save) SYNC(playerconsole[i]); SYNC(splitscreen_invitations[i]); + // Item lists + if (P_SyncUINT32(save, (K_GetItemListSize(i) ? PLYRSYNC_ITEMLIST : 0))) + { + K_SyncItemList(save, i); + } + SYNC(players[i].angleturn); SYNC(players[i].aiming); SYNC(players[i].drawangle); @@ -4222,6 +4232,7 @@ static boolean P_NetSyncMisc(savebuffer_t *save, boolean resending) SYNCBOOLEAN(airdropactive); SYNCBOOLEAN(itemlittering); SYNCBOOLEAN(itempushing); + SYNCBOOLEAN(itemlistactive); SYNC(bumpsparkactive); SYNC(antibumptime); diff --git a/src/p_setup.c b/src/p_setup.c index 777008647..8d1aff65b 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -108,6 +108,7 @@ #include "k_mapuser.h" #include "p_deepcopy.h" #include "k_specialstage.h" +#include "k_itemlist.hpp" #include "g_input.h" #include "blan/b_soc.h" @@ -164,6 +165,7 @@ boolean airthrustactive; boolean itemlittering; boolean itempushing; UINT8 bumpsparkactive; +boolean itemlistactive; UINT16 bossdisabled; boolean stoppedclock; boolean levelloading; @@ -8177,6 +8179,7 @@ static void P_InitLevelSettings(boolean reloadinggamestate) airthrustactive = false; itemlittering = false; itempushing = false; + itemlistactive = false; bumpsparkactive = 0; antibumptime = 0; @@ -8213,10 +8216,15 @@ static void P_InitLevelSettings(boolean reloadinggamestate) if (cv_kartitempush.value) itempushing = true; + if (cv_itemlist.value) + itemlistactive = true; + bumpsparkactive = (UINT8)cv_kartbumpspark.value; antibumptime = (tic_t)cv_kartantibump.value * TICRATE; + K_ClearItemRollLists(); + // emerald hunt hunt1 = hunt2 = hunt3 = NULL; diff --git a/src/y_inter.c b/src/y_inter.c index 458650138..d196758c9 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -49,11 +49,18 @@ #include "k_grandprix.h" #include "k_bot.h" // cv_botcanvote #include "r_fps.h" // R_GetTimeFrac +#include "k_hud.h" +#include "k_itemlist.hpp" #ifdef HWRENDER #include "hardware/hw_main.h" #endif +#define ITEMLIST_PLAYER_YOFFSET 9 +#define ITEMLIST_SCROLLSPEED (3 * FRACUNIT / 4) +#define ITEMLIST_SCROLLDELAY (3 * TICRATE) +#define ITEMLIST_SCROLLREPEAT 1 + typedef struct { char patch[9]; @@ -80,9 +87,23 @@ typedef struct UINT8 pos[MAXPLAYERS]; // player positions. used for ties boolean rankingsmode; // rankings mode + boolean itemrolls; boolean encore; // encore mode } y_data; +static boolean Y_ItemListActive(void) +{ + UINT8 i = 0, nump = 0; + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + continue; + nump++; + } + + return ((itemlistactive) && (nump > 1)); +} + static y_data data; // graphics @@ -99,6 +120,12 @@ static UINT8 *y_screenbuffer; static INT32 intertic; static INT32 endtic = -1; static INT32 sorttic = -1; +static INT32 rolltic = -1; + +static fixed_t listscroll_length = 0; +static boolean listscroll_reverse = false; +static INT32 listscroll_delay = 0; +static fixed_t xscroll = 0; intertype_t intertype = int_none; intertype_t intermissiontypes[NUMGAMETYPES]; @@ -253,6 +280,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) data.levelstring[sizeof data.levelstring - 1] = '\0'; data.encore = encoremode; + data.itemrolls = Y_ItemListActive(); memset(data.jitter, 0, sizeof (data.jitter)); } @@ -382,10 +410,17 @@ void Y_CleanupScreenBuffer(void) void Y_IntermissionDrawer(void) { INT32 i, whiteplayer = MAXPLAYERS, x = 4, hilicol = V_YELLOWMAP; // fallback + INT32 xx = x, x_base = x; if (intertype == int_none || rendermode == render_none) return; + INT32 w = (vid.width / vid.dupx); + INT32 h = (vid.height / vid.dupy); + + const INT32 vidxdiff = (w - BASEVIDWIDTH) / 2; + const INT32 vidydiff = (h - BASEVIDHEIGHT) / 2; + if (!useinterpic && y_screenbuffer == NULL #ifdef HWRENDER // TODO resolution changes breaks the screentexture capture, I have no clue why @@ -435,7 +470,18 @@ void Y_IntermissionDrawer(void) hilicol = V_YELLOWMAP; } - if (sorttic != -1 && intertic > sorttic) + if (rolltic != -1 && intertic > (rolltic - 8) && (intertic < (rolltic + 8))) + { + INT32 count = (intertic - (rolltic - 8)); + + if (count < 8) + x -= ((((count<>FRACBITS) / (8 * vid.dupx); + else if (count == 8) + goto skiptallydrawer; + else if (count < 16) + x += (((((16 - count)<>FRACBITS) / (8 * vid.dupx); + } + else if (sorttic != -1 && intertic > sorttic) { INT32 count = (intertic - sorttic); @@ -452,10 +498,15 @@ void Y_IntermissionDrawer(void) #define NUMFORNEWCOLUMN 8 INT32 y = 41, gutter = ((data.numplayers > NUMFORNEWCOLUMN) ? 0 : (BASEVIDWIDTH/2)); INT32 dupadjust = (vid.width/vid.dupx), duptweak = (dupadjust - BASEVIDWIDTH)/2; + fixed_t newlist_xpush = (BASEVIDWIDTH/2) * FRACUNIT; const char *timeheader; - + boolean manyplayers16 = (data.numplayers > NUMFORNEWCOLUMN*2); boolean manyplayers8 = (data.numplayers > NUMFORNEWCOLUMN); + boolean displayitemrolls = (data.itemrolls && (intertic <= rolltic)); + + if (displayitemrolls) + manyplayers16 = manyplayers8 = false; int y2; @@ -510,13 +561,8 @@ void Y_IntermissionDrawer(void) V_DrawRightAlignedString(x+152, 24, hilicol, timeheader); } - else - { - V_DrawCenteredString(x+6, 24, hilicol, "#"); - V_DrawString(x+36, 24, hilicol, "NAME"); - - V_DrawRightAlignedString(x+(BASEVIDWIDTH/2)+152, 24, hilicol, timeheader); - } + + INT32 xscroll_px = (xscroll / FRACUNIT); for (i = 0; i < data.numplayers; i++) { @@ -530,36 +576,78 @@ void Y_IntermissionDrawer(void) if (dojitter) y--; - if (manyplayers16) - V_DrawPingNum(x+6, y+2, 0, data.pos[i], NULL); + if (displayitemrolls) + { + if (data.pos[i] < 0 || data.pos[i] > 16) + V_DrawPingNum(x+2-xscroll_px, y+1, 0, data.pos[i], NULL); + else + V_DrawScaledPatch(x-5-xscroll_px, y+1, 0, kp_facenum[data.pos[i]]); + } + else if (manyplayers16) + { + V_DrawPingNum(x + 6, y + 2, 0, data.pos[i], NULL); + } else V_DrawCenteredString(x+6, y, 0, va("%d", data.pos[i])); if (data.color[i]) { UINT8 *colormap = R_GetTranslationColormap(*data.character[i], *data.color[i], GTC_CACHE); - patch_t *facerank = faceprefix[*data.character[i]][FACE_RANK]; + patch_t *facerank; + + facerank = faceprefix[*data.character[i]][FACE_RANK]; + + fixed_t scale = FRACUNIT; if (manyplayers16) { - // fixed_t scale = K_UseHighResPortraits() ? FRACUNIT/4 : FRACUNIT/2; - fixed_t scale = FRACUNIT/2; + scale = FRACUNIT / 2; + V_DrawFixedPatch((x+8)<leftoffset, y-4+facerank->topoffset, 0, facerank, colormap); + INT32 xoffs, yoffs; + + scale = (displayitemrolls) ? FRACUNIT/2 : FRACUNIT; + + xoffs = FixedMul(16, scale); + yoffs = FixedMul(4, scale); + + if (displayitemrolls) + { + V_DrawFixedPatch( + ((x+11-xscroll_px)*FRACUNIT) + ((facerank->leftoffset) * scale), + ((y+1)*FRACUNIT) + ((facerank->topoffset) * scale), + scale, + 0, + facerank, + colormap + ); + + if ((x_base - x) == 0) + { + // Terrible hack to remedy offset woes: Set a "base" value for x to reference. + x_base = (x+11) + 20; + } + + xx = x + x_base - 4; + } + else + { + V_DrawFixedPatch((x+xoffs)<leftoffset, y-4+highlight->topoffset, 0, highlight); } - if ((players[data.num[i]].pflags & PF_NOCONTEST) && players[data.num[i]].bot) + if ((!displayitemrolls) && (players[data.num[i]].pflags & PF_NOCONTEST) && players[data.num[i]].bot) { // RETIRED!! patch_t *retire = W_CachePatchName("K_NOBLNS", PU_CACHE); @@ -578,10 +666,32 @@ void Y_IntermissionDrawer(void) y2 = y; + INT32 slen = 0; + INT32 slen_temp = slen; + if (manyplayers16) - V_DrawThinString(x+18, y, hilicol|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + V_DrawThinString(x+18, y, ((data.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + else if (displayitemrolls) + { + V_DrawThinString(x + 20 - xscroll_px, y, ((data.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + + // Get the longest string size. This is... gross. + INT32 ii; + + for (ii = 0; ii < data.numplayers; ii++) + { + if (data.num[ii] != MAXPLAYERS && playeringame[data.num[ii]] && !players[data.num[ii]].spectator) + { + slen_temp = V_ThinStringWidth(data.name[ii], ((data.num[ii] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE); + slen = ((slen_temp > slen) ? slen_temp : slen); + } + } + } else if (manyplayers8) - V_DrawThinString(x+36, y2-1, ((data.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + { + V_DrawThinString(xx, y2 - 1, ((data.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE, strtime); + slen = V_ThinStringWidth(strtime, ((data.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE|V_6WIDTHSPACE); + } else V_DrawString(x+36, y2, ((data.num[i] == whiteplayer) ? hilicol : 0)|V_ALLOWLOWERCASE, strtime); @@ -622,6 +732,11 @@ void Y_IntermissionDrawer(void) else V_DrawRightAlignedString(x+152+gutter, y, 0, strtime); } + else if (data.itemrolls && (intertic <= rolltic)) + { + const fixed_t itemlistlen = K_DrawItemList((INT32)(data.num[i]), ((xx+2+slen-xscroll_px) * FRACUNIT), ((y+1) * FRACUNIT)); + newlist_xpush = max(newlist_xpush, ((x_base+9+slen) * FRACUNIT) + itemlistlen); + } else { if (data.val[i] == (UINT32_MAX-1)) @@ -671,17 +786,20 @@ void Y_IntermissionDrawer(void) } else { - y += 18; + y += (displayitemrolls) ? ITEMLIST_PLAYER_YOFFSET : 18; - if (i == NUMFORNEWCOLUMN-1) + if ((i % 16) == (((displayitemrolls) ? 2 : 1) * NUMFORNEWCOLUMN) - 1) { y = 41; - x += BASEVIDWIDTH/2; + x += (newlist_xpush / FRACUNIT); + + newlist_xpush = (BASEVIDWIDTH / 2) * FRACUNIT; } } - #undef NUMFORNEWCOLUMN } + + listscroll_length = max(0, x - BASEVIDWIDTH) * FRACUNIT; } skiptallydrawer: @@ -762,6 +880,53 @@ void Y_Ticker(void) intertic++; + if (listscroll_length && (intertic > (TICRATE * 2)) && (intertic <= rolltic)) + { + + if (!listscroll_reverse) + { + if ((xscroll >= listscroll_length)) + { + if (!listscroll_delay) + listscroll_delay = ITEMLIST_SCROLLDELAY; + } + else + xscroll = min(listscroll_length, xscroll + ITEMLIST_SCROLLSPEED); + } + else + { + if ((xscroll <= 0)) + { + if (!listscroll_delay) + listscroll_delay = ITEMLIST_SCROLLDELAY; + } + else + xscroll = max(0, xscroll - ITEMLIST_SCROLLSPEED); + } + + if (listscroll_delay) + { + listscroll_delay--; + + if (!listscroll_delay) + listscroll_reverse = (!listscroll_reverse); + } + } + else + { + listscroll_length = 0; + listscroll_delay = 0; + listscroll_reverse = false; + + if (xscroll > 0) + { + if (intertic < rolltic) + xscroll = max(0, xscroll - ITEMLIST_SCROLLSPEED); + else + xscroll = 0; + } + } + // Team scramble code for team match and CTF. // Don't do this if we're going to automatically scramble teams next round. /*if (G_GametypeHasTeams() && cv_teamscramble.value && !cv_scrambleonchange.value && server) @@ -942,7 +1107,7 @@ void Y_StartIntermission(void) if (!timer) { // Prevent a weird bug - timer = 1; + timer = 1; } else if (nump < 2 && !netgame) { @@ -991,6 +1156,14 @@ void Y_StartIntermission(void) { // Calculate who won Y_CalculateMatchData(0, Y_CompareTime); + + if (data.itemrolls) + { + rolltic = (15 * TICRATE); + + sorttic += rolltic; + timer += rolltic; + } break; } @@ -2152,3 +2325,7 @@ void Y_SetupVoteFinish(SINT8 pick, SINT8 level) g_pickedVote = pick; timer = 0; } + +#undef ITEMLIST_PLAYER_YOFFSET +#undef ITEMLIST_SCROLLSPEED +#undef ITEMLIST_SCROLLDELAY