From 4056c5bde64e9f8a404b1da738812fdd699436a6 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 3 Jun 2019 14:28:51 -0700 Subject: [PATCH 01/65] Prepare a font system --- src/font.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/font.h | 49 +++++++++++++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 src/font.c create mode 100644 src/font.h diff --git a/src/font.c b/src/font.c new file mode 100644 index 000000000..7207def62 --- /dev/null +++ b/src/font.c @@ -0,0 +1,76 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1999-2018 by Sonic Team Junior. +// Copyright (C) 2019 by Kart Krew. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file font.c +/// \brief Font setup + +#include "doomdef.h" +#include "hu_stuff.h" +#include "font.h" + +font_t fontv[MAX_FONTS]; +int fontc; + +static void +FontCache (font_t *fnt) +{ + int i; + int c; + + c = fnt->start; + for (i = 0; i < fnt->size; ++i, ++c) + { + fnt->font[i] = HU_CachePatch( + "%s%.*d", + fnt->digits, + fnt->prefix, + c); + } +} + +void +Font_Load (void) +{ + int i; + for (i = 0; i < fontc; ++i) + { + FontCache(&fontv[i]); + } +} + +int +Font_DumbRegister (const font_t *sfnt) +{ + font_t *fnt; + + if (fontc == MAX_FONTS) + return -1; + + fnt = &fontv[fontc]; + + if (!( fnt->font = ZZ_Alloc(sfnt->size * sizeof (patch_t *)) )) + return -1; + + memcpy(fnt, sfnt, sizeof (font_t)); + + return fontc++; +} + +int +Font_Register (const font_t *sfnt) +{ + int d; + + d = Font_DumbRegister(sfnt); + + if (d >= 0) + FontCache(&fontv[d]); + + return d; +} diff --git a/src/font.h b/src/font.h new file mode 100644 index 000000000..c2ef5cf34 --- /dev/null +++ b/src/font.h @@ -0,0 +1,49 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1999-2018 by Sonic Team Junior. +// Copyright (C) 2019 by Kart Krew. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file font.h +/// \brief Font setup + +#ifndef __FONT_H_ +#define __FONT_H__ + +#define MAX_FONTS 32 + +typedef struct font font_t; + +struct font +{ + patch_t **font; + + UINT8 start; + UINT8 size; + + char prefix[8];/* 7 used at most */ + unsigned digits : 2; +}; + +extern font_t fontv[MAX_FONTS]; +extern int fontc; + +/* +Reloads already registered fonts. +*/ +void Font_Load (void); + +/* +Registers and loads a new font. +*/ +int Font_Register (const font_t *); + +/* +Register a new font, but do not load it yet. +*/ +int Font_DumbRegister (const font_t *); + +#endif From 0b0b7c480bbcd9845b829157ffb99318ec11a0f3 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 3 Jun 2019 14:38:45 -0700 Subject: [PATCH 02/65] Set up with the font system --- src/hu_stuff.c | 213 ++++++++++++++++++++++--------------------------- src/hu_stuff.h | 27 +++++-- 2 files changed, 117 insertions(+), 123 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index f343f12b7..36ecb1101 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -14,6 +14,7 @@ #include "doomdef.h" #include "byteptr.h" #include "hu_stuff.h" +#include "font.h" #include "m_menu.h" // gametype_cons_t #include "m_cond.h" // emblems @@ -64,19 +65,9 @@ //------------------------------------------- // heads up font //------------------------------------------- -patch_t *hu_font[HU_FONTSIZE]; -patch_t *kart_font[KART_FONTSIZE]; // SRB2kart -patch_t *tny_font[HU_FONTSIZE]; -patch_t *tallnum[10]; // 0-9 -patch_t *nightsnum[10]; // 0-9 - -// Level title and credits fonts -patch_t *lt_font[LT_FONTSIZE]; -patch_t *cred_font[CRED_FONTSIZE]; // ping font // Note: I'd like to adress that at this point we might *REALLY* want to work towards a common drawString function that can take any font we want because this is really turning into a MESS. :V -Lat' -patch_t *pingnum[10]; patch_t *pinggfx[5]; // small ping graphic patch_t *framecounter; @@ -187,135 +178,52 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum); void HU_LoadGraphics(void) { - char buffer[9]; - INT32 i, j; + INT32 i; if (dedicated) return; - j = HU_FONTSTART; - for (i = 0; i < HU_FONTSIZE; i++, j++) - { - // cache the heads-up font for entire game execution - sprintf(buffer, "STCFN%.3d", j); - if (W_CheckNumForName(buffer) == LUMPERROR) - hu_font[i] = NULL; - else - hu_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); - - // tiny version of the heads-up font - sprintf(buffer, "TNYFN%.3d", j); - if (W_CheckNumForName(buffer) == LUMPERROR) - tny_font[i] = NULL; - else - tny_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); - } - - // cache the level title font for entire game execution - lt_font[0] = (patch_t *)W_CachePatchName("LTFNT039", PU_HUDGFX); /// \note fake start hack - - // Number support - lt_font[9] = (patch_t *)W_CachePatchName("LTFNT048", PU_HUDGFX); - lt_font[10] = (patch_t *)W_CachePatchName("LTFNT049", PU_HUDGFX); - lt_font[11] = (patch_t *)W_CachePatchName("LTFNT050", PU_HUDGFX); - lt_font[12] = (patch_t *)W_CachePatchName("LTFNT051", PU_HUDGFX); - lt_font[13] = (patch_t *)W_CachePatchName("LTFNT052", PU_HUDGFX); - lt_font[14] = (patch_t *)W_CachePatchName("LTFNT053", PU_HUDGFX); - lt_font[15] = (patch_t *)W_CachePatchName("LTFNT054", PU_HUDGFX); - lt_font[16] = (patch_t *)W_CachePatchName("LTFNT055", PU_HUDGFX); - lt_font[17] = (patch_t *)W_CachePatchName("LTFNT056", PU_HUDGFX); - lt_font[18] = (patch_t *)W_CachePatchName("LTFNT057", PU_HUDGFX); - - // SRB2kart - j = KART_FONTSTART; - for (i = 0; i < KART_FONTSIZE; i++, j++) - { - // cache the heads-up font for entire game execution - sprintf(buffer, "MKFNT%.3d", j); - if (W_CheckNumForName(buffer) == LUMPERROR) - kart_font[i] = NULL; - else - kart_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); - } - // - - j = LT_FONTSTART; - for (i = 0; i < LT_FONTSIZE; i++) - { - sprintf(buffer, "LTFNT%.3d", j); - j++; - - if (W_CheckNumForName(buffer) == LUMPERROR) - lt_font[i] = NULL; - else - lt_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); - } - - // cache the credits font for entire game execution (why not?) - j = CRED_FONTSTART; - for (i = 0; i < CRED_FONTSIZE; i++) - { - sprintf(buffer, "CRFNT%.3d", j); - j++; - - if (W_CheckNumForName(buffer) == LUMPERROR) - cred_font[i] = NULL; - else - cred_font[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); - } - - //cache numbers too! - for (i = 0; i < 10; i++) - { - sprintf(buffer, "STTNUM%d", i); - tallnum[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); - sprintf(buffer, "NGTNUM%d", i); - nightsnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - sprintf(buffer, "PINGN%d", i); - pingnum[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); - } + Font_Load(); // minus for negative tallnums - tallminus = (patch_t *)W_CachePatchName("STTMINUS", PU_HUDGFX); + tallminus = HU_CachePatch("STTMINUS"); // cache the crosshairs, don't bother to know which one is being used, // just cache all 3, they're so small anyway. for (i = 0; i < HU_CROSSHAIRS; i++) { - sprintf(buffer, "CROSHAI%c", '1'+i); - crosshair[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); + crosshair[i] = HU_CachePatch("CROSHAI%c", '1'+i); } - emblemicon = W_CachePatchName("EMBLICON", PU_HUDGFX); - tokenicon = W_CachePatchName("TOKNICON", PU_HUDGFX); + emblemicon = HU_CachePatch("EMBLICON"); + tokenicon = HU_CachePatch("TOKNICON"); - emeraldpics[0] = W_CachePatchName("CHAOS1", PU_HUDGFX); - emeraldpics[1] = W_CachePatchName("CHAOS2", PU_HUDGFX); - emeraldpics[2] = W_CachePatchName("CHAOS3", PU_HUDGFX); - emeraldpics[3] = W_CachePatchName("CHAOS4", PU_HUDGFX); - emeraldpics[4] = W_CachePatchName("CHAOS5", PU_HUDGFX); - emeraldpics[5] = W_CachePatchName("CHAOS6", PU_HUDGFX); - emeraldpics[6] = W_CachePatchName("CHAOS7", PU_HUDGFX); - tinyemeraldpics[0] = W_CachePatchName("TEMER1", PU_HUDGFX); - tinyemeraldpics[1] = W_CachePatchName("TEMER2", PU_HUDGFX); - tinyemeraldpics[2] = W_CachePatchName("TEMER3", PU_HUDGFX); - tinyemeraldpics[3] = W_CachePatchName("TEMER4", PU_HUDGFX); - tinyemeraldpics[4] = W_CachePatchName("TEMER5", PU_HUDGFX); - tinyemeraldpics[5] = W_CachePatchName("TEMER6", PU_HUDGFX); - tinyemeraldpics[6] = W_CachePatchName("TEMER7", PU_HUDGFX); + emeraldpics[0] = HU_CachePatch("CHAOS1"); + emeraldpics[1] = HU_CachePatch("CHAOS2"); + emeraldpics[2] = HU_CachePatch("CHAOS3"); + emeraldpics[3] = HU_CachePatch("CHAOS4"); + emeraldpics[4] = HU_CachePatch("CHAOS5"); + emeraldpics[5] = HU_CachePatch("CHAOS6"); + emeraldpics[6] = HU_CachePatch("CHAOS7"); + tinyemeraldpics[0] = HU_CachePatch("TEMER1"); + tinyemeraldpics[1] = HU_CachePatch("TEMER2"); + tinyemeraldpics[2] = HU_CachePatch("TEMER3"); + tinyemeraldpics[3] = HU_CachePatch("TEMER4"); + tinyemeraldpics[4] = HU_CachePatch("TEMER5"); + tinyemeraldpics[5] = HU_CachePatch("TEMER6"); + tinyemeraldpics[6] = HU_CachePatch("TEMER7"); - songcreditbg = W_CachePatchName("K_SONGCR", PU_HUDGFX); + songcreditbg = HU_CachePatch("K_SONGCR"); // cache ping gfx: for (i = 0; i < 5; i++) { - sprintf(buffer, "PINGGFX%d", i+1); - pinggfx[i] = (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); + pinggfx[i] = HU_CachePatch("PINGGFX%d", i+1); } // fps stuff - framecounter = W_CachePatchName("FRAMER", PU_HUDGFX); - frameslash = W_CachePatchName("FRAMESL", PU_HUDGFX);; + framecounter = HU_CachePatch("FRAMER"); + frameslash = HU_CachePatch("FRAMESL");; } // Initialise Heads up @@ -323,6 +231,8 @@ void HU_LoadGraphics(void) // void HU_Init(void) { + font_t font; + #ifndef NONET COM_AddCommand("say", Command_Say_f); COM_AddCommand("sayto", Command_Sayto_f); @@ -334,9 +244,78 @@ void HU_Init(void) // set shift translation table shiftxform = english_shiftxform; + /* + Setup fonts + */ + + if (!dedicated) + { +#define DIM( start, size ) ( font.start = start, font.size = size ) +#define ADIM( name ) DIM (name ## _FONTSTART, name ## _FONTSIZE) +#define PR( s ) strcpy(font.prefix, s) +#define DIG( n ) ( font.digits = n ) +#define REG Font_DumbRegister(&font) + + DIG (3); + + ADIM (HU); + + PR ("STCFN"); + REG; + + PR ("TNYFN"); + REG; + + ADIM (KART); + PR ("MKFNT"); + REG; + + ADIM (LT); + PR ("LTFNT"); + REG; + + ADIM (CRED); + PR ("CRFNT"); + REG; + + DIG (1); + + DIM (0, 10); + + PR ("STTNUM"); + REG; + + PR ("NGTNUM"); + REG; + + PR ("PINGN"); + REG; + +#undef REG +#undef DIG +#undef PR +#undef ADMIN +#undef DIM + } + HU_LoadGraphics(); } +patch_t *HU_CachePatch(const char *format, ...) +{ + va_list ap; + char buffer[9]; + + va_start (ap, format); + vsprintf(buffer, format, ap); + va_end (ap); + + if (W_CheckNumForName(buffer) == LUMPERROR) + return NULL; + else + return (patch_t *)W_CachePatchName(buffer, PU_HUDGFX); +} + static inline void HU_Stop(void) { headsupactive = false; diff --git a/src/hu_stuff.h b/src/hu_stuff.h index be6798a82..3b412e76a 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -17,6 +17,7 @@ #include "d_event.h" #include "w_wad.h" #include "r_defs.h" +#include "font.h" //------------------------------------ // heads up font @@ -42,6 +43,23 @@ #define CRED_FONTEND 'Z' // the last font character #define CRED_FONTSIZE (CRED_FONTEND - CRED_FONTSTART + 1) +#define X( name ) name ## _FONT +/* fonts */ +enum +{ + X (HU), + X (TINY), + X (KART), + + X (LT), + X (CRED), + + X (TALLNUM), + X (NIGHTSNUM), + X (PINGNUM), +}; +#undef X + #define HU_CROSSHAIRS 3 // maximum of 9 - see HU_Init(); extern char *shiftxform; // english translation shift table @@ -78,15 +96,9 @@ void HU_AddChatText(const char *text, boolean playsound); // set true when entering a chat message extern boolean chat_on; -extern patch_t *hu_font[HU_FONTSIZE], *kart_font[KART_FONTSIZE], *tny_font[HU_FONTSIZE]; // SRB2kart -extern patch_t *tallnum[10]; -extern patch_t *pingnum[10]; extern patch_t *pinggfx[5]; -extern patch_t *nightsnum[10]; extern patch_t *framecounter; extern patch_t *frameslash; -extern patch_t *lt_font[LT_FONTSIZE]; -extern patch_t *cred_font[CRED_FONTSIZE]; extern patch_t *emeraldpics[7]; extern patch_t *tinyemeraldpics[7]; extern patch_t *rflagico; @@ -104,6 +116,9 @@ void HU_Init(void); void HU_LoadGraphics(void); +// Load a HUDGFX patch or NULL. +patch_t *HU_CachePatch(const char *format, ...); + // reset heads up when consoleplayer respawns. void HU_Start(void); From 8ee25a7c8ab2f632d24583952bc0a6ba3c767c57 Mon Sep 17 00:00:00 2001 From: James R Date: Mon, 3 Jun 2019 14:57:58 -0700 Subject: [PATCH 03/65] Begin using the font system --- src/d_clisrv.c | 2 +- src/g_game.c | 2 +- src/hu_stuff.c | 4 ++-- src/k_kart.c | 2 +- src/st_stuff.h | 1 - 5 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 1dc2c1420..0149b8d30 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2212,7 +2212,7 @@ boolean CL_Responder(event_t *ev) if (cl_mode != CL_CHALLENGE) return false; - if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && hu_font[ch-HU_FONTSTART]) + if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && fontv[HU_FONT].font[ch-HU_FONTSTART]) || ch == ' ') // Allow spaces, of course { len = strlen(cl_challengepassword); diff --git a/src/g_game.c b/src/g_game.c index 10bd76acc..6f602661b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -8273,7 +8273,7 @@ boolean G_DemoTitleResponder(event_t *ev) return true; } - if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && hu_font[ch-HU_FONTSTART]) + if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && fontv[HU_FONT].font[ch-HU_FONTSTART]) || ch == ' ') // Allow spaces, of course { len = strlen(demo.titlename); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 36ecb1101..ad3f1967d 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -975,7 +975,7 @@ static inline boolean HU_keyInChatString(char *s, char ch) { size_t l; - if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && hu_font[ch-HU_FONTSTART]) + if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && fontv[HU_FONT].font[ch-HU_FONTSTART]) || ch == ' ') // Allow spaces, of course { l = strlen(s); @@ -1409,7 +1409,7 @@ static char *CHAT_WordWrap(INT32 x, INT32 w, INT32 option, const char *string) c = toupper(c); c -= HU_FONTSTART; - if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) + if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c]) { chw = spacewidth; lastusablespace = i; diff --git a/src/k_kart.c b/src/k_kart.c index 10d772ba0..d68a44e1c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8495,7 +8495,7 @@ static void K_drawInput(void) V_DrawFill(x+(xoffs), y+BUTTH, BUTTW-1, 2, splitflags|31);\ }\ V_DrawFill(x+(xoffs), y+offs, BUTTW-1, BUTTH, col);\ - V_DrawFixedPatch((x+1+(xoffs))< Date: Sun, 23 Jun 2019 16:33:53 -0700 Subject: [PATCH 04/65] Fix minor errors --- src/font.h | 2 +- src/hu_stuff.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/font.h b/src/font.h index c2ef5cf34..88bedb44f 100644 --- a/src/font.h +++ b/src/font.h @@ -10,7 +10,7 @@ /// \file font.h /// \brief Font setup -#ifndef __FONT_H_ +#ifndef __FONT_H__ #define __FONT_H__ #define MAX_FONTS 32 diff --git a/src/hu_stuff.c b/src/hu_stuff.c index ad3f1967d..6b83a5896 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -250,7 +250,7 @@ void HU_Init(void) if (!dedicated) { -#define DIM( start, size ) ( font.start = start, font.size = size ) +#define DIM( s, n ) ( font.start = s, font.size = n ) #define ADIM( name ) DIM (name ## _FONTSTART, name ## _FONTSIZE) #define PR( s ) strcpy(font.prefix, s) #define DIG( n ) ( font.digits = n ) From 8063cc6c2f2a0907d2ae877abc091cbf58bb065e Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 26 Jun 2019 00:31:20 -0700 Subject: [PATCH 05/65] Order correctly and include z_zone.h --- src/font.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/font.c b/src/font.c index 7207def62..aeaabd018 100644 --- a/src/font.c +++ b/src/font.c @@ -13,6 +13,7 @@ #include "doomdef.h" #include "hu_stuff.h" #include "font.h" +#include "z_zone.h" font_t fontv[MAX_FONTS]; int fontc; @@ -28,8 +29,8 @@ FontCache (font_t *fnt) { fnt->font[i] = HU_CachePatch( "%s%.*d", - fnt->digits, fnt->prefix, + fnt->digits, c); } } @@ -54,11 +55,11 @@ Font_DumbRegister (const font_t *sfnt) fnt = &fontv[fontc]; + memcpy(fnt, sfnt, sizeof (font_t)); + if (!( fnt->font = ZZ_Alloc(sfnt->size * sizeof (patch_t *)) )) return -1; - memcpy(fnt, sfnt, sizeof (font_t)); - return fontc++; } From 9579f6ea9e2dbd8c25a6f6a8d6ad7e90e3ac0834 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 26 Jun 2019 16:33:59 -0700 Subject: [PATCH 06/65] Add font system to Makefile and Visual Studio project files --- src/CMakeLists.txt | 2 ++ src/Makefile | 3 ++- src/sdl/Srb2SDL-vc10.vcxproj | 4 +++- src/sdl/Srb2SDL-vc9.vcproj | 44 ++++++++++++++++++++++++++++++++++ src/sdl12/Srb2SDL-vc10.vcxproj | 11 +++++++++ src/sdl12/Srb2SDL-vc9.vcproj | 44 ++++++++++++++++++++++++++++++++++ src/win32/Srb2win-vc10.vcxproj | 4 +++- src/win32/Srb2win-vc9.vcproj | 44 ++++++++++++++++++++++++++++++++++ 8 files changed, 153 insertions(+), 3 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2f97c173c..d8bb3956f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ set(SRB2_CORE_SOURCES filesrch.c g_game.c g_input.c + font.c hu_stuff.c i_tcp.c info.c @@ -74,6 +75,7 @@ set(SRB2_CORE_HEADERS g_game.h g_input.h g_state.h + font.h hu_stuff.h i_joy.h i_net.h diff --git a/src/Makefile b/src/Makefile index 214c2bf7a..adac06b2b 100644 --- a/src/Makefile +++ b/src/Makefile @@ -487,6 +487,7 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/am_map.o \ $(OBJDIR)/command.o \ $(OBJDIR)/console.o \ + $(OBJDIR)/font.o \ $(OBJDIR)/hu_stuff.o \ $(OBJDIR)/y_inter.o \ $(OBJDIR)/st_stuff.o \ @@ -788,7 +789,7 @@ $(OBJDIR)/v_video.o: v_video.c doomdef.h doomtype.h g_state.h m_swap.h r_local.h tables.h m_fixed.h screen.h command.h m_bbox.h r_main.h d_player.h \ p_pspr.h info.h d_think.h sounds.h p_mobj.h doomdata.h d_ticcmd.h \ r_data.h r_defs.h r_state.h r_bsp.h r_segs.h r_plane.h r_sky.h \ - r_things.h r_draw.h v_video.h hu_stuff.h d_event.h w_wad.h console.h \ + r_things.h r_draw.h v_video.h font.h hu_stuff.h d_event.h w_wad.h console.h \ i_video.h z_zone.h doomstat.h d_clisrv.h d_netcmd.h $(CC) $(CFLAGS) -fno-omit-frame-pointer $(WFLAGS) -c $< -o $@ endif diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index 45b1faab7..b5e897e62 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -231,6 +231,7 @@ + @@ -376,6 +377,7 @@ + @@ -488,4 +490,4 @@ - \ No newline at end of file + diff --git a/src/sdl/Srb2SDL-vc9.vcproj b/src/sdl/Srb2SDL-vc9.vcproj index 3898aeba4..6709ccd5e 100644 --- a/src/sdl/Srb2SDL-vc9.vcproj +++ b/src/sdl/Srb2SDL-vc9.vcproj @@ -2090,6 +2090,50 @@ RelativePath="..\console.h" > + + + + + + + + + + + + + + + + diff --git a/src/sdl12/Srb2SDL-vc10.vcxproj b/src/sdl12/Srb2SDL-vc10.vcxproj index 0ac7e9e55..5514f3eb4 100644 --- a/src/sdl12/Srb2SDL-vc10.vcxproj +++ b/src/sdl12/Srb2SDL-vc10.vcxproj @@ -655,6 +655,16 @@ %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) + + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) + %(PreprocessorDefinitions) + %(AdditionalIncludeDirectories) %(PreprocessorDefinitions) @@ -1366,6 +1376,7 @@ + diff --git a/src/sdl12/Srb2SDL-vc9.vcproj b/src/sdl12/Srb2SDL-vc9.vcproj index fa386e381..bf6cfb36d 100644 --- a/src/sdl12/Srb2SDL-vc9.vcproj +++ b/src/sdl12/Srb2SDL-vc9.vcproj @@ -2090,6 +2090,50 @@ RelativePath="..\console.h" > + + + + + + + + + + + + + + + + diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj index ced3d128e..f494aed1e 100644 --- a/src/win32/Srb2win-vc10.vcxproj +++ b/src/win32/Srb2win-vc10.vcxproj @@ -235,6 +235,7 @@ + @@ -402,6 +403,7 @@ + @@ -502,4 +504,4 @@ - \ No newline at end of file + diff --git a/src/win32/Srb2win-vc9.vcproj b/src/win32/Srb2win-vc9.vcproj index a64b8638c..7fa9f52ad 100644 --- a/src/win32/Srb2win-vc9.vcproj +++ b/src/win32/Srb2win-vc9.vcproj @@ -1831,6 +1831,50 @@ RelativePath="..\console.h" > + + + + + + + + + + + + + + + + From 5e1d110ed55a3e550e216c58e26d1d643cbd59a5 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 26 Jun 2019 16:45:26 -0700 Subject: [PATCH 07/65] Add V_DrawStringScaled --- src/v_video.c | 287 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/v_video.h | 11 ++ 2 files changed, 298 insertions(+) diff --git a/src/v_video.c b/src/v_video.c index 9233eda42..76dc95d4a 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1499,6 +1499,293 @@ char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string) return newstring; } +static inline fixed_t FixedCharacterDim( + fixed_t scale, + fixed_t chw, + INT32 hchw, + INT32 dupx, + fixed_t * cwp) +{ + (void)hchw; + (void)dupx; + (*cwp) = chw; + return 0; +} + +static inline fixed_t VariableCharacterDim( + fixed_t scale, + fixed_t chw, + INT32 hchw, + INT32 dupx, + fixed_t * cwp) +{ + (void)chw; + (void)hchw; + (void)dupx; + (*cwp) = FixedMul ((*cwp) << FRACBITS, scale); + return 0; +} + +static inline fixed_t CenteredCharacterDim( + fixed_t scale, + fixed_t chw, + INT32 hchw, + INT32 dupx, + fixed_t * cwp) +{ + INT32 cxoff; + /* + For example, center a 4 wide patch to 8 width: + 4/2 = 2 + 8/2 = 4 + 4 - 2 = 2 (our offset) + 2 + 4 = 6 = 8 - 2 (equal space on either side) + */ + cxoff = hchw -((*cwp) >> 1 ); + (*cwp) = chw; + return FixedMul (( cxoff * dupx )<< FRACBITS, scale); +} + +static inline fixed_t BunchedCharacterDim( + fixed_t scale, + fixed_t chw, + INT32 hchw, + INT32 dupx, + fixed_t * cwp) +{ + (void)chw; + (void)hchw; + (void)dupx; + (*cwp) = FixedMul (max (1, (*cwp) - 1) << FRACBITS, scale); + return 0; +} + +void V_DrawStringScaled( + fixed_t x, + fixed_t y, + fixed_t scale, + fixed_t spacescale, + fixed_t lfscale, + INT32 flags, + int fontno, + const char *s) +{ + fixed_t chw; + INT32 hchw;/* half-width for centering */ + fixed_t spacew; + fixed_t lfh; + + INT32 dupx; + + fixed_t right; + fixed_t bot; + + fixed_t (*dim_fn)(fixed_t,fixed_t,INT32,INT32,fixed_t *); + + font_t *font; + + boolean uppercase; + boolean notcolored; + + const UINT8 *colormap; + + fixed_t cx, cy; + + fixed_t cxoff; + fixed_t cw; + + INT32 spacing; + fixed_t left; + + int c; + + uppercase = !( flags & V_ALLOWLOWERCASE ); + flags &= ~(V_FLIP);/* These two (V_ALLOWLOWERCASE) share a bit. */ + + colormap = V_GetStringColormap(( flags & V_CHARCOLORMASK )); + notcolored = !colormap; + + font = &fontv[fontno]; + + chw = 0; + + spacing = ( flags & V_SPACINGMASK ); + + /* + Hardcoded until a better system can be implemented + for determining how fonts space. + */ + switch (fontno) + { + default: + case HU_FONT: + spacew = 4; + switch (spacing) + { + case V_MONOSPACE: + spacew = 8; + /* FALLTHRU */ + case V_OLDSPACING: + chw = 8; + break; + case V_6WIDTHSPACE: + spacew = 6; + } + break; + case TINY_FONT: + spacew = 2; + switch (spacing) + { + case V_MONOSPACE: + spacew = 5; + /* FALLTHRU */ + case V_OLDSPACING: + chw = 5; + break; + // Out of video flags, so we're reusing this for alternate charwidth instead + /*case V_6WIDTHSPACE: + spacewidth = 3;*/ + } + break; + case KART_FONT: + spacew = 12; + switch (spacing) + { + case V_MONOSPACE: + spacew = 12; + /* FALLTHRU */ + case V_OLDSPACING: + chw = 12; + break; + case V_6WIDTHSPACE: + spacew = 6; + } + break; + } + switch (fontno) + { + default: + case HU_FONT: + case TINY_FONT: + case KART_FONT: + if (( flags & V_RETURN8 )) + lfh = 8; + else + lfh = 12; + break; + case LT_FONT: + case CRED_FONT: + lfh = 12; + break; + } + + hchw = chw >> 1; + + chw <<= FRACBITS; + spacew <<= FRACBITS; + +#define Mul( id, scale ) ( id = FixedMul (scale, id) ) + Mul (chw, scale); + Mul (spacew, scale); + Mul (lfh, scale); + + Mul (spacew, spacescale); + Mul (lfh, lfscale); +#undef Mul + + if (( flags & V_NOSCALESTART )) + { + dupx = vid.dupx; + + hchw *= dupx; + + chw *= dupx; + spacew *= dupx; + lfh *= vid.dupy; + + right = vid.width; + } + else + { + dupx = 1; + + right = ( vid.width / vid.dupx ); + if (!( flags & V_SNAPTOLEFT )) + { + left = ( right - BASEVIDWIDTH )/ 2;/* left edge of drawable area */ + x = ( left << FRACBITS )+ x; + right -= left; + } + } + + right <<= FRACBITS; + bot = vid.height << FRACBITS; + + if (fontno == TINY_FONT) + { + if (chw) + dim_fn = FixedCharacterDim; + else + { + /* Reuse this flag for the alternate bunched-up spacing. */ + if (( flags & V_6WIDTHSPACE )) + dim_fn = BunchedCharacterDim; + else + dim_fn = VariableCharacterDim; + } + } + else + { + if (chw) + dim_fn = CenteredCharacterDim; + else + dim_fn = VariableCharacterDim; + } + + cx = x; + cy = y; + + for (; ( c = *s ); ++s) + { + switch (c) + { + case '\n': + cy += lfh; + if (cy >= bot) + return; + cx = x; + break; + default: + if (( c & 0x80 )) + { + if (notcolored) + { + colormap = V_GetStringColormap( + ( ( c & 0x7f )<< V_CHARCOLORSHIFT )& + V_CHARCOLORMASK); + } + } + else if (cx < right) + { + if (uppercase) + c = toupper(c); + + c -= font->start; + if (c >= 0 && c < font->size && font->font[c]) + { + cw = SHORT (font->font[c]->width) * dupx; + cxoff = (*dim_fn)(scale, chw, hchw, dupx, &cw); + V_DrawFixedPatch(cx + cxoff, cy, scale, + flags, font->font[c], colormap); + cx += cw; + } + else + cx += spacew; + } + } + } +} + // // Write a string using the hu_font // NOTE: the text is centered for screens larger than the base width diff --git a/src/v_video.h b/src/v_video.h index c8485c179..b01f65d06 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -173,6 +173,17 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string); // wordwrap a string using the hu_font char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string); +// draw a string using a font +void V_DrawStringScaled( + fixed_t x, + fixed_t y, + fixed_t scale, + fixed_t space_scale, + fixed_t linefeed_scale, + INT32 flags, + int font, + const char *text); + // draw a string using the hu_font void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string); void V_DrawKartString(INT32 x, INT32 y, INT32 option, const char *string); // SRB2kart From 295631b953ae4d17cd609c5fe9348e50247a8ae8 Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 26 Jun 2019 17:02:21 -0700 Subject: [PATCH 08/65] Support Level Title and Credit font in V_DrawStringScaled --- src/v_video.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/v_video.c b/src/v_video.c index 76dc95d4a..0e334dc8b 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1661,6 +1661,12 @@ void V_DrawStringScaled( spacew = 6; } break; + case LT_FONT: + spacew = 12; + break; + case CRED_FONT: + spacew = 16; + break; } switch (fontno) { From d8a2d6c7e2ce82375bb7d9184fe245203149fefa Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 26 Jun 2019 17:09:13 -0700 Subject: [PATCH 09/65] Remove old string drawing functions --- src/v_video.c | 615 -------------------------------------------------- src/v_video.h | 27 ++- 2 files changed, 20 insertions(+), 622 deletions(-) diff --git a/src/v_video.c b/src/v_video.c index 0e334dc8b..5440cbcdb 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1791,217 +1791,6 @@ void V_DrawStringScaled( } } } - -// -// Write a string using the hu_font -// NOTE: the text is centered for screens larger than the base width -// -void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) -{ - INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0; - const char *ch = string; - INT32 charflags = 0; - const UINT8 *colormap = NULL; - INT32 spacewidth = 4, charwidth = 0; - - INT32 lowercase = (option & V_ALLOWLOWERCASE); - option &= ~V_FLIP; // which is also shared with V_ALLOWLOWERCASE... - - if (option & V_NOSCALESTART) - { - dupx = vid.dupx; - dupy = vid.dupy; - scrwidth = vid.width; - } - else - { - dupx = dupy = 1; - scrwidth = vid.width/vid.dupx; - if (!(option & V_SNAPTOLEFT)) - { - left = (scrwidth - BASEVIDWIDTH)/2; - scrwidth -= left; - } - } - - charflags = (option & V_CHARCOLORMASK); - colormap = V_GetStringColormap(charflags); - - switch (option & V_SPACINGMASK) - { - case V_MONOSPACE: - spacewidth = 8; - /* FALLTHRU */ - case V_OLDSPACING: - charwidth = 8; - break; - case V_6WIDTHSPACE: - spacewidth = 6; - default: - break; - } - - for (;;ch++) - { - if (!*ch) - break; - if (*ch & 0x80) //color parsing -x 2.16.09 - { - // manually set flags override color codes - if (!(option & V_CHARCOLORMASK)) - { - charflags = ((*ch & 0x7f) << V_CHARCOLORSHIFT) & V_CHARCOLORMASK; - colormap = V_GetStringColormap(charflags); - } - continue; - } - if (*ch == '\n') - { - cx = x; - - if (option & V_RETURN8) - cy += 8*dupy; - else - cy += 12*dupy; - - continue; - } - - c = *ch; - if (!lowercase) - c = toupper(c); - c -= HU_FONTSTART; - - // character does not exist or is a space - if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) - { - cx += spacewidth * dupx; - continue; - } - - if (charwidth) - { - w = charwidth * dupx; - center = w/2 - SHORT(hu_font[c]->width)*dupx/2; - } - else - w = SHORT(hu_font[c]->width) * dupx; - - if (cx > scrwidth) - break; - if (cx+left + w < 0) //left boundary check - { - cx += w; - continue; - } - - V_DrawFixedPatch((cx + center)<= KART_FONTSIZE || !kart_font[c]) - { - cx += spacewidth * dupx; - continue; - } - - if (charwidth) - { - w = charwidth * dupx; - center = w/2 - SHORT(kart_font[c]->width)*dupx/2; - } - else - w = SHORT(kart_font[c]->width) * dupx; - - if (cx > scrwidth) - break; - if (cx+left + w < 0) //left boundary check - { - cx += w; - continue; - } - - V_DrawFixedPatch((cx + center)<= HU_FONTSIZE || !hu_font[c]) - { - cx += spacewidth * dupx; - continue; - } - - if (charwidth) - { - w = charwidth * dupx; - center = w/2 - SHORT(hu_font[c]->width)*dupx/4; - } - else - w = SHORT(hu_font[c]->width) * dupx / 2; - if (cx > scrwidth) - break; - if (cx+left + w < 0) //left boundary check - { - cx += w; - continue; - } - - V_DrawFixedPatch((cx + center)<= HU_FONTSIZE || !tny_font[c]) - { - cx += spacewidth * dupx; - continue; - } - - if (charwidth) - w = charwidth * dupx; - else - w = ((option & V_6WIDTHSPACE ? max(1, SHORT(tny_font[c]->width)-1) // Reuse this flag for the alternate bunched-up spacing - : SHORT(tny_font[c]->width)) * dupx); - - if (cx > scrwidth) - break; - if (cx+left + w < 0) //left boundary check - { - cx += w; - continue; - } - - V_DrawFixedPatch(cx<= HU_FONTSIZE || !hu_font[c]) - { - cx += (spacewidth * dupx)<width)*(dupx/2); - } - else - w = SHORT(hu_font[c]->width) * dupx; - - if ((cx>>FRACBITS) > scrwidth) - break; - if ((cx>>FRACBITS)+left + w < 0) //left boundary check - { - cx += w<= CRED_FONTSIZE) - { - cx += (16*dupx)<width) * dupx; - if ((cx>>FRACBITS) > scrwidth) - break; - - V_DrawSciencePatch(cx, cy, option, cred_font[c], FRACUNIT); - cx += w<= LT_FONTSIZE || !lt_font[c]) - { - cx += 12*dupx; - continue; - } - - w = SHORT(lt_font[c]->width) * dupx; - if (cx > scrwidth) - break; - if (cx+left + w < 0) //left boundary check - { - cx += w; - continue; - } - - V_DrawScaledPatch(cx, cy, option, lt_font[c]); - cx += w; - } -} - // Find string width from lt_font chars // INT32 V_LevelNameWidth(const char *string) diff --git a/src/v_video.h b/src/v_video.h index b01f65d06..d561cea4f 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -161,6 +161,12 @@ void V_DrawFadeScreen(UINT16 color, UINT8 strength); void V_DrawFadeConsBack(INT32 plines); +/* Convenience macros for leagacy string function macros. */ +#define V__DrawOneScaleString( x,y,scale,option,font,string ) \ + V_DrawStringScaled(x,y,scale,FRACUNIT,FRACUNIT,option,font,string) +#define V__DrawDupxString( x,y,scale,option,font,string )\ + V__DrawOneScaleString ((x)<>1,option,HU_FONT,string) void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *string); // draw a string using the tny_font -void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string); +#define V_DrawThinString( x,y,option,string ) \ + V__DrawDupxString (x,y,FRACUNIT,option,TINY_FONT,string) void V_DrawCenteredThinString(INT32 x, INT32 y, INT32 option, const char *string); void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *string); -void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string); +#define V_DrawStringAtFixed( x,y,option,string ) \ + V__DrawOneScaleString (x,y,FRACUNIT,option,HU_FONT,string) // Draw tall nums, used for menu, HUD, intermission void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num); @@ -213,7 +225,8 @@ void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colorm INT32 V_LevelNameWidth(const char *string); INT32 V_LevelNameHeight(const char *string); -void V_DrawCreditString(fixed_t x, fixed_t y, INT32 option, const char *string); +#define V_DrawCreditString( x,y,option,string ) \ + V__DrawOneScaleString (x,y,FRACUNIT,option,CRED_FONT,string) INT32 V_CreditStringWidth(const char *string); // Find string width from hu_font chars From e04f8b2e53ae3a142f6a1f7b6f855b68e3a8b80f Mon Sep 17 00:00:00 2001 From: James R Date: Wed, 26 Jun 2019 18:25:49 -0700 Subject: [PATCH 10/65] Use font system for other string drawing functions --- src/v_video.c | 58 +++++++++++++++++++++++++-------------------------- 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/src/v_video.c b/src/v_video.c index 5440cbcdb..146c784b1 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1393,17 +1393,17 @@ void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed) c -= HU_FONTSTART; else c = toupper(c) - HU_FONTSTART; - if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) + if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c]) return; - w = SHORT(hu_font[c]->width); + w = SHORT(fontv[HU_FONT].font[c]->width); if (x + w > vid.width) return; if (colormap != NULL) - V_DrawMappedPatch(x, y, flags, hu_font[c], colormap); + V_DrawMappedPatch(x, y, flags, fontv[HU_FONT].font[c], colormap); else - V_DrawScaledPatch(x, y, flags, hu_font[c]); + V_DrawScaledPatch(x, y, flags, fontv[HU_FONT].font[c]); } // Writes a single character for the chat (half scaled). (draw WHITE if bit 7 set) @@ -1420,14 +1420,14 @@ void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UI c -= HU_FONTSTART; else c = toupper(c) - HU_FONTSTART; - if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) + if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c]) return; - w = SHORT(hu_font[c]->width)/2; + w = SHORT(fontv[HU_FONT].font[c]->width)/2; if (x + w > vid.width) return; - V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT/2, flags, hu_font[c], colormap); + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, FRACUNIT/2, flags, fontv[HU_FONT].font[c], colormap); } // Precompile a wordwrapped string to any given width. @@ -1478,13 +1478,13 @@ char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string) c = toupper(c); c -= HU_FONTSTART; - if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) + if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c]) { chw = spacewidth; lastusablespace = i; } else - chw = (charwidth ? charwidth : hu_font[c]->width); + chw = (charwidth ? charwidth : fontv[HU_FONT].font[c]->width); x += chw; @@ -1826,7 +1826,7 @@ void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *st // Draws a tallnum. Replaces two functions in y_inter and st_stuff void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num) { - INT32 w = SHORT(tallnum[0]->width); + INT32 w = SHORT(fontv[TALLNUM_FONT].font[0]->width); boolean neg; if (flags & V_NOSCALESTART) @@ -1839,7 +1839,7 @@ void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num) do { x -= w; - V_DrawScaledPatch(x, y, flags, tallnum[num % 10]); + V_DrawScaledPatch(x, y, flags, fontv[TALLNUM_FONT].font[num % 10]); num /= 10; } while (num); @@ -1852,7 +1852,7 @@ void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num) // Does not handle negative numbers in a special way, don't try to feed it any. void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits) { - INT32 w = SHORT(tallnum[0]->width); + INT32 w = SHORT(fontv[TALLNUM_FONT].font[0]->width); if (flags & V_NOSCALESTART) w *= vid.dupx; @@ -1864,7 +1864,7 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits) do { x -= w; - V_DrawScaledPatch(x, y, flags, tallnum[num % 10]); + V_DrawScaledPatch(x, y, flags, fontv[TALLNUM_FONT].font[num % 10]); num /= 10; } while (--digits); } @@ -1874,7 +1874,7 @@ void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits) void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colormap) { - INT32 w = SHORT(pingnum[0]->width); // this SHOULD always be 5 but I guess custom graphics exist. + INT32 w = SHORT(fontv[PINGNUM_FONT].font[0]->width); // this SHOULD always be 5 but I guess custom graphics exist. if (flags & V_NOSCALESTART) w *= vid.dupx; @@ -1886,7 +1886,7 @@ void V_DrawPingNum(INT32 x, INT32 y, INT32 flags, INT32 num, const UINT8 *colorm do { x -= (w-1); // Oni wanted their outline to intersect. - V_DrawFixedPatch(x<= CRED_FONTSIZE) w += 16; else - w += SHORT(cred_font[c]->width); + w += SHORT(fontv[CRED_FONT].font[c]->width); } return w; @@ -1924,10 +1924,10 @@ INT32 V_LevelNameWidth(const char *string) for (i = 0; i < strlen(string); i++) { c = toupper(string[i]) - LT_FONTSTART; - if (c < 0 || c >= LT_FONTSIZE || !lt_font[c]) + if (c < 0 || c >= LT_FONTSIZE || !fontv[LT_FONT].font[c]) w += 12; else - w += SHORT(lt_font[c]->width); + w += SHORT(fontv[LT_FONT].font[c]->width); } return w; @@ -1943,11 +1943,11 @@ INT32 V_LevelNameHeight(const char *string) for (i = 0; i < strlen(string); i++) { c = toupper(string[i]) - LT_FONTSTART; - if (c < 0 || c >= LT_FONTSIZE || !lt_font[c]) + if (c < 0 || c >= LT_FONTSIZE || !fontv[LT_FONT].font[c]) continue; - if (SHORT(lt_font[c]->height) > w) - w = SHORT(lt_font[c]->height); + if (SHORT(fontv[LT_FONT].font[c]->height) > w) + w = SHORT(fontv[LT_FONT].font[c]->height); } return w; @@ -1983,10 +1983,10 @@ INT32 V_StringWidth(const char *string, INT32 option) continue; c = toupper(c) - HU_FONTSTART; - if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) + if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c]) w += spacewidth; else - w += (charwidth ? charwidth : SHORT(hu_font[c]->width)); + w += (charwidth ? charwidth : SHORT(fontv[HU_FONT].font[c]->width)); } return w; @@ -2022,10 +2022,10 @@ INT32 V_SmallStringWidth(const char *string, INT32 option) continue; c = toupper(c) - HU_FONTSTART; - if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) + if (c < 0 || c >= HU_FONTSIZE || !fontv[HU_FONT].font[c]) w += spacewidth; else - w += (charwidth ? charwidth : SHORT(hu_font[c]->width)/2); + w += (charwidth ? charwidth : SHORT(fontv[HU_FONT].font[c]->width)/2); } return w; @@ -2062,17 +2062,17 @@ INT32 V_ThinStringWidth(const char *string, INT32 option) if ((UINT8)c >= 0x80 && (UINT8)c <= 0x8F) //color parsing! -Inuyasha 2.16.09 continue; - if (!lowercase || !tny_font[c-HU_FONTSTART]) + if (!lowercase || !fontv[TINY_FONT].font[c-HU_FONTSTART]) c = toupper(c); c -= HU_FONTSTART; - if (c < 0 || c >= HU_FONTSIZE || !tny_font[c]) + if (c < 0 || c >= HU_FONTSIZE || !fontv[TINY_FONT].font[c]) w += spacewidth; else { w += (charwidth ? charwidth - : ((option & V_6WIDTHSPACE && i < strlen(string)-1) ? max(1, SHORT(tny_font[c]->width)-1) // Reuse this flag for the alternate bunched-up spacing - : SHORT(tny_font[c]->width))); + : ((option & V_6WIDTHSPACE && i < strlen(string)-1) ? max(1, SHORT(fontv[TINY_FONT].font[c]->width)-1) // Reuse this flag for the alternate bunched-up spacing + : SHORT(fontv[TINY_FONT].font[c]->width))); } } From 4a3262da41164fb54c7820fef2bf732ba326a445 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 9 May 2020 03:42:51 -0400 Subject: [PATCH 11/65] Initial GP commit Has a rudimentary option in the menu, which spawns the specific bots & spawns you on Green Hills in singleplayer. --- src/CMakeLists.txt | 4 ++ src/Makefile | 3 +- src/d_clisrv.c | 1 + src/d_main.c | 1 + src/k_bot.c | 141 ------------------------------------- src/k_grandprix.c | 168 +++++++++++++++++++++++++++++++++++++++++++++ src/k_grandprix.h | 18 +++++ src/m_menu.c | 27 ++++++-- src/p_setup.c | 17 ++++- 9 files changed, 230 insertions(+), 150 deletions(-) create mode 100644 src/k_grandprix.c create mode 100644 src/k_grandprix.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4447614d3..ce1fd031b 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -159,7 +159,9 @@ set(SRB2_CORE_GAME_SOURCES p_user.c k_battle.c k_bheap.c + k_bot.c k_collide.c + k_grandprix.c k_kart.c k_pathfind.c k_pwrlv.c @@ -177,7 +179,9 @@ set(SRB2_CORE_GAME_SOURCES p_tick.h k_battle.h k_bheap.h + k_bot.h k_collide.h + k_grandprix.h k_kart.h k_pathfind.h k_pwrlv.h diff --git a/src/Makefile b/src/Makefile index 2b1e7c7f8..27477d10f 100644 --- a/src/Makefile +++ b/src/Makefile @@ -501,6 +501,8 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/k_waypoint.o\ $(OBJDIR)/k_pathfind.o\ $(OBJDIR)/k_bheap.o \ + $(OBJDIR)/k_bot.o \ + $(OBJDIR)/k_grandprix.o\ $(OBJDIR)/m_aatree.o \ $(OBJDIR)/m_anigif.o \ $(OBJDIR)/m_argv.o \ @@ -550,7 +552,6 @@ OBJS:=$(i_main_o) \ $(OBJDIR)/i_tcp.o \ $(OBJDIR)/lzf.o \ $(OBJDIR)/vid_copy.o \ - $(OBJDIR)/k_bot.o \ $(i_cdmus_o) \ $(i_net_o) \ $(i_system_o) \ diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 406c07150..08b93bfbf 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -50,6 +50,7 @@ #include "k_battle.h" #include "k_pwrlv.h" #include "k_bot.h" +#include "k_grandprix.h" #ifdef CLIENT_LOADINGSCREEN // cl loading screen diff --git a/src/d_main.c b/src/d_main.c index 1ef581407..075a3f8b1 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -774,6 +774,7 @@ void D_StartTitle(void) // In case someone exits out at the same time they start a time attack run, // reset modeattacking modeattacking = ATTACKING_NONE; + grandprixmatch = 0; // empty maptol so mario/etc sounds don't play in sound test when they shouldn't maptol = 0; diff --git a/src/k_bot.c b/src/k_bot.c index 285207caa..bc35bc21d 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -197,147 +197,6 @@ void K_UpdateMatchRaceBots(void) // We should have enough bots now :) } -#if 0 -// This is mostly just pesudo code right now... -void K_InitGrandPrixBots(void) -{ - const UINT8 defaultbotskin = 9; // eggrobo - - // startingdifficulty: Easy = 3, Normal = 6, Hard = 9 - const UINT8 startingdifficulty = min(MAXBOTDIFFICULTY, (cv_kartspeed.value + 1) * 3); - UINT8 difficultylevels[MAXPLAYERS]; - - UINT8 playercount = 8; - UINT8 wantedbots = 0; - - UINT8 numplayers = 0; - UINT8 competitors[4]; - - boolean skinusable[MAXSKINS]; - UINT8 botskinlist[MAXPLAYERS]; - UINT8 botskinlistpos = 0; - - UINT8 i; - - memset(difficultylevels, MAXBOTDIFFICULTY, sizeof (difficultylevels)); - memset(competitors, MAXPLAYERS, sizeof (competitors)); - memset(botskinlist, defaultbotskin, sizeof (botskinlist)); - - // init usable bot skins list - for (i = 0; i < MAXSKINS; i++) - { - if (i < numskins) - { - skinusable[i] = true; - } - else - { - skinusable[i] = false; - } - } - - // init difficulty levels list - //if (!mastermodebots) { - difficultylevels[MAXPLAYERS] = { - max(1, startingdifficulty), - max(1, startingdifficulty-1), - max(1, startingdifficulty-2), - max(1, startingdifficulty-3), - max(1, startingdifficulty-3), - max(1, startingdifficulty-4), - max(1, startingdifficulty-4), - max(1, startingdifficulty-4), - max(1, startingdifficulty-5), - max(1, startingdifficulty-5), - max(1, startingdifficulty-6), - max(1, startingdifficulty-6), - max(1, startingdifficulty-7), - max(1, startingdifficulty-7), - max(1, startingdifficulty-8), - max(1, startingdifficulty-8), - }; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (numplayers < MAXSPLITSCREENPLAYERS) - { - if (playeringame[i] && !players[i].spectator) - { - competitors[numplayers] = i; - numplayers++; - } - } - else - { - if (playeringame[i]) - { - players[i].spectator = true; // force spectate for all other players, if they happen to exist? - } - } - } - - if (numplayers > 2) - { - // Add 3 bots per player beyond 2P - playercount += (numplayers-2) * 3; - } - - wantedbots = playercount - numplayers; - - // Create rival list - - // TODO: Use player skin's set rivals - // Starting with P1's rival1, P2's rival1, P3's rival1, P4's rival1, - // then P1's rival2, P2's rival2, etc etc etc etc....... - // then skip over any duplicates. - - // Pad the remaining list with random skins if we need to - if (botskinlistpos < wantedbots) - { - for (i = botskinlistpos; i < wantedbots; i++) - { - UINT8 val = M_RandomKey(numskins); - UINT8 loops = 0; - - while (!skinusable[val]) - { - if (loops >= numskins) - { - // no more skins - break; - } - - val++; - - if (val >= numskins) - { - val = 0; - } - - loops++; - } - - if (loops >= numskins) - { - // leave the rest of the table as the default skin - break; - } - - botskinlist[i] = val; - skinusable[val] = false; - } - } - - for (i = 0; i < wantedbots; i++) - { - if (!K_AddBot(botskinlist[i], difficultylevels[i], &newplayernum)) - { - break; - } - } -} -#endif - boolean K_PlayerUsesBotMovement(player_t *player) { if (player->bot || player->exiting) diff --git a/src/k_grandprix.c b/src/k_grandprix.c new file mode 100644 index 000000000..b8f04aa6e --- /dev/null +++ b/src/k_grandprix.c @@ -0,0 +1,168 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2007-2016 by John "JTE" Muniz. +// Copyright (C) 2011-2018 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_grandprix.c +/// \brief Grand Prix mode specific code + +#include "k_grandprix.h" +#include "doomdef.h" +#include "d_player.h" +#include "g_game.h" +#include "k_bot.h" +#include "k_kart.h" +#include "m_random.h" +#include "r_things.h" + +UINT8 grandprixmatch = 0; +boolean initgpbots = false; + +void K_InitGrandPrixBots(void) +{ + const UINT8 defaultbotskin = 9; // eggrobo + + // startingdifficulty: Easy = 3, Normal = 6, Hard = 9 + const UINT8 startingdifficulty = min(MAXBOTDIFFICULTY, (gamespeed + 1) * 3); + UINT8 difficultylevels[MAXPLAYERS]; + + UINT8 playercount = 8; + UINT8 wantedbots = 0; + + UINT8 numplayers = 0; + UINT8 competitors[4]; + + boolean skinusable[MAXSKINS]; + UINT8 botskinlist[MAXPLAYERS]; + UINT8 botskinlistpos = 0; + + UINT8 newplayernum = 0; + UINT8 i; + + if (initgpbots != true) + { + return; + } + + memset(difficultylevels, MAXBOTDIFFICULTY, sizeof (difficultylevels)); + memset(competitors, MAXPLAYERS, sizeof (competitors)); + memset(botskinlist, defaultbotskin, sizeof (botskinlist)); + + // init usable bot skins list + for (i = 0; i < MAXSKINS; i++) + { + if (i < numskins) + { + skinusable[i] = true; + } + else + { + skinusable[i] = false; + } + } + +#if MAXPLAYERS != 16 + I_Error("GP bot difficulty levels need rebalacned for the new player count!\n"); +#endif + + // init difficulty levels list + //if (!mastermodebots) { // leave as all level 9 + difficultylevels[0] = max(1, startingdifficulty); + difficultylevels[1] = max(1, startingdifficulty-1); + difficultylevels[2] = max(1, startingdifficulty-2); + difficultylevels[3] = max(1, startingdifficulty-3); + difficultylevels[4] = max(1, startingdifficulty-3); + difficultylevels[5] = max(1, startingdifficulty-4); + difficultylevels[6] = max(1, startingdifficulty-4); + difficultylevels[7] = max(1, startingdifficulty-4); + difficultylevels[8] = max(1, startingdifficulty-5); + difficultylevels[9] = max(1, startingdifficulty-5); + difficultylevels[10] = max(1, startingdifficulty-5); + difficultylevels[11] = max(1, startingdifficulty-6); + difficultylevels[12] = max(1, startingdifficulty-6); + difficultylevels[13] = max(1, startingdifficulty-6); + difficultylevels[14] = max(1, startingdifficulty-7); + difficultylevels[15] = max(1, startingdifficulty-7); + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + if (numplayers < MAXSPLITSCREENPLAYERS && !players[i].spectator) + { + competitors[numplayers] = i; + numplayers++; + } + else + { + players[i].spectator = true; // force spectate for all other players, if they happen to exist? + } + } + } + + if (numplayers > 2) + { + // Add 3 bots per player beyond 2P + playercount += (numplayers-2) * 3; + } + + wantedbots = playercount - numplayers; + + // Create rival list + + // TODO: Use player skin's set rivals + // Starting with P1's rival1, P2's rival1, P3's rival1, P4's rival1, + // then P1's rival2, P2's rival2, etc etc etc etc....... + // then skip over any duplicates. + + // Pad the remaining list with random skins if we need to + if (botskinlistpos < wantedbots) + { + for (i = botskinlistpos; i < wantedbots; i++) + { + UINT8 val = M_RandomKey(numskins); + UINT8 loops = 0; + + while (!skinusable[val]) + { + if (loops >= numskins) + { + // no more skins + break; + } + + val++; + + if (val >= numskins) + { + val = 0; + } + + loops++; + } + + if (loops >= numskins) + { + // leave the rest of the table as the default skin + break; + } + + botskinlist[i] = val; + skinusable[val] = false; + } + } + + for (i = 0; i < wantedbots; i++) + { + if (!K_AddBot(botskinlist[i], difficultylevels[i], &newplayernum)) + { + break; + } + } + + initgpbots = false; +} diff --git a/src/k_grandprix.h b/src/k_grandprix.h new file mode 100644 index 000000000..4644c43ca --- /dev/null +++ b/src/k_grandprix.h @@ -0,0 +1,18 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2007-2016 by John "JTE" Muniz. +// Copyright (C) 2012-2018 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_grandprix.h +/// \brief Grand Prix mode specific code + +#include "doomdef.h" + +extern UINT8 grandprixmatch; +extern boolean initgpbots; + +void K_InitGrandPrixBots(void); diff --git a/src/m_menu.c b/src/m_menu.c index 3abf64476..f9a9112ac 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -59,6 +59,7 @@ #include "k_kart.h" // SRB2kart #include "k_pwrlv.h" #include "d_player.h" // KITEM_ constants +#include "k_grandprix.h" #include "i_joy.h" // for joystick menu controls @@ -225,7 +226,7 @@ menu_t SP_MainDef, MP_MainDef, OP_MainDef; menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef, MISC_ChangeSpectateDef; // Single Player -//static void M_LoadGame(INT32 choice); +static void M_StartGrandPrix(INT32 choice); static void M_TimeAttack(INT32 choice); static boolean M_QuitTimeAttackMenu(void); static void M_BreakTheCapsules(INT32 choice); @@ -803,14 +804,14 @@ static menuitem_t SR_EmblemHintMenu[] = // Single Player Main static menuitem_t SP_MainMenu[] = { - //{IT_CALL | IT_STRING, NULL, "Grand Prix", M_LoadGame, 92}, - {IT_SECRET, NULL, "Time Attack", M_TimeAttack, 100}, - {IT_SECRET, NULL, "Break the Capsules", M_BreakTheCapsules, 108}, + {IT_CALL | IT_STRING, NULL, "Grand Prix", M_StartGrandPrix, 92}, + {IT_SECRET, NULL, "Time Attack", M_TimeAttack, 100}, + {IT_SECRET, NULL, "Break the Capsules", M_BreakTheCapsules, 108}, }; enum { - //spgrandprix, + spgrandprix, sptimeattack, spbreakthecapsules }; @@ -6568,6 +6569,8 @@ static void M_Credits(INT32 choice) static void M_SinglePlayerMenu(INT32 choice) { (void)choice; + + SP_MainMenu[spgrandprix].status = IT_CALL|IT_STRING; SP_MainMenu[sptimeattack].status = (M_SecretUnlocked(SECRET_TIMEATTACK)) ? IT_CALL|IT_STRING : IT_SECRET; SP_MainMenu[spbreakthecapsules].status = @@ -7646,6 +7649,20 @@ void M_DrawTimeAttackMenu(void) } } +// Start Grand Prix! +static void M_StartGrandPrix(INT32 choice) +{ + (void)choice; + + M_ClearMenus(true); + + grandprixmatch = 1; + initgpbots = true; + + G_DeferedInitNew(false, "MAP01", (UINT8)(cv_chooseskin.value-1), 0, false); // G_BuildMapName(startmap) +} + + // Going to Time Attack menu... static void M_TimeAttack(INT32 choice) { diff --git a/src/p_setup.c b/src/p_setup.c index d5891e84a..b4a2def48 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -88,6 +88,7 @@ #include "k_pwrlv.h" #include "k_waypoint.h" #include "k_bot.h" +#include "k_grandprix.h" // // Map MD5, calculated on level load. @@ -2426,9 +2427,6 @@ static void P_LevelInitStuff(void) memset(&battleovertime, 0, sizeof(struct battleovertime)); speedscramble = encorescramble = -1; - - //if (!grandprix) - K_UpdateMatchRaceBots(); } // @@ -3368,6 +3366,19 @@ boolean P_SetupLevel(boolean skipprecip) // NOW you can try to spawn in the Battle capsules, if there's not enough players for a match K_SpawnBattleCapsules(); + if (grandprixmatch == 0) + { + K_UpdateMatchRaceBots(); + } + else + { + if (initgpbots == true) + { + K_InitGrandPrixBots(); + initgpbots = false; + } + } + return true; } From cbac15f380cb644265b8601f6d16ec325a2d1ba1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 9 May 2020 17:36:36 -0400 Subject: [PATCH 12/65] Cup definitions from new-menus --- src/dehacked.c | 143 +++++++++++++++++++++++++++++++++++++++++++++++++ src/doomstat.h | 21 ++++++++ 2 files changed, 164 insertions(+) diff --git a/src/dehacked.c b/src/dehacked.c index 47f57513d..a0ae6f48f 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1394,6 +1394,113 @@ static void readlevelheader(MYFILE *f, INT32 num) Z_Free(s); } +static void readcupheader(MYFILE *f, cupheader_t *cup) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2; + char *tmp; + INT32 i; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Set / reset word, because some things (Lua.) move it + word = s; + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + i = atoi(word2); // used for numerical settings + strupr(word2); + + if (fastcmp(word, "ICON")) + { + deh_strlcpy(cup->icon, word2, + sizeof(cup->icon), va("%s Cup: icon", cup->name)); + } + else if (fastcmp(word, "LEVELLIST")) + { + cup->numlevels = 0; + + tmp = strtok(word2,","); + do { + INT32 map = atoi(tmp); + + if (tmp[0] >= 'A' && tmp[0] <= 'Z' && tmp[2] == '\0') + map = M_MapNumber(tmp[0], tmp[1]); + + if (!map) + break; + + if (cup->numlevels >= MAXLEVELLIST) + { + deh_warning("%s Cup: reached max levellist (%d)\n", cup->name, MAXLEVELLIST); + break; + } + + cup->levellist[cup->numlevels] = map - 1; + cup->numlevels++; + } while((tmp = strtok(NULL,",")) != NULL); + } + else if (fastcmp(word, "BONUSGAME")) + { + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') + i = M_MapNumber(word2[0], word2[1]); + cup->bonusgame = (INT16)i - 1; + } + else if (fastcmp(word, "SPECIALSTAGE")) + { + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') + i = M_MapNumber(word2[0], word2[1]); + cup->specialstage = (INT16)i - 1; + } + else if (fastcmp(word, "EMERALDNUM")) + { + if (i >= 0 && i <= 14) + cup->emeraldnum = (UINT8)i; + else + deh_warning("%s Cup: invalid emerald number %d", cup->name, i); + } + else if (fastcmp(word, "UNLOCKABLE")) + { + if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something + cup->unlockrequired = (SINT8)i - 1; + else + deh_warning("%s Cup: invalid unlockable number %d", cup->name, i); + } + else + deh_warning("%s Cup: unknown word '%s'", cup->name, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum) { char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL); @@ -3566,6 +3673,42 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad) } DEH_WriteUndoline(word, word2, UNDO_HEADER); } + else if (fastcmp(word, "CUP")) + { + cupheader_t *cup = kartcupheaders; + cupheader_t *prev = NULL; + + while (cup) + { + if (fastcmp(cup->name, word2)) + { + // mark as a major mod if it replaces an already-existing cup + G_SetGameModified(multiplayer, true); + break; + } + + prev = cup; + cup = cup->next; + } + + // Nothing found, add to the end. + if (!cup) + { + cup = Z_Calloc(sizeof (cupheader_t), PU_STATIC, NULL); + cup->id = numkartcupheaders; + deh_strlcpy(cup->name, word2, + sizeof(cup->name), va("Cup header %s: name", word2)); + if (prev != NULL) + prev->next = cup; + if (kartcupheaders == NULL) + kartcupheaders = cup; + numkartcupheaders++; + CONS_Printf("Added cup %d ('%s')\n", cup->id, cup->name); + } + + readcupheader(f, cup); + DEH_WriteUndoline(word, word2, UNDO_HEADER); + } else if (fastcmp(word, "CUTSCENE")) { if (i > 0 && i < 129) diff --git a/src/doomstat.h b/src/doomstat.h index 9464385ea..62652c87d 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -324,6 +324,27 @@ typedef struct extern mapheader_t* mapheaderinfo[NUMMAPS]; +// This could support more, but is that a good idea? +// Keep in mind that it may encourage people making overly long cups just because they "can", and would be a waste of memory. +#define MAXLEVELLIST 5 + +typedef struct cupheader_s +{ + UINT16 id; ///< Cup ID + char name[15]; ///< Cup title (14 chars) + char icon[9]; ///< Name of the icon patch + INT16 levellist[MAXLEVELLIST]; ///< List of levels that belong to this cup + UINT8 numlevels; ///< Number of levels defined in levellist + INT16 bonusgame; ///< Map number to use for bonus game + INT16 specialstage; ///< Map number to use for special stage + UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald) + SINT8 unlockrequired; ///< An unlockable is required to select this cup. -1 for no unlocking required. + struct cupheader_s *next; ///< Next cup in linked list +} cupheader_t; + +extern cupheader_t *kartcupheaders; // Start of cup linked list +extern UINT16 numkartcupheaders; + enum TypeOfLevel { TOL_SP = 0x01, ///< Single Player From 06002beb65d73284f519e9ab335d073d8c96f151 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 9 May 2020 18:42:00 -0400 Subject: [PATCH 13/65] Add cupheaders to g_game.c Whoops --- src/g_game.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/g_game.c b/src/g_game.c index 9d516625b..405ebbc72 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -166,6 +166,10 @@ struct quake quake; // Map Header Information mapheader_t* mapheaderinfo[NUMMAPS] = {NULL}; +// Kart cup definitions +cupheader_t *kartcupheaders = NULL; +UINT16 numkartcupheaders = 0; + static boolean exitgame = false; static boolean retrying = false; From 4ae81ff236c6003aa8d62de6613a097383f8382d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 9 May 2020 20:03:14 -0400 Subject: [PATCH 14/65] Add menu for selecting cup & other options --- src/d_main.c | 5 +- src/d_netcmd.c | 16 +++-- src/g_game.c | 76 ++++++++++------------- src/k_grandprix.c | 10 +--- src/k_grandprix.h | 28 +++++---- src/m_menu.c | 149 ++++++++++++++++++++++++++++++++++++++-------- src/p_setup.c | 38 +++++++----- 7 files changed, 210 insertions(+), 112 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index 075a3f8b1..bc3260134 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -75,6 +75,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #include "fastcmp.h" #include "keys.h" #include "filesrch.h" // refreshdirmenu +#include "k_grandprix.h" #ifdef CMAKECONFIG #include "config.h" @@ -774,7 +775,9 @@ void D_StartTitle(void) // In case someone exits out at the same time they start a time attack run, // reset modeattacking modeattacking = ATTACKING_NONE; - grandprixmatch = 0; + + // Reset GP + memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); // empty maptol so mario/etc sounds don't play in sound test when they shouldn't maptol = 0; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 12d246405..c330b36ee 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -50,6 +50,7 @@ #include "k_battle.h" #include "k_pwrlv.h" #include "y_inter.h" +#include "k_grandprix.h" #ifdef NETGAME_DEVMODE #define CV_RESTRICT CV_NETVAR @@ -2760,6 +2761,12 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r if (netgame || multiplayer) FLS = false; + if (grandprixinfo.roundnum != 0) + { + // Too lazy to change the input value for every instance of this function....... + pencoremode = grandprixinfo.encore; + } + if (delay != 2) { UINT8 flags = 0; @@ -2983,7 +2990,6 @@ static void Command_Map_f(void) // new encoremode value // use cvar by default - newencoremode = (cv_kartencore.value == 1); if (COM_CheckParm("-encore")) @@ -6314,7 +6320,7 @@ static void Command_ShowTime_f(void) // SRB2Kart: On change messages static void BaseNumLaps_OnChange(void) { - if (gamestate == GS_LEVEL) + if (gamestate == GS_LEVEL && grandprixinfo.roundnum == 0) { if (cv_basenumlaps.value) CONS_Printf(M_GetText("Number of laps will be changed to %d next round.\n"), cv_basenumlaps.value); @@ -6344,7 +6350,7 @@ static void KartSpeed_OnChange(void) return; } - if (G_RaceGametype()) + if (G_RaceGametype() && grandprixinfo.roundnum == 0) { if ((gamestate == GS_LEVEL && leveltime < starttime) && (cv_kartspeed.value != KARTSPEED_AUTO)) { @@ -6360,7 +6366,7 @@ static void KartSpeed_OnChange(void) static void KartEncore_OnChange(void) { - if (G_RaceGametype()) + if (G_RaceGametype() && grandprixinfo.roundnum == 0) { if ((cv_kartencore.value == 1) != encoremode && gamestate == GS_LEVEL /*&& leveltime > starttime*/) CONS_Printf(M_GetText("Encore Mode will be set to %s next round.\n"), cv_kartencore.string); @@ -6385,6 +6391,6 @@ static void KartComeback_OnChange(void) static void KartEliminateLast_OnChange(void) { - if (G_RaceGametype() && cv_karteliminatelast.value) + if (G_RaceGametype() && cv_karteliminatelast.value && grandprixinfo.roundnum == 0) P_CheckRacers(); } diff --git a/src/g_game.c b/src/g_game.c index 405ebbc72..0d6b27289 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -50,6 +50,7 @@ #include "k_kart.h" // SRB2kart #include "k_battle.h" #include "k_pwrlv.h" +#include "k_grandprix.h" gameaction_t gameaction; gamestate_t gamestate = GS_NULL; @@ -3628,7 +3629,6 @@ void G_AddMapToBuffer(INT16 map) static void G_DoCompleted(void) { INT32 i, j = 0; - boolean gottoken = false; SINT8 powertype = PWRLV_DISABLED; tokenlist = 0; // Reset the list @@ -3669,11 +3669,25 @@ static void G_DoCompleted(void) // go to next level // nextmap is 0-based, unlike gamemap if (nextmapoverride != 0) + { nextmap = (INT16)(nextmapoverride-1); - else if (mapheaderinfo[gamemap-1]->nextlevel == 1101) // SRB2Kart: !!! WHENEVER WE GET GRAND PRIX, GO TO AWARDS MAP INSTEAD !!! - nextmap = (INT16)(mapheaderinfo[gamemap] ? gamemap : (spstage_start-1)); // (gamemap-1)+1 == gamemap :V + } + else if (grandprixinfo.roundnum != 0) + { + if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) + { + nextmap = 1101; // ceremonymap + } + else + { + nextmap = grandprixinfo.cup->levellist[grandprixinfo.roundnum]; + grandprixinfo.roundnum++; + } + } else + { nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1); + } // Remember last map for when you come out of the special stage. if (!G_IsSpecialStage(gamemap)) @@ -3683,8 +3697,7 @@ static void G_DoCompleted(void) // a map of the proper gametype -- skip levels that don't support // the current gametype. (Helps avoid playing boss levels in Race, // for instance). - if (!token && !G_IsSpecialStage(gamemap) && !modeattacking - && (nextmap >= 0 && nextmap < NUMMAPS)) + if (!modeattacking && (grandprixinfo.roundnum == 0) && (nextmap >= 0 && nextmap < NUMMAPS)) { register INT16 cm = nextmap; INT16 tolflag = G_TOLFlag(gametype); @@ -3728,44 +3741,18 @@ static void G_DoCompleted(void) if (nextmap < 0 || (nextmap >= NUMMAPS && nextmap < 1100-1) || nextmap > 1102-1) I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1); - // wrap around in race - if (nextmap >= 1100-1 && nextmap <= 1102-1 && G_RaceGametype()) - nextmap = (INT16)(spstage_start-1); - - if (gametype == GT_COOP && token) - { - token--; - gottoken = true; - - if (!(emeralds & EMERALD1)) - nextmap = (INT16)(sstage_start - 1); // Special Stage 1 - else if (!(emeralds & EMERALD2)) - nextmap = (INT16)(sstage_start); // Special Stage 2 - else if (!(emeralds & EMERALD3)) - nextmap = (INT16)(sstage_start + 1); // Special Stage 3 - else if (!(emeralds & EMERALD4)) - nextmap = (INT16)(sstage_start + 2); // Special Stage 4 - else if (!(emeralds & EMERALD5)) - nextmap = (INT16)(sstage_start + 3); // Special Stage 5 - else if (!(emeralds & EMERALD6)) - nextmap = (INT16)(sstage_start + 4); // Special Stage 6 - else if (!(emeralds & EMERALD7)) - nextmap = (INT16)(sstage_start + 5); // Special Stage 7 - else - gottoken = false; - } - - if (G_IsSpecialStage(gamemap) && !gottoken) - nextmap = lastmap; // Exiting from a special stage? Go back to the game. Tails 08-11-2001 - automapactive = false; if (gametype != GT_COOP) { if (cv_advancemap.value == 0) // Stay on same map. + { nextmap = prevmap; + } else if (cv_advancemap.value == 2) // Go to random map. + { nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, false, 0, false, NULL); + } } // We are committed to this map now. @@ -3842,7 +3829,7 @@ void G_NextLevel(void) { if (gamestate != GS_VOTING) { - if ((cv_advancemap.value == 3) && !modeattacking && !skipstats && (multiplayer || netgame)) + if ((cv_advancemap.value == 3) && grandprixinfo.roundnum == 0 && !modeattacking && !skipstats && (multiplayer || netgame)) { UINT8 i; for (i = 0; i < MAXPLAYERS; i++) @@ -4508,7 +4495,7 @@ void G_SaveGame(UINT32 savegameslot) void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar, UINT8 ssplayers, boolean FLS) { INT32 i; - UINT8 color = 0; + //UINT8 color = 0; paused = false; if (demo.playback) @@ -4528,23 +4515,20 @@ void G_DeferedInitNew(boolean pencoremode, const char *mapname, INT32 pickedchar // this leave the actual game if needed SV_StartSinglePlayerServer(); - if (savedata.lives > 0) - { - color = savedata.skincolor; - } - else if (splitscreen != ssplayers) + if (splitscreen != ssplayers) { splitscreen = ssplayers; SplitScreen_OnChange(); } - if (!color && !modeattacking) - color = skins[pickedchar].prefcolor; + //if (!color) + //color = skins[pickedchar].prefcolor; + SetPlayerSkinByNum(consoleplayer, pickedchar); CV_StealthSet(&cv_skin, skins[pickedchar].name); - if (color) - CV_StealthSetValue(&cv_playercolor, color); + //if (color) + //CV_StealthSetValue(&cv_playercolor, color); if (mapname) D_MapChange(M_MapNumber(mapname[3], mapname[4]), gametype, pencoremode, true, 1, false, FLS); diff --git a/src/k_grandprix.c b/src/k_grandprix.c index b8f04aa6e..ddbda5a2e 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -19,8 +19,7 @@ #include "m_random.h" #include "r_things.h" -UINT8 grandprixmatch = 0; -boolean initgpbots = false; +struct grandprixinfo grandprixinfo; void K_InitGrandPrixBots(void) { @@ -43,11 +42,6 @@ void K_InitGrandPrixBots(void) UINT8 newplayernum = 0; UINT8 i; - if (initgpbots != true) - { - return; - } - memset(difficultylevels, MAXBOTDIFFICULTY, sizeof (difficultylevels)); memset(competitors, MAXPLAYERS, sizeof (competitors)); memset(botskinlist, defaultbotskin, sizeof (botskinlist)); @@ -163,6 +157,4 @@ void K_InitGrandPrixBots(void) break; } } - - initgpbots = false; } diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 4644c43ca..0eacf8ff9 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -1,18 +1,20 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 2007-2016 by John "JTE" Muniz. -// Copyright (C) 2012-2018 by Sonic Team Junior. -// -// This program is free software distributed under the -// terms of the GNU General Public License, version 2. -// See the 'LICENSE' file for more details. -//----------------------------------------------------------------------------- -/// \file k_grandprix.h -/// \brief Grand Prix mode specific code +#ifndef __K_GRANDPRIX__ +#define __K_GRANDPRIX__ #include "doomdef.h" +#include "doomstat.h" -extern UINT8 grandprixmatch; -extern boolean initgpbots; +extern struct grandprixinfo +{ + UINT8 roundnum; ///< Round number -- if 0, then we're not in a Grand Prix. + cupheader_t *cup; ///< Which cup are we playing? + UINT8 gamespeed; ///< Copy of gamespeed, just to make sure you can't cheat it with cvars + boolean encore; ///< Ditto, but for encore mode + boolean masterbots; ///< If true, all bots should be max difficulty (Master Mode) + boolean initbots; ///< If true, we need to initialize the bots that are competing. +} grandprixinfo; void K_InitGrandPrixBots(void); + +#endif + diff --git a/src/m_menu.c b/src/m_menu.c index f9a9112ac..879db62ea 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -226,6 +226,7 @@ menu_t SP_MainDef, MP_MainDef, OP_MainDef; menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef, MISC_ChangeSpectateDef; // Single Player +static void M_GrandPrixTemp(INT32 choice); static void M_StartGrandPrix(INT32 choice); static void M_TimeAttack(INT32 choice); static boolean M_QuitTimeAttackMenu(void); @@ -240,6 +241,7 @@ static void M_ModeAttackEndGame(INT32 choice); static void M_SetGuestReplay(INT32 choice); //static void M_ChoosePlayer(INT32 choice); menu_t SP_LevelStatsDef; +static menu_t SP_GrandPrixTempDef; static menu_t SP_TimeAttackDef, SP_ReplayDef, SP_GuestReplayDef, SP_GhostDef; //static menu_t SP_NightsAttackDef, SP_NightsReplayDef, SP_NightsGuestReplayDef, SP_NightsGhostDef; @@ -466,6 +468,13 @@ static consvar_t cv_dummycontinues = {"dummycontinues", "0", CV_HIDEN, liveslimi //static consvar_t cv_dummymares = {"dummymares", "Overall", CV_HIDEN|CV_CALL, dummymares_cons_t, Dummymares_OnChange, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_dummystaff = {"dummystaff", "0", CV_HIDEN|CV_CALL, dummystaff_cons_t, Dummystaff_OnChange, 0, NULL, NULL, 0, 0, NULL}; +static CV_PossibleValue_t dummygpdifficulty_cons_t[] = {{0, "Easy"}, {1, "Normal"}, {2, "Hard"}, {3, "Master"}, {0, NULL}}; +static CV_PossibleValue_t dummygpcup_cons_t[50] = {{1, "TEMP"}}; // A REALLY BIG NUMBER, SINCE THIS IS TEMP UNTIL NEW MENUS + +static consvar_t cv_dummygpdifficulty = {"dummygpdifficulty", "Normal", CV_HIDEN, dummygpdifficulty_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cv_dummygpencore = {"dummygpencore", "Off", CV_HIDEN, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +static consvar_t cv_dummygpcup = {"dummygpcup", "TEMP", CV_HIDEN, dummygpcup_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; + // ========================================================================== // ORGANIZATION START. // ========================================================================== @@ -804,7 +813,7 @@ static menuitem_t SR_EmblemHintMenu[] = // Single Player Main static menuitem_t SP_MainMenu[] = { - {IT_CALL | IT_STRING, NULL, "Grand Prix", M_StartGrandPrix, 92}, + {IT_STRING|IT_CALL, NULL, "Grand Prix", M_GrandPrixTemp, 92}, {IT_SECRET, NULL, "Time Attack", M_TimeAttack, 100}, {IT_SECRET, NULL, "Break the Capsules", M_BreakTheCapsules, 108}, }; @@ -817,17 +826,17 @@ enum }; // Single Player Load Game -/*static menuitem_t SP_LoadGameMenu[] = +static menuitem_t SP_GrandPrixPlaceholderMenu[] = { - {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLoadSave, '\0'}, // dummy menuitem for the control func -}; + {IT_STRING|IT_CVAR, NULL, "Character", &cv_chooseskin, 10}, + {IT_STRING|IT_CVAR, NULL, "Color", &cv_playercolor, 20}, -// Single Player Level Select -static menuitem_t SP_LevelSelectMenu[] = -{ - {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 78}, - {IT_WHITESTRING|IT_CALL, NULL, "Start", M_LevelSelectWarp, 130}, -};*/ + {IT_STRING|IT_CVAR, NULL, "Difficulty", &cv_dummygpdifficulty, 40}, + {IT_STRING|IT_CVAR, NULL, "Encore Mode", &cv_dummygpencore, 50}, + + {IT_STRING|IT_CVAR, NULL, "Cup", &cv_dummygpcup, 70}, + {IT_STRING|IT_CALL, NULL, "Start", M_StartGrandPrix, 80}, +}; // Single Player Time Attack static menuitem_t SP_TimeAttackMenu[] = @@ -1830,6 +1839,8 @@ menu_t SP_LevelStatsDef = NULL }; +static menu_t SP_GrandPrixTempDef = DEFAULTMENUSTYLE(NULL, SP_GrandPrixPlaceholderMenu, &MainDef, 60, 30); + static menu_t SP_TimeAttackDef = { "M_ATTACK", @@ -3277,6 +3288,10 @@ void M_Init(void) //CV_RegisterVar(&cv_dummymares); CV_RegisterVar(&cv_dummystaff); + CV_RegisterVar(&cv_dummygpdifficulty); + CV_RegisterVar(&cv_dummygpencore); + CV_RegisterVar(&cv_dummygpcup); + quitmsg[QUITMSG] = M_GetText("Eggman's tied explosives\nto your girlfriend, and\nwill activate them if\nyou press the 'Y' key!\nPress 'N' to save her!\n\n(Press 'Y' to quit)"); quitmsg[QUITMSG1] = M_GetText("What would Tails say if\nhe saw you quitting the game?\n\n(Press 'Y' to quit)"); quitmsg[QUITMSG2] = M_GetText("Hey!\nWhere do ya think you're goin'?\n\n(Press 'Y' to quit)"); @@ -4147,6 +4162,35 @@ static void M_PatchSkinNameTable(void) return; } +// +// M_PrepareCupList +// +static void M_PrepareCupList(void) +{ + cupheader_t *cup = kartcupheaders; + INT32 i = 0; + + memset(dummygpcup_cons_t, 0, sizeof (dummygpcup_cons_t)); + + while (cup != NULL) + { + dummygpcup_cons_t[i].strvalue = cup->name; + dummygpcup_cons_t[i].value = i+1; + // this will probably crash or do something stupid at over 50 cups, + // but this is all behavior that gets completely overwritten in new-menus, so I'm not worried + i++; + cup = cup->next; + } + + for (; i < 50; i++) + { + dummygpcup_cons_t[i].strvalue = NULL; + dummygpcup_cons_t[i].value = 0; + } + + CV_SetValue(&cv_dummygpcup, 1); // This causes crash sometimes?! +} + // Call before showing any level-select menus static void M_PrepareLevelSelect(void) { @@ -7455,6 +7499,77 @@ static void M_HandleLevelStats(INT32 choice) } } +static void M_GrandPrixTemp(INT32 choice) +{ + (void)choice; + M_PatchSkinNameTable(); + M_PrepareCupList(); + M_SetupNextMenu(&SP_GrandPrixTempDef); +} + +// Start Grand Prix! +static void M_StartGrandPrix(INT32 choice) +{ + cupheader_t *gpcup = kartcupheaders; + + (void)choice; + + if (gpcup == NULL) + { + // welp + I_Error("No cup definitions for GP\n"); + return; + } + + M_ClearMenus(true); + + memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); + + switch (cv_dummygpdifficulty.value) + { + case 0: + grandprixinfo.gamespeed = KARTSPEED_EASY; + break; + case 1: + default: + grandprixinfo.gamespeed = KARTSPEED_NORMAL; + break; + case 2: + grandprixinfo.gamespeed = KARTSPEED_HARD; + break; + case 3: + grandprixinfo.gamespeed = KARTSPEED_HARD; + grandprixinfo.masterbots = true; + break; + + } + + grandprixinfo.encore = (boolean)(cv_dummygpencore.value); + + while (gpcup != NULL && gpcup->id != cv_dummygpcup.value-1) + { + gpcup = gpcup->next; + } + + if (gpcup == NULL) + { + gpcup = kartcupheaders; + } + + grandprixinfo.cup = gpcup; + + grandprixinfo.roundnum = 1; + grandprixinfo.initbots = true; + + G_DeferedInitNew( + false, + G_BuildMapName(grandprixinfo.cup->levellist[0] + 1), + (UINT8)(cv_chooseskin.value - 1), + (UINT8)(cv_splitplayers.value - 1), + false + ); +} + // =========== // MODE ATTACK // =========== @@ -7649,20 +7764,6 @@ void M_DrawTimeAttackMenu(void) } } -// Start Grand Prix! -static void M_StartGrandPrix(INT32 choice) -{ - (void)choice; - - M_ClearMenus(true); - - grandprixmatch = 1; - initgpbots = true; - - G_DeferedInitNew(false, "MAP01", (UINT8)(cv_chooseskin.value-1), 0, false); // G_BuildMapName(startmap) -} - - // Going to Time Attack menu... static void M_TimeAttack(INT32 choice) { diff --git a/src/p_setup.c b/src/p_setup.c index b4a2def48..dbb1769f7 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2401,8 +2401,23 @@ static void P_LevelInitStuff(void) } // SRB2Kart: map load variables - if (modeattacking) // Just play it safe and set everything + if (grandprixinfo.roundnum != 0) { + if (G_BattleGametype()) + { + gamespeed = KARTSPEED_EASY; + } + else + { + gamespeed = grandprixinfo.gamespeed; + } + + franticitems = false; + comeback = true; + } + else if (modeattacking) + { + // Just play it safe and set everything gamespeed = KARTSPEED_HARD; franticitems = false; comeback = true; @@ -2611,12 +2626,6 @@ static void P_ForceCharacter(const char *forcecharskin) } SetPlayerSkin(consoleplayer, forcecharskin); - // normal player colors in single player - if ((unsigned)cv_playercolor.value != skins[players[consoleplayer].skin].prefcolor && !modeattacking) - { - CV_StealthSetValue(&cv_playercolor, skins[players[consoleplayer].skin].prefcolor); - players[consoleplayer].skincolor = skins[players[consoleplayer].skin].prefcolor; - } } } @@ -3366,17 +3375,18 @@ boolean P_SetupLevel(boolean skipprecip) // NOW you can try to spawn in the Battle capsules, if there's not enough players for a match K_SpawnBattleCapsules(); - if (grandprixmatch == 0) + if (grandprixinfo.roundnum != 0) { - K_UpdateMatchRaceBots(); + if (grandprixinfo.initbots == true) + { + K_InitGrandPrixBots(); + grandprixinfo.initbots = false; + } } else { - if (initgpbots == true) - { - K_InitGrandPrixBots(); - initgpbots = false; - } + // We're in a Match Race, use simplistic randomized bots. + K_UpdateMatchRaceBots(); } return true; From 22afa6865117da73b7d27f4fd3ef6929112e95e6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 9 May 2020 21:55:50 -0400 Subject: [PATCH 15/65] Rivals system You can input a predetermined list of rivals for each character, up to 3. In S_SKIN: `rivals = sonic,tails,knuckles`. Can support skin names from addons without throwing warnings. --- src/k_grandprix.c | 24 +++++++++++++++++++----- src/r_things.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ src/r_things.h | 3 +++ 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index ddbda5a2e..708390f5a 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -40,7 +40,7 @@ void K_InitGrandPrixBots(void) UINT8 botskinlistpos = 0; UINT8 newplayernum = 0; - UINT8 i; + UINT8 i, j; memset(difficultylevels, MAXBOTDIFFICULTY, sizeof (difficultylevels)); memset(competitors, MAXPLAYERS, sizeof (competitors)); @@ -107,11 +107,25 @@ void K_InitGrandPrixBots(void) wantedbots = playercount - numplayers; // Create rival list + if (numplayers > 0) + { + for (i = 0; i < SKINRIVALS; i++) + { + for (j = 0; j < numplayers; j++) + { + player_t *p = &players[competitors[j]]; + char *rivalname = skins[p->skin].rivals[i]; + SINT8 rivalnum = R_SkinAvailable(rivalname); - // TODO: Use player skin's set rivals - // Starting with P1's rival1, P2's rival1, P3's rival1, P4's rival1, - // then P1's rival2, P2's rival2, etc etc etc etc....... - // then skip over any duplicates. + if (rivalnum != -1 && skinusable[rivalnum]) + { + botskinlist[botskinlistpos] = rivalnum; + skinusable[rivalnum] = false; + botskinlistpos++; + } + } + } + } // Pad the remaining list with random skins if we need to if (botskinlistpos < wantedbots) diff --git a/src/r_things.c b/src/r_things.c index 587be375d..8e92a1c66 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -2893,6 +2893,11 @@ static void Sk_SetDefaultValue(skin_t *skin) strncpy(skin->facewant, "PLAYWANT", 9); strncpy(skin->facemmap, "PLAYMMAP", 9); + for (i = 0; i < SKINRIVALS; i++) + { + strcpy(skin->rivals[i], ""); + } + skin->starttranscolor = 96; skin->prefcolor = SKINCOLOR_GREEN; @@ -3163,6 +3168,45 @@ void R_AddSkins(UINT16 wadnum) strupr(value); strncpy(skin->facemmap, value, sizeof skin->facemmap); } + else if (!stricmp(stoken, "rivals")) + { + size_t len = strlen(value); + size_t i; + char rivalname[SKINNAMESIZE] = ""; + UINT8 pos = 0; + UINT8 numrivals = 0; + + // Can't use strtok, because this function's already using it. + // Using it causes it to upset the saved pointer, + // corrupting the reading for the rest of the file. + + // So instead we get to crawl through the value, character by character, + // and write it down as we go, until we hit a comma or the end of the string. + // Yaaay. + + for (i = 0; i <= len; i++) + { + if (numrivals >= SKINRIVALS) + { + break; + } + + if (value[i] == ',' || i == len) + { + STRBUFCPY(skin->rivals[numrivals], rivalname); + strlwr(skin->rivals[numrivals]); + numrivals++; + + memset(rivalname, 0, sizeof (rivalname)); + pos = 0; + + continue; + } + + rivalname[pos] = value[i]; + pos++; + } + } #define FULLPROCESS(field) else if (!stricmp(stoken, #field)) skin->field = get_number(value); // character type identification diff --git a/src/r_things.h b/src/r_things.h index 470e32fb2..6d0e86425 100644 --- a/src/r_things.h +++ b/src/r_things.h @@ -67,6 +67,7 @@ void R_DrawMasked(void); // SKINS STUFF // ----------- #define SKINNAMESIZE 16 +#define SKINRIVALS 3 // should be all lowercase!! S_SKIN processing does a strlwr #define DEFAULTSKIN "sonic" #define DEFAULTSKIN2 "tails" // secondary player @@ -95,6 +96,8 @@ typedef struct UINT8 prefcolor; fixed_t highresscale; // scale of highres, default is 0.5 + char rivals[SKINRIVALS][SKINNAMESIZE+1]; // Your top 3 rivals for GP mode. Uses names so that you can reference skins that aren't added + // specific sounds per skin sfxenum_t soundsid[NUMSKINSOUNDS]; // sound # in S_sfx table } skin_t; From c1e8cc108fda769bfa8a29e8d20ed89b0b3788b3 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 9 May 2020 22:32:56 -0400 Subject: [PATCH 16/65] Master Mode bots are functional --- src/k_grandprix.c | 44 +++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 708390f5a..da78cb332 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -42,7 +42,6 @@ void K_InitGrandPrixBots(void) UINT8 newplayernum = 0; UINT8 i, j; - memset(difficultylevels, MAXBOTDIFFICULTY, sizeof (difficultylevels)); memset(competitors, MAXPLAYERS, sizeof (competitors)); memset(botskinlist, defaultbotskin, sizeof (botskinlist)); @@ -63,24 +62,31 @@ void K_InitGrandPrixBots(void) I_Error("GP bot difficulty levels need rebalacned for the new player count!\n"); #endif - // init difficulty levels list - //if (!mastermodebots) { // leave as all level 9 - difficultylevels[0] = max(1, startingdifficulty); - difficultylevels[1] = max(1, startingdifficulty-1); - difficultylevels[2] = max(1, startingdifficulty-2); - difficultylevels[3] = max(1, startingdifficulty-3); - difficultylevels[4] = max(1, startingdifficulty-3); - difficultylevels[5] = max(1, startingdifficulty-4); - difficultylevels[6] = max(1, startingdifficulty-4); - difficultylevels[7] = max(1, startingdifficulty-4); - difficultylevels[8] = max(1, startingdifficulty-5); - difficultylevels[9] = max(1, startingdifficulty-5); - difficultylevels[10] = max(1, startingdifficulty-5); - difficultylevels[11] = max(1, startingdifficulty-6); - difficultylevels[12] = max(1, startingdifficulty-6); - difficultylevels[13] = max(1, startingdifficulty-6); - difficultylevels[14] = max(1, startingdifficulty-7); - difficultylevels[15] = max(1, startingdifficulty-7); + if (grandprixinfo.masterbots) + { + // Everyone is max difficulty!! + memset(difficultylevels, MAXBOTDIFFICULTY, sizeof (difficultylevels)); + } + else + { + // init difficulty levels list + difficultylevels[0] = max(1, startingdifficulty); + difficultylevels[1] = max(1, startingdifficulty-1); + difficultylevels[2] = max(1, startingdifficulty-2); + difficultylevels[3] = max(1, startingdifficulty-3); + difficultylevels[4] = max(1, startingdifficulty-3); + difficultylevels[5] = max(1, startingdifficulty-4); + difficultylevels[6] = max(1, startingdifficulty-4); + difficultylevels[7] = max(1, startingdifficulty-4); + difficultylevels[8] = max(1, startingdifficulty-5); + difficultylevels[9] = max(1, startingdifficulty-5); + difficultylevels[10] = max(1, startingdifficulty-5); + difficultylevels[11] = max(1, startingdifficulty-6); + difficultylevels[12] = max(1, startingdifficulty-6); + difficultylevels[13] = max(1, startingdifficulty-6); + difficultylevels[14] = max(1, startingdifficulty-7); + difficultylevels[15] = max(1, startingdifficulty-7); + } for (i = 0; i < MAXPLAYERS; i++) { From 8bb145e24914a75d24ac27eea4853c271c39b1c4 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 9 May 2020 22:33:10 -0400 Subject: [PATCH 17/65] Use proper scoring system in GP --- src/g_game.c | 10 +-- src/k_pwrlv.c | 22 +++++++ src/k_pwrlv.h | 1 + src/y_inter.c | 176 ++++++++++++++++++++++++++++++-------------------- 4 files changed, 130 insertions(+), 79 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 0d6b27289..c3b561c1b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3629,7 +3629,7 @@ void G_AddMapToBuffer(INT16 map) static void G_DoCompleted(void) { INT32 i, j = 0; - SINT8 powertype = PWRLV_DISABLED; + SINT8 powertype = K_UsingPowerLevels(); tokenlist = 0; // Reset the list @@ -3762,14 +3762,6 @@ static void G_DoCompleted(void) P_AllocMapHeader(nextmap); // Set up power level gametype scrambles - if (netgame && cv_kartusepwrlv.value) - { - if (G_RaceGametype()) - powertype = PWRLV_RACE; - else if (G_BattleGametype()) - powertype = PWRLV_BATTLE; - } - K_SetPowerLevelScrambles(powertype); demointermission: diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c index b8d78e0ee..20810a7f5 100644 --- a/src/k_pwrlv.c +++ b/src/k_pwrlv.c @@ -8,6 +8,7 @@ #include "m_random.h" #include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems #include "p_tick.h" // leveltime +#include "k_grandprix.h" // Online rankings for the main gametypes. // This array is saved to the gamedata. @@ -25,6 +26,27 @@ INT16 nospectategrief[MAXPLAYERS]; SINT8 speedscramble = -1; SINT8 encorescramble = -1; +SINT8 K_UsingPowerLevels(void) +{ + SINT8 pt = PWRLV_DISABLED; + + if (!cv_kartusepwrlv.value || !netgame || grandprixinfo.roundnum > 0) + { + return PWRLV_DISABLED; + } + + if (G_RaceGametype()) + { + pt = PWRLV_RACE; + } + else if (G_BattleGametype()) + { + pt = PWRLV_BATTLE; + } + + return pt; +} + void K_ClearClientPowerLevels(void) { UINT8 i, j; diff --git a/src/k_pwrlv.h b/src/k_pwrlv.h index dfa300114..579e298af 100644 --- a/src/k_pwrlv.h +++ b/src/k_pwrlv.h @@ -21,6 +21,7 @@ extern UINT16 vspowerlevel[PWRLV_NUMTYPES]; extern UINT16 clientpowerlevels[MAXPLAYERS][PWRLV_NUMTYPES]; extern INT16 nospectategrief[MAXPLAYERS]; +SINT8 K_UsingPowerLevels(void); void K_ClearClientPowerLevels(void); INT16 K_CalculatePowerLevelInc(INT16 diff); INT16 K_CalculatePowerLevelAvg(void); diff --git a/src/y_inter.c b/src/y_inter.c index 62bb5ca5e..9a1dac032 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -43,6 +43,7 @@ #include "k_pwrlv.h" #include "console.h" // cons_menuhighlight #include "lua_hook.h" // IntermissionThinker hook +#include "k_grandprix.h" #ifdef HWRENDER #include "hardware/hw_main.h" @@ -198,7 +199,12 @@ static void Y_CompareScore(INT32 i) static void Y_CompareRank(INT32 i) { INT16 increase = ((data.match.increase[i] == INT16_MIN) ? 0 : data.match.increase[i]); - UINT32 score = (powertype != -1 ? clientpowerlevels[i][powertype] : players[i].score); + UINT32 score = players[i].score; + + if (powertype != PWRLV_DISABLED) + { + score = clientpowerlevels[i][powertype]; + } if (!(data.match.val[data.match.numplayers] == UINT32_MAX || (score - increase) > data.match.val[data.match.numplayers])) return; @@ -306,14 +312,18 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) else data.match.pos[data.match.numplayers] = data.match.numplayers+1; - if ((!rankingsmode && powertype == -1) // Single player rankings (grand prix). Online rank is handled below. - && !(players[i].pflags & PF_TIMEOVER) && (data.match.pos[data.match.numplayers] < (numplayersingame + numgriefers))) + if ((powertype == PWRLV_DISABLED) + && (!rankingsmode) + && !(players[i].pflags & PF_TIMEOVER) + && (data.match.pos[data.match.numplayers] < (numplayersingame + numgriefers))) { + // Single player rankings (grand prix). Online rank is handled below. data.match.increase[i] = (numplayersingame + numgriefers) - data.match.pos[data.match.numplayers]; players[i].score += data.match.increase[i]; } if (demo.recording && !rankingsmode) + { G_WriteStanding( data.match.pos[data.match.numplayers], data.match.name[data.match.numplayers], @@ -321,6 +331,7 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) *data.match.color[data.match.numplayers], data.match.val[data.match.numplayers] ); + } data.match.numplayers++; } @@ -440,9 +451,36 @@ void Y_IntermissionDrawer(void) int y2; if (data.match.rankingsmode) - timeheader = "PWR.LV"; + { + if (powertype == PWRLV_DISABLED) + { + timeheader = "RANK"; + } + else + { + timeheader = "PWR.LV"; + } + } else - timeheader = ((intertype == int_race || (intertype == int_match && battlecapsules)) ? "TIME" : "SCORE"); + { + switch (intertype) + { + default: + case int_race: + timeheader = "TIME"; + break; + case int_match: + if (battlecapsules) + { + timeheader = "TIME"; + } + else + { + timeheader = "SCORE"; + } + break; + } + } // draw the level name V_DrawCenteredString(-4 + x + BASEVIDWIDTH/2, 12, 0, data.match.levelstring); @@ -533,8 +571,11 @@ void Y_IntermissionDrawer(void) if (data.match.rankingsmode) { - if (!clientpowerlevels[data.match.num[i]][powertype]) // No power level (splitscreen guests) + if (powertype != PWRLV_DISABLED && !clientpowerlevels[data.match.num[i]][powertype]) + { + // No power level (splitscreen guests) STRBUFCPY(strtime, "----"); + } else { if (data.match.increase[data.match.num[i]] != INT16_MIN) @@ -600,7 +641,7 @@ void Y_IntermissionDrawer(void) } dotimer: - if (timer) + if (timer && grandprixinfo.roundnum == 0) { char *string; INT32 tickdown = (timer+1)/TICRATE; @@ -705,74 +746,73 @@ void Y_Ticker(void) if (intertype == int_race || intertype == int_match) { - if (netgame || multiplayer) + if (!(multiplayer && demo.playback)) // Don't advance to rankings in replays { - if (sorttic == -1) - sorttic = intertic + max((cv_inttime.value/2)-2, 2)*TICRATE; // 8 second pause after match results - else if (!(multiplayer && demo.playback)) // Don't advance to rankings in replays + if (!data.match.rankingsmode && (intertic >= sorttic + 8)) { - if (!data.match.rankingsmode && (intertic >= sorttic + 8)) - Y_CalculateMatchData(1, Y_CompareRank); + Y_CalculateMatchData(1, Y_CompareRank); + } - if (data.match.rankingsmode && intertic > sorttic+16+(2*TICRATE)) + if (data.match.rankingsmode && intertic > sorttic+16+(2*TICRATE)) + { + INT32 q=0,r=0; + boolean kaching = true; + + for (q = 0; q < data.match.numplayers; q++) { - INT32 q=0,r=0; - boolean kaching = true; - - for (q = 0; q < data.match.numplayers; q++) - { - if (data.match.num[q] == MAXPLAYERS + if (data.match.num[q] == MAXPLAYERS || !data.match.increase[data.match.num[q]] || data.match.increase[data.match.num[q]] == INT16_MIN) - continue; + { + continue; + } - r++; - data.match.jitter[data.match.num[q]] = 1; + r++; + data.match.jitter[data.match.num[q]] = 1; - if (powertype != -1) + if (powertype != PWRLV_DISABLED) + { + // Power Levels + if (abs(data.match.increase[data.match.num[q]]) < 10) { - // Power Levels - if (abs(data.match.increase[data.match.num[q]]) < 10) - { - // Not a lot of point increase left, just set to 0 instantly - data.match.increase[data.match.num[q]] = 0; - } - else - { - SINT8 remove = 0; // default (should not happen) - - if (data.match.increase[data.match.num[q]] < 0) - remove = -10; - else if (data.match.increase[data.match.num[q]] > 0) - remove = 10; - - // Remove 10 points at a time - data.match.increase[data.match.num[q]] -= remove; - - // Still not zero, no kaching yet - if (data.match.increase[data.match.num[q]] != 0) - kaching = false; - } + // Not a lot of point increase left, just set to 0 instantly + data.match.increase[data.match.num[q]] = 0; } else { - // Basic bitch points - if (data.match.increase[data.match.num[q]]) - { - if (--data.match.increase[data.match.num[q]]) - kaching = false; - } + SINT8 remove = 0; // default (should not happen) + + if (data.match.increase[data.match.num[q]] < 0) + remove = -10; + else if (data.match.increase[data.match.num[q]] > 0) + remove = 10; + + // Remove 10 points at a time + data.match.increase[data.match.num[q]] -= remove; + + // Still not zero, no kaching yet + if (data.match.increase[data.match.num[q]] != 0) + kaching = false; } } - - if (r) - { - S_StartSound(NULL, (kaching ? sfx_chchng : sfx_ptally)); - Y_CalculateMatchData(2, Y_CompareRank); - } else - endtic = intertic + 3*TICRATE; // 3 second pause after end of tally + { + // Basic bitch points + if (data.match.increase[data.match.num[q]]) + { + if (--data.match.increase[data.match.num[q]]) + kaching = false; + } + } } + + if (r) + { + S_StartSound(NULL, (kaching ? sfx_chchng : sfx_ptally)); + Y_CalculateMatchData(2, Y_CompareRank); + } + else + endtic = intertic + 3*TICRATE; // 3 second pause after end of tally } } else @@ -1043,19 +1083,11 @@ void Y_StartIntermission(void) #endif // set player Power Level type - powertype = PWRLV_DISABLED; - - if (netgame && cv_kartusepwrlv.value) - { - if (G_RaceGametype()) - powertype = PWRLV_RACE; - else if (G_BattleGametype()) - powertype = PWRLV_BATTLE; - } + powertype = K_UsingPowerLevels(); if (!multiplayer) { - timer = 0; + timer = 20*TICRATE; if (!majormods && !multiplayer && !demo.playback) // move this once we have a proper time attack screen { @@ -1074,7 +1106,7 @@ void Y_StartIntermission(void) } else { - if (cv_inttime.value == 0 && gametype == GT_COOP) + if (cv_inttime.value == 0) timer = 0; else if (demo.playback) // Override inttime (which is pulled from the replay anyway timer = 10*TICRATE; @@ -1087,6 +1119,8 @@ void Y_StartIntermission(void) } } + sorttic = max((timer/2) - 2*TICRATE, 2*TICRATE); // 8 second pause after match results + if (gametype == GT_MATCH) intertype = int_match; else //if (gametype == GT_RACE) @@ -1132,7 +1166,9 @@ void Y_StartIntermission(void) } if (powertype != PWRLV_DISABLED) + { K_UpdatePowerLevels(); + } //if (intertype == int_race || intertype == int_match) { From e2d95ae9c1545e059dc5883dd75f1ad92ee97697 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 9 May 2020 23:08:11 -0400 Subject: [PATCH 18/65] Fake results for bots that didn't get to finish in time --- src/g_game.c | 41 +++++++++++++++---------------------- src/k_battle.c | 1 - src/k_grandprix.c | 52 +++++++++++++++++++++++++++++++++++++++++++++++ src/k_grandprix.h | 1 + src/p_setup.c | 9 ++------ src/p_user.c | 2 -- 6 files changed, 71 insertions(+), 35 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index c3b561c1b..d4b328c76 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3646,10 +3646,21 @@ static void G_DoCompleted(void) // SRB2Kart: exitlevel shouldn't get you the points if (!players[i].exiting && !(players[i].pflags & PF_TIMEOVER)) { - players[i].pflags |= PF_TIMEOVER; - if (P_IsLocalPlayer(&players[i])) - j++; + if (players[i].bot) + { + K_FakeBotResults(&players[i]); + } + else + { + players[i].pflags |= PF_TIMEOVER; + + if (P_IsLocalPlayer(&players[i])) + { + j++; + } + } } + G_PlayerFinishLevel(i); // take away cards and stuff } @@ -3918,7 +3929,7 @@ static void G_DoContinued(void) token = 0; // Reset # of lives - pl->lives = (ultimatemode) ? 1 : 3; + pl->lives = 3; D_MapChange(gamemap, gametype, false, false, 0, false, false); @@ -4562,27 +4573,7 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool players[i].starpostangle = players[i].starpostnum = players[i].starposttime = 0; players[i].starpostx = players[i].starposty = players[i].starpostz = 0; -#if 0 - if (netgame || multiplayer) - { - players[i].lives = cv_startinglives.value; - players[i].continues = 0; - } - else if (pultmode) - { - players[i].lives = 1; - players[i].continues = 0; - } - else - { - players[i].lives = 3; - players[i].continues = 1; - } - - players[i].xtralife = 0; -#else - players[i].lives = 1; // SRB2Kart -#endif + players[i].lives = 3; // SRB2Kart // The latter two should clear by themselves, but just in case players[i].pflags &= ~(PF_TAGIT|PF_TAGGED|PF_FULLSTASIS); diff --git a/src/k_battle.c b/src/k_battle.c index fe727cee3..fc1141581 100644 --- a/src/k_battle.c +++ b/src/k_battle.c @@ -254,7 +254,6 @@ void K_CheckBumpers(void) for (i = 0; i < MAXPLAYERS; i++) { players[i].pflags |= PF_TIMEOVER; - //players[i].lives = 0; P_DoPlayerExit(&players[i]); } } diff --git a/src/k_grandprix.c b/src/k_grandprix.c index da78cb332..c3a4185cc 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -178,3 +178,55 @@ void K_InitGrandPrixBots(void) } } } + +void K_FakeBotResults(player_t *bot) +{ + const UINT32 distfactor = 32; + UINT32 worstdist = 0; + tic_t besttime = UINT32_MAX; + UINT8 numplayers = 0; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + { + numplayers++; + + if (players[i].exiting && players[i].realtime < besttime) + { + besttime = players[i].realtime; + } + } + } + + if (besttime == UINT32_MAX) + { + // No one finished, so you don't finish either. + bot->pflags |= PF_TIMEOVER; + return; + } + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator) + { + if (players[i].distancetofinish > worstdist) + { + worstdist = players[i].distancetofinish; + } + } + } + + if (bot->distancetofinish >= worstdist) + { + // Last place, you aren't going to finish. + bot->pflags |= PF_TIMEOVER; + return; + } + + // hey, you "won" + bot->exiting = 2; + bot->realtime = bot->realtime + (bot->distancetofinish / distfactor); + bot->distancetofinish = 0; +} diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 0eacf8ff9..d0734bc42 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -15,6 +15,7 @@ extern struct grandprixinfo } grandprixinfo; void K_InitGrandPrixBots(void); +void K_FakeBotResults(player_t *bot); #endif diff --git a/src/p_setup.c b/src/p_setup.c index dbb1769f7..a353314f8 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2352,15 +2352,10 @@ static void P_LevelInitStuff(void) for (i = 0; i < MAXPLAYERS; i++) { -#if 0 - if ((netgame || multiplayer) && (gametype == GT_COMPETITION || players[i].lives <= 0)) + if (grandprixinfo.roundnum == 0) { - // In Co-Op, replenish a user's lives if they are depleted. - players[i].lives = cv_startinglives.value; + players[i].lives = 3; } -#else - players[i].lives = 1; // SRB2Kart -#endif players[i].realtime = countdown = countdown2 = 0; curlap = bestlap = 0; // SRB2Kart diff --git a/src/p_user.c b/src/p_user.c index 7621542b0..05d96bec1 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8023,8 +8023,6 @@ void P_DoTimeOver(player_t *player) P_DamageMobj(player->mo, NULL, NULL, 10000); } - player->lives = 0; - P_EndingMusic(player); if (!countdown2) From 2282fa7452d2e8ccc6acb52c285b92362a8ec39a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 10 May 2020 00:17:36 -0400 Subject: [PATCH 19/65] Adjust bot rubberbanding distance with game speed --- src/k_bot.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index bc35bc21d..a6a7b0a29 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -219,8 +219,9 @@ boolean K_BotCanTakeCut(player_t *player) static UINT32 K_BotRubberbandDistance(player_t *player) { - const UINT32 spacing = 2048; + const UINT32 spacing = 1024; const UINT8 portpriority = player - players; + UINT32 dist = 0; UINT8 pos = 0; UINT8 i; @@ -236,20 +237,23 @@ static UINT32 K_BotRubberbandDistance(player_t *player) // First check difficulty levels, then score, then settle it with port priority! if (player->botvars.difficulty < players[i].botvars.difficulty) { - pos++; + pos += 3; } else if (player->score < players[i].score) { - pos++; + pos += 2; } else if (i < portpriority) { - pos++; + pos += 1; } } } - return (pos * spacing); + dist = (pos * spacing); + dist = FixedDiv(dist * FRACUNIT, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; + + return dist; } fixed_t K_BotRubberband(player_t *player) From fa5fccffc58acb6556f392a5f0f0749db9cef59a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 13 May 2020 02:14:39 -0400 Subject: [PATCH 20/65] Implement lives system Lose a life & restart the current race if you place below the top half. Lose all of your lives, and you get kicked to the title screen. --- src/d_clisrv.c | 2 + src/d_clisrv.h | 1 + src/d_netcmd.c | 14 ++--- src/d_player.h | 1 + src/g_game.c | 130 +++++++++++++++++++++++++++--------------- src/k_bot.c | 12 ---- src/k_bot.h | 15 ++--- src/k_grandprix.c | 27 +++++++++ src/k_grandprix.h | 5 +- src/k_kart.c | 9 +-- src/lua_playerlib.c | 4 ++ src/m_menu.c | 4 +- src/p_inter.c | 135 ++++++++++++++++++++++++++++++-------------- src/p_saveg.c | 2 + src/p_setup.c | 37 ++++++------ src/p_user.c | 124 +++++++++++++++------------------------- 16 files changed, 303 insertions(+), 219 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 358ab15f3..5d7c0da83 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -580,6 +580,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) // Score is resynched in the rspfirm resync packet rsp->health = 0; // resynched with mo health rsp->lives = players[i].lives; + rsp->lostlife = players[i].lostlife; rsp->continues = players[i].continues; rsp->scoreadd = players[i].scoreadd; rsp->xtralife = players[i].xtralife; @@ -710,6 +711,7 @@ static void resynch_read_player(resynch_pak *rsp) // Score is resynched in the rspfirm resync packet players[i].health = rsp->health; players[i].lives = rsp->lives; + players[i].lostlife = rsp->lostlife; players[i].continues = rsp->continues; players[i].scoreadd = rsp->scoreadd; players[i].xtralife = rsp->xtralife; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index abf43e5f9..027ad42dd 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -219,6 +219,7 @@ typedef struct // Score is resynched in the confirm resync packet INT32 health; SINT8 lives; + boolean lostlife; SINT8 continues; UINT8 scoreadd; SINT8 xtralife; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 500520e12..244e6df59 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -5710,14 +5710,14 @@ void Command_ExitGame_f(void) void Command_Retry_f(void) { - if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_VOTING)) + if (!(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)) + { CONS_Printf(M_GetText("You must be in a level to use this.\n")); - else if (netgame || multiplayer) - CONS_Printf(M_GetText("This only works in single player.\n")); - /*else if (!&players[consoleplayer] || players[consoleplayer].lives <= 1) - CONS_Printf(M_GetText("You can't retry without any lives remaining!\n")); - else if (G_IsSpecialStage(gamemap)) - CONS_Printf(M_GetText("You can't retry special stages!\n"));*/ + } + else if (netgame || grandprixinfo.roundnum == 0) + { + CONS_Printf(M_GetText("This only works in Grand Prix.\n")); + } else { M_ClearMenus(true); diff --git a/src/d_player.h b/src/d_player.h index 40e5a4bc0..d3af08ede 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -506,6 +506,7 @@ typedef struct player_s UINT32 charflags; // Extra abilities/settings for skins (combinable stuff) // See SF_ flags SINT8 lives; + boolean lostlife; SINT8 continues; // continues that player has acquired SINT8 xtralife; // Ring Extra Life counter diff --git a/src/g_game.c b/src/g_game.c index 99230316b..a5e31994b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2354,15 +2354,17 @@ void G_Ticker(boolean run) if (gamestate == GS_LEVEL) { // Or, alternatively, retry. - if (!(netgame || multiplayer) && G_GetRetryFlag()) + if (G_GetRetryFlag()) { G_ClearRetryFlag(); - // Costs a life to retry ... unless the player in question is dead already. - /*if (G_GametypeUsesLives() && players[consoleplayer].playerstate == PST_LIVE) - players[consoleplayer].lives -= 1; - - G_DoReborn(consoleplayer);*/ + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + K_PlayerLoseLife(&players[i]); + } + } D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), true, 1, false, false); } @@ -2569,6 +2571,7 @@ void G_PlayerReborn(INT32 player) player_t *p; INT32 score, marescore; INT32 lives; + boolean lostlife; INT32 continues; // SRB2kart UINT8 kartspeed; @@ -2613,6 +2616,7 @@ void G_PlayerReborn(INT32 player) score = players[player].score; marescore = players[player].marescore; lives = players[player].lives; + lostlife = players[player].lostlife; continues = players[player].continues; ctfteam = players[player].ctfteam; exiting = players[player].exiting; @@ -2697,6 +2701,7 @@ void G_PlayerReborn(INT32 player) p->score = score; p->marescore = marescore; p->lives = lives; + p->lostlife = lostlife; p->continues = continues; p->pflags = pflags; p->ctfteam = ctfteam; @@ -3217,6 +3222,47 @@ void G_ExitLevel(void) { if (gamestate == GS_LEVEL) { + if (grandprixinfo.roundnum > 0 && grandprixinfo.wonround != true) + { + UINT8 i; + + // You didn't win... + + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && !players[i].spectator && !players[i].bot) + { + if (players[i].lives > 0) + { + break; + } + } + } + + if (i == MAXPLAYERS) + { + // GAME OVER, try again from the start! + + if (netgame) + { + ; // restart cup here if we do online GP + } + else + { + D_QuitNetGame(); + CL_Reset(); + D_StartTitle(); + } + } + else + { + // Go redo this course. + G_SetRetryFlag(); + } + + return; + } + gameaction = ga_completed; lastdraw = true; @@ -3292,18 +3338,15 @@ boolean G_IsSpecialStage(INT32 mapnum) // boolean G_GametypeUsesLives(void) { - // SRB2kart NEEDS no lives -#if 0 - // Coop, Competitive - if ((gametype == GT_COOP || gametype == GT_COMPETITION) - && !modeattacking // No lives in Time Attack - //&& !G_IsSpecialStage(gamemap) - && !(maptol & TOL_NIGHTS)) // No lives in NiGHTS + if ((grandprixinfo.roundnum > 0) // In Grand Prix + && (gametype == GT_RACE) // NOT in bonus round + && !(modeattacking) // NOT in Record Attack + && !G_IsSpecialStage(gamemap)) // NOT in special stage + { return true; + } + return false; -#else - return false; -#endif } // @@ -4570,43 +4613,42 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool if (!demo.playback && !netgame) // Netgame sets random seed elsewhere, demo playback sets seed just before us! P_SetRandSeed(M_RandomizedSeed()); // Use a more "Random" random seed - //SRB2Kart - Score is literally the only thing you SHOULDN'T reset at all times - //if (resetplayer) + // Clear a bunch of variables + tokenlist = token = sstimer = redscore = bluescore = lastmap = 0; + racecountdown = exitcountdown = mapreset = 0; + + for (i = 0; i < MAXPLAYERS; i++) { - // Clear a bunch of variables - tokenlist = token = sstimer = redscore = bluescore = lastmap = 0; - racecountdown = exitcountdown = mapreset = 0; + players[i].playerstate = PST_REBORN; + players[i].starpostangle = players[i].starpostnum = players[i].starposttime = 0; + players[i].starpostx = players[i].starposty = players[i].starpostz = 0; - for (i = 0; i < MAXPLAYERS; i++) + // The latter two should clear by themselves, but just in case + players[i].pflags &= ~(PF_TAGIT|PF_TAGGED|PF_FULLSTASIS); + + // Clear cheatcodes too, just in case. + players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS); + + players[i].marescore = 0; + + if (resetplayer && !(multiplayer && demo.playback)) // SRB2Kart { - players[i].playerstate = PST_REBORN; - players[i].starpostangle = players[i].starpostnum = players[i].starposttime = 0; - players[i].starpostx = players[i].starposty = players[i].starpostz = 0; + players[i].score = 0; - players[i].lives = 3; // SRB2Kart - - // The latter two should clear by themselves, but just in case - players[i].pflags &= ~(PF_TAGIT|PF_TAGGED|PF_FULLSTASIS); - - // Clear cheatcodes too, just in case. - players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS); - - players[i].marescore = 0; - - if (resetplayer && !(multiplayer && demo.playback)) // SRB2Kart + if (grandprixinfo.roundnum == 0 || grandprixinfo.initalize == true) { - players[i].score = 0; + players[i].lives = 3; } } - - // Reset unlockable triggers - unlocktriggers = 0; - - // clear itemfinder, just in case - if (!dedicated) // except in dedicated servers, where it is not registered and can actually I_Error debug builds - CV_StealthSetValue(&cv_itemfinder, 0); } + // Reset unlockable triggers + unlocktriggers = 0; + + // clear itemfinder, just in case + if (!dedicated) // except in dedicated servers, where it is not registered and can actually I_Error debug builds + CV_StealthSetValue(&cv_itemfinder, 0); + // internal game map // well this check is useless because it is done before (d_netcmd.c::command_map_f) // but in case of for demos.... diff --git a/src/k_bot.c b/src/k_bot.c index a6a7b0a29..b7f79efd9 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1,15 +1,3 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 2007-2016 by John "JTE" Muniz. -// Copyright (C) 2011-2018 by Sonic Team Junior. -// -// This program is free software distributed under the -// terms of the GNU General Public License, version 2. -// See the 'LICENSE' file for more details. -//----------------------------------------------------------------------------- -/// \file k_bot.c -/// \brief Basic bot handling - #include "doomdef.h" #include "d_player.h" #include "g_game.h" diff --git a/src/k_bot.h b/src/k_bot.h index ef7409f97..afdbf5ee9 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -1,14 +1,5 @@ -// SONIC ROBO BLAST 2 -//----------------------------------------------------------------------------- -// Copyright (C) 2007-2016 by John "JTE" Muniz. -// Copyright (C) 2012-2018 by Sonic Team Junior. -// -// This program is free software distributed under the -// terms of the GNU General Public License, version 2. -// See the 'LICENSE' file for more details. -//----------------------------------------------------------------------------- -/// \file k_bot.h -/// \brief Basic bot handling +#ifndef __K_BOT__ +#define __K_BOT__ #include "k_waypoint.h" #include "d_player.h" @@ -28,3 +19,5 @@ boolean K_PlayerUsesBotMovement(player_t *player); boolean K_BotCanTakeCut(player_t *player); fixed_t K_BotRubberband(player_t *player); void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd); + +#endif diff --git a/src/k_grandprix.c b/src/k_grandprix.c index c3a4185cc..f54055598 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -230,3 +230,30 @@ void K_FakeBotResults(player_t *bot) bot->realtime = bot->realtime + (bot->distancetofinish / distfactor); bot->distancetofinish = 0; } + +void K_PlayerLoseLife(player_t *player) +{ + if (!G_GametypeUsesLives()) + { + return; + } + + if (player->spectator || player->exiting || player->bot || player->lostlife) + { + return; + } + + player->lives--; + player->lostlife = true; + +#if 0 + if (player->lives <= 0) + { + if (P_IsLocalPlayer(player)) + { + S_StopMusic(); + S_ChangeMusicInternal("gmover", false); + } + } +#endif +} diff --git a/src/k_grandprix.h b/src/k_grandprix.h index d0734bc42..2127a5eb6 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -11,11 +11,12 @@ extern struct grandprixinfo UINT8 gamespeed; ///< Copy of gamespeed, just to make sure you can't cheat it with cvars boolean encore; ///< Ditto, but for encore mode boolean masterbots; ///< If true, all bots should be max difficulty (Master Mode) - boolean initbots; ///< If true, we need to initialize the bots that are competing. + boolean initalize; ///< If true, we need to initialize a new cup. + boolean wonround; ///< If false, then we retry the map instead of going to the next. } grandprixinfo; void K_InitGrandPrixBots(void); void K_FakeBotResults(player_t *bot); +void K_PlayerLoseLife(player_t *player); #endif - diff --git a/src/k_kart.c b/src/k_kart.c index 5a6c0db34..cf984252c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9891,6 +9891,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I static void K_drawKartLapsAndRings(void) { + const boolean uselives = G_GametypeUsesLives(); SINT8 ringanim_realframe = stplyr->karthud[khud_ringframe]; INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM|V_SNAPTOLEFT); UINT8 rn[2]; @@ -9975,7 +9976,7 @@ static void K_drawKartLapsAndRings(void) } // Rings - if (netgame) + if (!uselives) { V_DrawScaledPatch(fx-2 + (flipflag ? (SHORT(kp_ringstickersplit[1]->width) - 3) : 0), fy-10, V_HUDTRANS|splitflags|flipflag, kp_ringstickersplit[1]); if (flipflag) @@ -9997,7 +9998,7 @@ static void K_drawKartLapsAndRings(void) V_DrawScaledPatch(fr-12, fy-23, V_HUDTRANS|splitflags, kp_ringspblocksmall[stplyr->karthud[khud_ringspblock]]); // Lives - if (!netgame) + if (uselives) { UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|splitflags, facemmapprefix[stplyr->skin], colormap); @@ -10015,7 +10016,7 @@ static void K_drawKartLapsAndRings(void) V_DrawKartString(LAPS_X+33, LAPS_Y+3, V_HUDTRANS|splitflags, va("%d/%d", stplyr->laps, cv_numlaps.value)); // Rings - if (netgame) + if (!uselives) V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[1]); else V_DrawScaledPatch(LAPS_X, LAPS_Y-11, V_HUDTRANS|splitflags, kp_ringsticker[0]); @@ -10039,7 +10040,7 @@ static void K_drawKartLapsAndRings(void) V_DrawScaledPatch(LAPS_X-5, LAPS_Y-28, V_HUDTRANS|splitflags, kp_ringspblock[stplyr->karthud[khud_ringspblock]]); // Lives - if (!netgame) + if (uselives) { UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); V_DrawMappedPatch(LAPS_X+46, LAPS_Y-16, V_HUDTRANS|splitflags, facerankprefix[stplyr->skin], colormap); diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 5088045f7..0a51c1e09 100644 --- a/src/lua_playerlib.c +++ b/src/lua_playerlib.c @@ -241,6 +241,8 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->charflags); else if (fastcmp(field,"lives")) lua_pushinteger(L, plr->lives); + else if (fastcmp(field,"lostlife")) + lua_pushboolean(L, plr->lostlife); else if (fastcmp(field,"continues")) lua_pushinteger(L, plr->continues); else if (fastcmp(field,"xtralife")) @@ -489,6 +491,8 @@ static int player_set(lua_State *L) plr->charflags = (UINT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"lives")) plr->lives = (SINT8)luaL_checkinteger(L, 3); + else if (fastcmp(field,"lostlife")) + plr->lostlife = luaL_checkboolean(L, 3); else if (fastcmp(field,"continues")) plr->continues = (SINT8)luaL_checkinteger(L, 3); else if (fastcmp(field,"xtralife")) diff --git a/src/m_menu.c b/src/m_menu.c index 34e0696c8..7dd5cf7c9 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -7649,7 +7649,9 @@ static void M_StartGrandPrix(INT32 choice) grandprixinfo.cup = gpcup; grandprixinfo.roundnum = 1; - grandprixinfo.initbots = true; + grandprixinfo.wonround = false; + + grandprixinfo.initalize = true; G_DeferedInitNew( false, diff --git a/src/p_inter.c b/src/p_inter.c index d6dc7a9e6..0aad1bc33 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -29,6 +29,7 @@ #include "k_kart.h" // SRB2kart #include "k_battle.h" #include "k_pwrlv.h" +#include "k_grandprix.h" // CTF player names #define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : "" @@ -1972,71 +1973,133 @@ void P_CheckPointLimit(void) // Checks whether or not to end a race netgame. boolean P_CheckRacers(void) { - INT32 i, j, numplayersingame = 0, numexiting = 0; + UINT8 i; + UINT8 numplayersingame = 0; + UINT8 numexiting = 0; + boolean eliminatelast = cv_karteliminatelast.value; + boolean canexit = true; boolean griefed = false; // Check if all the players in the race have finished. If so, end the level. for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[i] || players[i].spectator || players[i].exiting || players[i].bot || !players[i].lives) - continue; + if (nospectategrief[i] != -1) // prevent spectate griefing + { + griefed = true; + } - break; + if (!playeringame[i] || players[i].spectator || players[i].lives <= 0) // Not playing + { + // Y'all aren't even playing + continue; + } + + numplayersingame++; + + if (players[i].exiting || (players[i].pflags & PF_TIMEOVER)) + { + numexiting++; + } + else + { + if (players[i].bot) + { + // Isn't a human, thus doesn't matter. (Sorry, robots.) + continue; + } + + canexit = false; + } } - if (i == MAXPLAYERS) // finished + if (canexit) { + // Everyone's finished, we're done here! racecountdown = exitcountdown = 0; return true; } - for (j = 0; j < MAXPLAYERS; j++) + if (numplayersingame <= 1) { - if (nospectategrief[j] != -1) // prevent spectate griefing - griefed = true; - if (!playeringame[j] || players[j].spectator) - continue; - numplayersingame++; - if (players[j].exiting) - numexiting++; + // Never do this without enough players. + eliminatelast = false; + } + else + { + if (grandprixinfo.roundnum > 0) + { + // Always do this in GP + eliminatelast = true; + } + else if (griefed) + { + // Don't do this if someone spectated + eliminatelast = false; + } } - if (cv_karteliminatelast.value && numplayersingame > 1 && !griefed) + if (eliminatelast == true && (numplayersingame <= numexiting-1)) { - // check if we just got unlucky and there was only one guy who was a problem - for (j = i+1; j < MAXPLAYERS; j++) + // Everyone's done playing but one guy apparently. + // Just kill everyone who is still playing. + + for (i = 0; i < MAXPLAYERS; i++) { - if (!playeringame[j] || players[j].spectator || players[j].exiting || !players[j].lives) + if (!playeringame[i] || players[i].spectator || players[i].lives <= 0) // Not playing + { + // Y'all aren't even playing continue; - break; + } + + if (players[i].exiting || (players[i].pflags & PF_TIMEOVER)) + { + // You're done, you're free to go. + continue; + } + + P_DoTimeOver(&players[i]); } - if (j == MAXPLAYERS) // finish anyways, force a time over - { - P_DoTimeOver(&players[i]); - racecountdown = exitcountdown = 0; - return true; - } + // Everyone should be done playing at this point now. + racecountdown = exitcountdown = 0; + return true; } - if (!racecountdown) // Check to see if the winners have finished, to set countdown. + // SO, we're not done playing. + // Let's see if it's time to start the death counter! + + if (!racecountdown) { + // If the winners are all done, then start the death timer. UINT8 winningpos = 1; winningpos = max(1, numplayersingame/2); if (numplayersingame % 2) // any remainder? + { winningpos++; + } if (numexiting >= winningpos) - racecountdown = (((netgame || multiplayer) ? cv_countdowntime.value : 30)*TICRATE) + 1; // 30 seconds to finish, get going! + { + tic_t countdown = 30*TICRATE; // 30 seconds left to finish, get going! + + if (netgame) + { + // Custom timer + countdown = cv_countdowntime.value * TICRATE; + } + + racecountdown = countdown + 1; + } } - if (numplayersingame < 2) // reset nospectategrief in free play + // We're still playing, but no one else is, so we need to reset spectator griefing. + if (numplayersingame <= 1) { - for (j = 0; j < MAXPLAYERS; j++) - nospectategrief[j] = -1; + memset(nospectategrief, -1, sizeof (nospectategrief)); } + // Turns out we're still having a good time & playing the game, we didn't have to do anything :) return false; } @@ -2250,20 +2313,6 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source) target->flags |= MF_NOBLOCKMAP|MF_NOCLIPHEIGHT; P_SetThingPosition(target); - if (!target->player->bot && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives()) - { - target->player->lives -= 1; // Lose a life Tails 03-11-2000 - - if (target->player->lives <= 0) // Tails 03-14-2000 - { - if (P_IsLocalPlayer(target->player)/* && target->player == &players[consoleplayer] */) - { - S_StopMusic(); // Stop the Music! Tails 03-14-2000 - S_ChangeMusicInternal("gmover", false); // Yousa dead now, Okieday? Tails 03-14-2000 - } - } - } - target->player->playerstate = PST_DEAD; if (target->player == &players[consoleplayer]) diff --git a/src/p_saveg.c b/src/p_saveg.c index 007ce919c..6f8b5cb79 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -142,6 +142,7 @@ static void P_NetArchivePlayers(void) WRITEFIXED(save_p, players[i].dashspeed); WRITEINT32(save_p, players[i].dashtime); WRITESINT8(save_p, players[i].lives); + WRITEUINT8(save_p, players[i].lostlife); WRITESINT8(save_p, players[i].continues); WRITESINT8(save_p, players[i].xtralife); WRITEUINT8(save_p, players[i].gotcontinue); @@ -327,6 +328,7 @@ static void P_NetUnArchivePlayers(void) players[i].dashspeed = READFIXED(save_p); // dashing speed players[i].dashtime = READINT32(save_p); // dashing speed players[i].lives = READSINT8(save_p); + players[i].lostlife = (boolean)READUINT8(save_p); players[i].continues = READSINT8(save_p); // continues that player has acquired players[i].xtralife = READSINT8(save_p); // Ring Extra Life counter players[i].gotcontinue = READUINT8(save_p); // got continue from stage diff --git a/src/p_setup.c b/src/p_setup.c index 73cbc615a..5228f7ed8 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2350,6 +2350,8 @@ static void P_LevelInitStuff(void) memset(localaiming, 0, sizeof(localaiming)); + grandprixinfo.wonround = false; + // special stage tokens, emeralds, and ring total tokenbits = 0; runemeraldmanager = false; @@ -2399,6 +2401,7 @@ static void P_LevelInitStuff(void) players[i].realtime = racecountdown = exitcountdown = 0; curlap = bestlap = 0; // SRB2Kart + players[i].lostlife = false; players[i].gotcontinue = false; players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0; @@ -3362,6 +3365,23 @@ boolean P_SetupLevel(boolean skipprecip) } #endif + // NOW you can try to spawn in the Battle capsules, if there's not enough players for a match + K_SpawnBattleCapsules(); + + if (grandprixinfo.roundnum != 0) + { + if (grandprixinfo.initalize == true) + { + K_InitGrandPrixBots(); + grandprixinfo.initalize = false; + } + } + else if (!modeattacking) + { + // We're in a Match Race, use simplistic randomized bots. + K_UpdateMatchRaceBots(); + } + P_MapEnd(); // Remove the loading shit from the screen @@ -3413,23 +3433,6 @@ boolean P_SetupLevel(boolean skipprecip) #endif } - // NOW you can try to spawn in the Battle capsules, if there's not enough players for a match - K_SpawnBattleCapsules(); - - if (grandprixinfo.roundnum != 0) - { - if (grandprixinfo.initbots == true) - { - K_InitGrandPrixBots(); - grandprixinfo.initbots = false; - } - } - else if (!modeattacking) - { - // We're in a Match Race, use simplistic randomized bots. - K_UpdateMatchRaceBots(); - } - return true; } diff --git a/src/p_user.c b/src/p_user.c index 5b638152a..34fa8642f 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -40,13 +40,14 @@ #include "st_stuff.h" #include "lua_script.h" #include "lua_hook.h" -#include "k_bot.h" // Objectplace #include "m_cheat.h" // SRB2kart #include "m_cond.h" // M_UpdateUnlockablesAndExtraEmblems #include "k_kart.h" #include "console.h" // CON_LogMessage +#include "k_bot.h" +#include "k_grandprix.h" #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -1681,12 +1682,20 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) // Player exits the map via sector trigger void P_DoPlayerExit(player_t *player) { + const boolean losing = K_IsPlayerLosing(player); + if (player->exiting || mapreset) return; if (P_IsLocalPlayer(player) && (!player->spectator && !demo.playback)) legitimateexit = true; + if (G_GametypeUsesLives() && losing) + { + // Remove a life from the losing player + K_PlayerLoseLife(player); + } + if (G_RaceGametype()) // If in Race Mode, allow { player->exiting = raceexittime+2; @@ -1697,7 +1706,7 @@ void P_DoPlayerExit(player_t *player) if (P_IsDisplayPlayer(player)) { sfxenum_t sfx_id; - if (K_IsPlayerLosing(player)) + if (losing) sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_klose].skinsound]; else sfx_id = ((skin_t *)player->mo->skin)->soundsid[S_sfx[sfx_kwin].skinsound]; @@ -1705,7 +1714,7 @@ void P_DoPlayerExit(player_t *player) } else { - if (K_IsPlayerLosing(player)) + if (losing) S_StartSound(player->mo, sfx_klose); else S_StartSound(player->mo, sfx_kwin); @@ -1715,10 +1724,6 @@ void P_DoPlayerExit(player_t *player) if (cv_inttime.value > 0) P_EndingMusic(player); - // SRB2kart 120217 - //if (!exitcountdown) - //exitcountdown = racecountdown + 8*TICRATE; - if (P_CheckRacers()) player->exiting = raceexittime+1; } @@ -1730,24 +1735,18 @@ void P_DoPlayerExit(player_t *player) else player->exiting = raceexittime+2; // Accidental death safeguard??? - //player->pflags &= ~PF_GLIDING; - /* // SRB2kart - don't need - if (player->climbing) + if (grandprixinfo.roundnum > 0 && !losing && !player->bot) { - player->climbing = 0; - player->pflags |= PF_JUMPED; - P_SetPlayerMobjState(player->mo, S_PLAY_ATK1); + // YOU WIN + grandprixinfo.wonround = true; } - */ + player->powers[pw_underwater] = 0; player->powers[pw_spacetime] = 0; player->karthud[khud_cardanimation] = 0; // srb2kart: reset battle animation if (player == &players[consoleplayer]) demo.savebutton = leveltime; - - /*if (playeringame[player-players] && netgame && !circuitmap) - CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]);*/ } #define SPACESPECIAL 12 @@ -6995,14 +6994,9 @@ static void P_DeathThink(player_t *player) K_KartPlayerHUDUpdate(player); - // Force respawn if idle for more than 30 seconds in shooter modes. - if (player->lives > 0 /*&& leveltime >= starttime*/) // *could* you respawn? + if (player->lives > 0 && !(player->pflags & PF_TIMEOVER) && player->deadtimer > TICRATE) { - // SRB2kart - spawn automatically after 1 second - if (player->deadtimer > ((netgame || multiplayer) - ? cv_respawntime.value*TICRATE - : TICRATE)) // don't let them change it in record attack - player->playerstate = PST_REBORN; + player->playerstate = PST_REBORN; } // Keep time rolling @@ -8256,13 +8250,28 @@ static void P_CalcPostImg(player_t *player) void P_DoTimeOver(player_t *player) { - if (netgame && player->health > 0) + if (player->pflags & PF_TIMEOVER) + { + // NO! Don't do this! + return; + } + + if (P_IsLocalPlayer(player) && !demo.playback) + { + legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p + } + + if (netgame && !player->bot) + { CON_LogMessage(va(M_GetText("%s ran out of time.\n"), player_names[player-players])); + } player->pflags |= PF_TIMEOVER; - if (P_IsLocalPlayer(player) && !demo.playback) - legitimateexit = true; // SRB2kart: losing a race is still seeing it through to the end :p + if (G_GametypeUsesLives()) + { + K_PlayerLoseLife(player); + } if (player->mo) { @@ -8384,7 +8393,7 @@ void P_PlayerThink(player_t *player) { if (playeringame[i] && !players[i].spectator) { - if (!players[i].exiting && players[i].lives > 0) + if (!players[i].exiting && !(players[i].pflags & PF_TIMEOVER) && players[i].lives > 0) break; } } @@ -8392,24 +8401,26 @@ void P_PlayerThink(player_t *player) if (i == MAXPLAYERS && player->exiting == raceexittime+2) // finished player->exiting = raceexittime+1; +#if 0 // If 10 seconds are left on the timer, // begin the drown music for countdown! - - // SRB2Kart: despite how perfect this is, it's disabled FOR A REASON - /*if (racecountdown == 11*TICRATE - 1) + if (racecountdown == 11*TICRATE - 1) { if (P_IsLocalPlayer(player)) S_ChangeMusicInternal("drown", false); - }*/ + } +#endif // If you've hit the countdown and you haven't made // it to the exit, you're a goner! - else if (racecountdown == 1 && !player->exiting && !player->spectator && player->lives > 0) + if (racecountdown == 1 && !player->spectator && !player->exiting && !(player->pflags & PF_TIMEOVER) && player->lives > 0) { P_DoTimeOver(player); if (player->playerstate == PST_DEAD) + { return; + } } } @@ -8423,33 +8434,9 @@ void P_PlayerThink(player_t *player) if (player->exiting == 2 || exitcountdown == 2) { - if (cv_playersforexit.value) // Count to be sure everyone's exited + if (server) { - INT32 i; - - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i] || players[i].spectator || players[i].bot) - continue; - if (players[i].lives <= 0) - continue; - - if (!players[i].exiting || players[i].exiting > 3) - break; - } - - if (i == MAXPLAYERS) - { - if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); - } - else - player->exiting = 3; - } - else - { - if (server) - SendNetXCmd(XD_EXITLEVEL, NULL, 0); + SendNetXCmd(XD_EXITLEVEL, NULL, 0); } } } @@ -8487,28 +8474,9 @@ void P_PlayerThink(player_t *player) player->health = 1; } -#if 0 - if ((netgame || multiplayer) && player->lives <= 0) - { - // In Co-Op, replenish a user's lives if they are depleted. - // of course, this is just a cheap hack, meh... - player->lives = cv_startinglives.value; - } -#else - player->lives = 1; // SRB2Kart -#endif - // SRB2kart 010217 if (leveltime < starttime) player->powers[pw_nocontrol] = 2; - /* - if ((gametype == GT_RACE || gametype == GT_COMPETITION) && leveltime < 4*TICRATE) - { - cmd->buttons &= BT_BRAKE; // Remove all buttons except BT_BRAKE - cmd->forwardmove = 0; - cmd->sidemove = 0; - } - */ // Synchronizes the "real" amount of time spent in the level. if (!player->exiting) From 24b0c3afd601ee40772b5bff51fb69b4efda10c2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 13 May 2020 02:17:52 -0400 Subject: [PATCH 21/65] Use skinname for default bot --- src/k_grandprix.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index f54055598..77bdf723c 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -23,7 +23,8 @@ struct grandprixinfo grandprixinfo; void K_InitGrandPrixBots(void) { - const UINT8 defaultbotskin = 9; // eggrobo + const char *defaultbotskinname = "eggrobo"; + SINT8 defaultbotskin = R_SkinAvailable(defaultbotskinname); // startingdifficulty: Easy = 3, Normal = 6, Hard = 9 const UINT8 startingdifficulty = min(MAXBOTDIFFICULTY, (gamespeed + 1) * 3); @@ -42,6 +43,12 @@ void K_InitGrandPrixBots(void) UINT8 newplayernum = 0; UINT8 i, j; + if (defaultbotskin == -1) + { + // This shouldn't happen, but just in case + defaultbotskin = 0; + } + memset(competitors, MAXPLAYERS, sizeof (competitors)); memset(botskinlist, defaultbotskin, sizeof (botskinlist)); From 482141a95f99b2dc89c8f46fcad46effbf6bc28a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 13 May 2020 02:47:10 -0400 Subject: [PATCH 22/65] Reduce spacing between bots --- src/k_bot.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index b7f79efd9..c4acf481e 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -207,9 +207,8 @@ boolean K_BotCanTakeCut(player_t *player) static UINT32 K_BotRubberbandDistance(player_t *player) { - const UINT32 spacing = 1024; + const UINT32 spacing = FixedDiv(512 * FRACUNIT, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; const UINT8 portpriority = player - players; - UINT32 dist = 0; UINT8 pos = 0; UINT8 i; @@ -238,10 +237,7 @@ static UINT32 K_BotRubberbandDistance(player_t *player) } } - dist = (pos * spacing); - dist = FixedDiv(dist * FRACUNIT, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; - - return dist; + return (pos * spacing); } fixed_t K_BotRubberband(player_t *player) @@ -280,8 +276,8 @@ fixed_t K_BotRubberband(player_t *player) if (wanteddist > player->distancetofinish) { - // Whoa, you're too far ahead! - rubberband += (MAXBOTDIFFICULTY - player->botvars.difficulty) * distdiff; + // Whoa, you're too far ahead! Slow back down a little. + rubberband += (MAXBOTDIFFICULTY - player->botvars.difficulty) * (distdiff / 3); } else { From e51bceb567f32d4f9cff5b0bacd373b87b8525ee Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 13 May 2020 03:04:33 -0400 Subject: [PATCH 23/65] Items respawn in GP --- src/d_netcmd.c | 18 +----------------- src/d_netcmd.h | 4 ---- src/p_inter.c | 21 +++++++++++++++++++-- src/p_mobj.c | 7 ------- 4 files changed, 20 insertions(+), 30 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 244e6df59..f02d315a3 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -682,8 +682,6 @@ void D_RegisterServerCommands(void) AddMServCommands(); // p_mobj.c - CV_RegisterVar(&cv_itemrespawntime); - CV_RegisterVar(&cv_itemrespawn); CV_RegisterVar(&cv_flagtime); CV_RegisterVar(&cv_suddendeath); @@ -5152,11 +5150,6 @@ void D_GameTypeChanged(INT32 lastgametype) // There will always be a server, and this only needs to be done once. if (server && (multiplayer || netgame)) { - if (gametype == GT_COMPETITION || gametype == GT_COOP) - CV_SetValue(&cv_itemrespawn, 0); - else if (!cv_itemrespawn.changed) - CV_SetValue(&cv_itemrespawn, 1); - switch (gametype) { case GT_MATCH: @@ -5167,8 +5160,6 @@ void D_GameTypeChanged(INT32 lastgametype) CV_SetValue(&cv_pointlimit, 0); CV_SetValue(&cv_timelimit, 120); } - if (!cv_itemrespawntime.changed) - CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally break; case GT_TAG: case GT_HIDEANDSEEK: @@ -5179,8 +5170,6 @@ void D_GameTypeChanged(INT32 lastgametype) CV_SetValue(&cv_timelimit, 5); CV_SetValue(&cv_pointlimit, 0); } - if (!cv_itemrespawntime.changed) - CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally break; case GT_CTF: if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits @@ -5189,17 +5178,12 @@ void D_GameTypeChanged(INT32 lastgametype) CV_SetValue(&cv_timelimit, 0); CV_SetValue(&cv_pointlimit, 5); } - if (!cv_itemrespawntime.changed) - CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally break; } } else if (!multiplayer && !netgame) { - gametype = GT_RACE; // SRB2kart - // These shouldn't matter anymore - //CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); - //CV_SetValue(&cv_itemrespawn, 0); + gametype = GT_RACE; } // reset timelimit and pointlimit in race/coop, prevent stupid cheats diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 22ca441be..5e13f56da 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -60,10 +60,6 @@ extern consvar_t cv_usemouse2; extern consvar_t cv_mouse2opt; #endif -// normally in p_mobj but the .h is not read -extern consvar_t cv_itemrespawntime; -extern consvar_t cv_itemrespawn; - extern consvar_t cv_flagtime; extern consvar_t cv_suddendeath; diff --git a/src/p_inter.c b/src/p_inter.c index 0aad1bc33..a4845e8f5 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2217,11 +2217,28 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source) { if (target->flags & MF_MONITOR || target->type == MT_RANDOMITEM) { + UINT8 i; + P_SetTarget(&target->target, source); source->player->numboxes++; - if (cv_itemrespawn.value && (netgame || multiplayer)) + + for (i = 0; i < MAXPLAYERS; i++) { - target->fuse = cv_itemrespawntime.value*TICRATE + 2; // Random box generation + if (&players[i] == source->player) + { + continue; + } + + if (playeringame[i] && !players[i].spectator && players[i].lives != 0) + { + break; + } + } + + if (i < MAXPLAYERS) + { + // Respawn items in multiplayer, don't respawn them when alone + target->fuse = 2*TICRATE + 2; } } diff --git a/src/p_mobj.c b/src/p_mobj.c index 3d70b3c38..305e83d8a 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11377,9 +11377,6 @@ void P_RemoveSavegameMobj(mobj_t *mobj) P_RemoveThinker((thinker_t *)mobj); } -static CV_PossibleValue_t respawnitemtime_cons_t[] = {{1, "MIN"}, {300, "MAX"}, {0, NULL}}; -consvar_t cv_itemrespawntime = {"respawnitemtime", "2", CV_NETVAR|CV_CHEAT, respawnitemtime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_itemrespawn = {"respawnitem", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; static CV_PossibleValue_t flagtime_cons_t[] = {{0, "MIN"}, {300, "MAX"}, {0, NULL}}; consvar_t cv_flagtime = {"flagtime", "30", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, flagtime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_suddendeath = {"suddendeath", "Off", CV_NETVAR|CV_CHEAT|CV_NOSHOWHELP, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -11650,10 +11647,6 @@ void P_RespawnSpecials(void) time = (time * 3) / max(1, mapheaderinfo[gamemap-1]->numlaps); } - // only respawn items when cv_itemrespawn is on - //if (!cv_itemrespawn.value) // TODO: remove this cvar - //return; - // nothing left to respawn? if (iquehead == iquetail) return; From 5c8275f905ac47e86aa7f4d214f3cbeca12a6cb7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 13 May 2020 05:21:35 -0400 Subject: [PATCH 24/65] Don't reset score when retrying --- src/g_game.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index a5e31994b..3ea0c8f0f 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2366,7 +2366,7 @@ void G_Ticker(boolean run) } } - D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), true, 1, false, false); + D_MapChange(gamemap, gametype, (cv_kartencore.value == 1), false, 1, false, false); } for (i = 0; i < MAXPLAYERS; i++) @@ -4633,12 +4633,8 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool if (resetplayer && !(multiplayer && demo.playback)) // SRB2Kart { + players[i].lives = 3; players[i].score = 0; - - if (grandprixinfo.roundnum == 0 || grandprixinfo.initalize == true) - { - players[i].lives = 3; - } } } From 62cc86c78807414761d06f83f90a5e1e631fa0ab Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 13 May 2020 21:29:46 -0400 Subject: [PATCH 25/65] Eliminate the last place bot properly --- src/p_inter.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/p_inter.c b/src/p_inter.c index a4845e8f5..43dc16aa8 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1977,7 +1977,8 @@ boolean P_CheckRacers(void) UINT8 numplayersingame = 0; UINT8 numexiting = 0; boolean eliminatelast = cv_karteliminatelast.value; - boolean canexit = true; + boolean everyonedone = true; + boolean eliminatebots = false; boolean griefed = false; // Check if all the players in the race have finished. If so, end the level. @@ -2005,14 +2006,19 @@ boolean P_CheckRacers(void) if (players[i].bot) { // Isn't a human, thus doesn't matter. (Sorry, robots.) + // Set this so that we can check for bots that need to get eliminated, though! + eliminatebots = true; continue; } - canexit = false; + everyonedone = false; } } - if (canexit) + // If we returned here with bots left, then the last place bot may have a chance to finish the map and NOT get time over. + // Not that it affects anything, they didn't make the map take longer or even get any points from it. But... it's a bit inconsistent! + // So if there's any bots, we'll let the game skip this, continue onto calculating eliminatelast, THEN we return true anyway. + if (everyonedone && !eliminatebots) { // Everyone's finished, we're done here! racecountdown = exitcountdown = 0; @@ -2065,6 +2071,13 @@ boolean P_CheckRacers(void) return true; } + if (everyonedone) + { + // See above: there might be bots that are still going, but all players are done, so we can exit now. + racecountdown = exitcountdown = 0; + return true; + } + // SO, we're not done playing. // Let's see if it's time to start the death counter! From 48322a8dd2b551e04e48930857d2662c992445b8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 13 May 2020 22:17:38 -0400 Subject: [PATCH 26/65] Adjust faked results with gamespeed and scale --- src/k_grandprix.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 77bdf723c..a408e4198 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -188,7 +188,7 @@ void K_InitGrandPrixBots(void) void K_FakeBotResults(player_t *bot) { - const UINT32 distfactor = 32; + const UINT32 distfactor = FixedMul(32 * bot->mo->scale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; UINT32 worstdist = 0; tic_t besttime = UINT32_MAX; UINT8 numplayers = 0; @@ -234,7 +234,7 @@ void K_FakeBotResults(player_t *bot) // hey, you "won" bot->exiting = 2; - bot->realtime = bot->realtime + (bot->distancetofinish / distfactor); + bot->realtime += (bot->distancetofinish / distfactor); bot->distancetofinish = 0; } From 6c8d67ab862344e2ecc8007dec5dc30e959ceb7f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 13 May 2020 23:15:57 -0400 Subject: [PATCH 27/65] Give bonus points to 1st, 2nd, and 3rd place. --- src/k_grandprix.c | 66 +++++++++++++++++++++++++++++++++++++++++++++-- src/k_grandprix.h | 2 ++ src/y_inter.c | 8 ++++-- 3 files changed, 72 insertions(+), 4 deletions(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index a408e4198..33ea7926e 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -21,13 +21,75 @@ struct grandprixinfo grandprixinfo; +UINT8 K_BotStartingDifficulty(SINT8 value) +{ + // startingdifficulty: Easy = 3, Normal = 6, Hard = 9 + SINT8 difficulty = (value + 1) * 3; + + if (difficulty > MAXBOTDIFFICULTY) + { + difficulty = MAXBOTDIFFICULTY; + } + else if (difficulty < 1) + { + difficulty = 1; + } + + return difficulty; +} + +INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers) +{ + INT16 points; + + if (position >= numplayers || position == 0) + { + // Invalid position, no points + return 0; + } + + points = numplayers - position; + + // Give bonus to high-ranking players, depending on player count + // This rounds out the point gain when you get 1st every race, + // and gives bots able to catch up in points if a player gets an early lead. + // The maximum points you can get in a cup is: ((number of players - 1) + (max extra points)) * (number of races) + // 8P: (7 + 3) * 5 = 50 maximum points + // 12P: (11 + 3) * 5 = 70 maximum points + // 16P: (15 + 3) * 5 = 90 maximum points + switch (numplayers) + { + case 0: case 1: case 2: // 1v1 + break; // No bonus needed. + case 3: case 4: // 3-4P + if (position == 1) { points += 1; } // 1st gets +1 extra point + break; + case 5: case 6: + if (position == 1) { points += 2; } // 1st gets +2 extra points + else if (position == 2) { points += 1; } // 2nd gets +1 extra point + break; + default: // Normal matches + if (position == 1) { points += 3; } // 1st gets +3 extra points + else if (position == 2) { points += 2; } // 2nd gets +2 extra points + else if (position == 3) { points += 1; } // 3rd gets +1 extra point + break; + } + + // somehow underflowed? + if (points < 0) + { + points = 0; + } + + return points; +} + void K_InitGrandPrixBots(void) { const char *defaultbotskinname = "eggrobo"; SINT8 defaultbotskin = R_SkinAvailable(defaultbotskinname); - // startingdifficulty: Easy = 3, Normal = 6, Hard = 9 - const UINT8 startingdifficulty = min(MAXBOTDIFFICULTY, (gamespeed + 1) * 3); + const UINT8 startingdifficulty = K_BotStartingDifficulty(grandprixinfo.gamespeed); UINT8 difficultylevels[MAXPLAYERS]; UINT8 playercount = 8; diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 2127a5eb6..28895c2dc 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -15,6 +15,8 @@ extern struct grandprixinfo boolean wonround; ///< If false, then we retry the map instead of going to the next. } grandprixinfo; +UINT8 K_BotStartingDifficulty(SINT8 value); +INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers); void K_InitGrandPrixBots(void); void K_FakeBotResults(player_t *bot); void K_PlayerLoseLife(player_t *player); diff --git a/src/y_inter.c b/src/y_inter.c index b0a24acbd..52ff82dee 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -308,17 +308,21 @@ static void Y_CalculateMatchData(UINT8 rankingsmode, void (*comparison)(INT32)) data.match.name[data.match.numplayers] = player_names[i]; if (data.match.numplayers && (data.match.val[data.match.numplayers] == data.match.val[data.match.numplayers-1])) + { data.match.pos[data.match.numplayers] = data.match.pos[data.match.numplayers-1]; + } else + { data.match.pos[data.match.numplayers] = data.match.numplayers+1; + } if ((powertype == PWRLV_DISABLED) && (!rankingsmode) && !(players[i].pflags & PF_TIMEOVER) && (data.match.pos[data.match.numplayers] < (numplayersingame + numgriefers))) { - // Single player rankings (grand prix). Online rank is handled below. - data.match.increase[i] = (numplayersingame + numgriefers) - data.match.pos[data.match.numplayers]; + // Online rank is handled further below in this file. + data.match.increase[i] = K_CalculateGPRankPoints(data.match.pos[data.match.numplayers], numplayersingame + numgriefers); players[i].score += data.match.increase[i]; } From 47c2c875d570626c15a49d9b4ab4f99802f42f1f Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 14 May 2020 02:18:32 -0400 Subject: [PATCH 28/65] Increase bot difficulty every match based on placement, implement rival tag Rival tag does not have an in-game indicator, but it is there & it gives the bot better odds --- src/d_clisrv.c | 4 ++ src/d_clisrv.h | 2 + src/d_player.h | 10 +-- src/g_game.c | 6 ++ src/k_grandprix.c | 174 ++++++++++++++++++++++++++++++++++++++++++++++ src/k_grandprix.h | 2 + src/k_kart.c | 30 ++++++-- src/p_saveg.c | 4 ++ src/p_setup.c | 4 ++ src/p_user.c | 14 +++- 10 files changed, 236 insertions(+), 14 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 5d7c0da83..6097d5ec2 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -650,6 +650,8 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i) rsp->bot = players[i].bot; rsp->bot_difficulty = players[i].botvars.difficulty; + rsp->bot_diffincrease = players[i].botvars.diffincrease; + rsp->bot_rival = players[i].botvars.rival; rsp->bot_itemdelay = players[i].botvars.itemdelay; rsp->bot_itemconfirm = players[i].botvars.itemconfirm; rsp->bot_lastturn = players[i].botvars.lastturn; @@ -780,6 +782,8 @@ static void resynch_read_player(resynch_pak *rsp) players[i].bot = rsp->bot; players[i].botvars.difficulty = rsp->bot_difficulty; + players[i].botvars.diffincrease = rsp->bot_diffincrease; + players[i].botvars.rival = rsp->bot_rival; players[i].botvars.itemdelay = rsp->bot_itemdelay; players[i].botvars.itemconfirm = rsp->bot_itemconfirm; players[i].botvars.lastturn = rsp->bot_lastturn; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 027ad42dd..349951ff6 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -288,6 +288,8 @@ typedef struct boolean bot; UINT8 bot_difficulty; + UINT8 bot_diffincrease; + boolean bot_rival; tic_t bot_itemdelay; tic_t bot_itemconfirm; INT16 bot_lastturn; diff --git a/src/d_player.h b/src/d_player.h index d3af08ede..eb660213c 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -423,12 +423,14 @@ typedef enum // player_t struct for all bot variables typedef struct botvars_s { - UINT8 difficulty; + UINT8 difficulty; // Bot's difficulty setting + UINT8 diffincrease; // In GP: bot difficulty will increase this much next round + boolean rival; // If true, they're the GP rival - tic_t itemdelay; - tic_t itemconfirm; + tic_t itemdelay; // Delay before using item at all + tic_t itemconfirm; // When high enough, they will use their item - INT16 lastturn; + INT16 lastturn; // Last turn direction } botvars_t; // ======================================================================== diff --git a/src/g_game.c b/src/g_game.c index 3ea0c8f0f..2dd31a379 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2598,6 +2598,8 @@ void G_PlayerReborn(INT32 player) boolean spectator; boolean bot; UINT8 botdifficulty; + UINT8 botdiffincrease; + boolean botrival; SINT8 pity; // SRB2kart @@ -2652,6 +2654,8 @@ void G_PlayerReborn(INT32 player) mare = players[player].mare; bot = players[player].bot; botdifficulty = players[player].botvars.difficulty; + botdiffincrease = players[player].botvars.diffincrease; + botrival = players[player].botvars.rival; pity = players[player].pity; // SRB2kart @@ -2733,6 +2737,8 @@ void G_PlayerReborn(INT32 player) p->mare = mare; p->bot = bot; p->botvars.difficulty = botdifficulty; + p->botvars.diffincrease = botdiffincrease; + p->botvars.rival = botrival; p->pity = pity; // SRB2kart diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 33ea7926e..e82f34560 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -248,6 +248,179 @@ void K_InitGrandPrixBots(void) } } +static INT16 K_RivalScore(player_t *bot) +{ + const UINT16 difficulty = bot->botvars.difficulty; + const UINT16 score = bot->score; + const SINT8 roundsleft = grandprixinfo.cup->numlevels - grandprixinfo.roundnum; + + // In the early game, difficulty is more important for long-term challenge. + // When we're running low on matches left though, we need to focus more on score. + + return (difficulty * roundsleft) + (score * grandprixinfo.roundnum); +} + +void K_UpdateGrandPrixBots(void) +{ + player_t *oldrival = NULL; + player_t *newrival = NULL; + UINT16 newrivalscore = 0; + UINT8 i; + + // Find the rival. + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator || !players[i].bot) + { + continue; + } + + if (players[i].botvars.diffincrease) + { + players[i].botvars.difficulty += players[i].botvars.diffincrease; + + if (players[i].botvars.difficulty > MAXBOTDIFFICULTY) + { + players[i].botvars.difficulty = MAXBOTDIFFICULTY; + } + + players[i].botvars.diffincrease = 0; + } + + if (players[i].botvars.rival) + { + if (oldrival == NULL) + { + // Record the old rival to compare with our calculated new rival + oldrival = &players[i]; + } + else + { + // Somehow 2 rivals were set?! + // Let's quietly fix our mess... + players[i].botvars.rival = false; + } + } + } + + // Find the bot with the best average of score & difficulty. + for (i = 0; i < MAXPLAYERS; i++) + { + UINT16 ns = 0; + + if (!playeringame[i] || players[i].spectator || !players[i].bot) + { + continue; + } + + ns = K_RivalScore(&players[i]); + + if (ns > newrivalscore) + { + newrival = &players[i]; + newrivalscore = ns; + } + } + + // Even if there's a new rival, we want to make sure that they're a better fit than the current one! + if (oldrival != newrival) + { + if (oldrival != NULL) + { + UINT16 os = K_RivalScore(oldrival); + + if (newrivalscore <= os + 100) + { + // Our current rival's just fine, thank you very much. + return; + } + + // Hand over your badge. + oldrival->botvars.rival = false; + } + + // Set our new rival! + newrival->botvars.rival = true; + CONS_Printf("Rival is now %s\n", player_names[newrival - players]); + } +} + +static UINT8 K_BotExpectedStanding(player_t *bot) +{ + UINT8 pos = 1; + UINT8 i; + + for (i = 0; i < MAXPLAYERS; i++) + { + if (i == (bot - players)) + { + continue; + } + + if (playeringame[i] && !players[i].spectator) + { + if (players[i].bot) + { + if (players[i].botvars.difficulty > bot->botvars.difficulty) + { + pos++; + } + else if (players[i].score > bot->score) + { + pos++; + } + } + else + { + // Human player, always increment + pos++; + } + } + } + + return pos; +} + +void K_IncreaseBotDifficulty(player_t *bot) +{ + UINT8 expectedstanding; + INT16 standingdiff; + + if (bot->botvars.difficulty >= MAXBOTDIFFICULTY) + { + // Already at max difficulty, don't need to increase + return; + } + + // Increment bot difficulty based on what position you were meant to come in! + expectedstanding = K_BotExpectedStanding(bot); + standingdiff = expectedstanding - bot->kartstuff[k_position]; + + if (standingdiff >= -2) + { + UINT8 increase; + + if (standingdiff > 5) + { + increase = 3; + } + else if (standingdiff > 2) + { + increase = 2; + } + else + { + increase = 1; + } + + bot->botvars.diffincrease = increase; + } + else + { + bot->botvars.diffincrease = 0; + } +} + void K_FakeBotResults(player_t *bot) { const UINT32 distfactor = FixedMul(32 * bot->mo->scale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; @@ -298,6 +471,7 @@ void K_FakeBotResults(player_t *bot) bot->exiting = 2; bot->realtime += (bot->distancetofinish / distfactor); bot->distancetofinish = 0; + K_IncreaseBotDifficulty(bot); } void K_PlayerLoseLife(player_t *player) diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 28895c2dc..7368b96f7 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -18,6 +18,8 @@ extern struct grandprixinfo UINT8 K_BotStartingDifficulty(SINT8 value); INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers); void K_InitGrandPrixBots(void); +void K_UpdateGrandPrixBots(void); +void K_IncreaseBotDifficulty(player_t *bot); void K_FakeBotResults(player_t *bot); void K_PlayerLoseLife(player_t *player); diff --git a/src/k_kart.c b/src/k_kart.c index cf984252c..00dad9549 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -854,7 +854,7 @@ static void K_KartGetItemResult(player_t *player, SINT8 getitem) \return void */ -static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot) +static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean spbrush, boolean bot, boolean rival) { INT32 newodds; INT32 i; @@ -932,10 +932,12 @@ static INT32 K_KartGetItemOdds(UINT8 pos, SINT8 item, fixed_t mashed, boolean sp #define POWERITEMODDS(odds) {\ if (franticitems) \ - odds <<= 1; \ - odds = FixedMul(odds<> FRACBITS; \ + odds *= 2; \ + if (rival) \ + odds *= 2; \ + odds = FixedMul(odds * FRACUNIT, FRACUNIT + ((PLAYERSCALING * FRACUNIT) / 25)) / FRACUNIT; \ if (mashed > 0) \ - odds = FixedDiv(odds<> FRACBITS; \ + odds = FixedDiv(odds * FRACUNIT, FRACUNIT + mashed) / FRACUNIT; \ } #define COOLDOWNONSTART (leveltime < (30*TICRATE)+starttime) @@ -1071,7 +1073,7 @@ static UINT8 K_FindUseodds(player_t *player, fixed_t mashed, UINT32 pdis, UINT8 for (j = 1; j < NUMKARTRESULTS; j++) { - if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot) > 0) + if (K_KartGetItemOdds(i, j, mashed, spbrush, player->bot, (player->bot && player->botvars.rival)) > 0) { available = true; break; @@ -1233,7 +1235,9 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) pdis = FixedDiv(pdis * FRACUNIT, mapobjectscale) / FRACUNIT; if (franticitems) // Frantic items make the distances between everyone artifically higher, for crazier items + { pdis = (15 * pdis) / 14; + } if (spbplace != -1 && player->kartstuff[k_position] == spbplace+1) // SPB Rush Mode: It's 2nd place's job to catch-up items and make 1st place's job hell { @@ -1241,6 +1245,12 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) spbrush = true; } + if (player->bot && player->botvars.rival) + { + // Rival has better odds :) + pdis = (15 * pdis) / 14; + } + pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count // SPECIAL CASE No. 1: @@ -1369,7 +1379,7 @@ static void K_KartItemRoulette(player_t *player, ticcmd_t *cmd) useodds = K_FindUseodds(player, mashed, pdis, bestbumper, spbrush); for (i = 1; i < NUMKARTRESULTS; i++) - spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot)); + spawnchance[i] = (totalspawnchance += K_KartGetItemOdds(useodds, i, mashed, spbrush, player->bot, (player->bot && player->botvars.rival))); // Award the player whatever power is rolled if (totalspawnchance > 0) @@ -11277,13 +11287,19 @@ static void K_drawDistributionDebugger(void) spbrush = true; } + if (stplyr->bot && stplyr->botvars.rival) + { + // Rival has better odds :) + pdis = (15 * pdis) / 14; + } + pdis = ((28 + (8-pingame)) * pdis) / 28; // scale with player count useodds = K_FindUseodds(stplyr, 0, pdis, bestbumper, spbrush); for (i = 1; i < NUMKARTRESULTS; i++) { - const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot); + const INT32 itemodds = K_KartGetItemOdds(useodds, i, 0, spbrush, stplyr->bot, (stplyr->bot && stplyr->botvars.rival)); if (itemodds <= 0) continue; diff --git a/src/p_saveg.c b/src/p_saveg.c index 6f8b5cb79..354a96b1c 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -271,6 +271,8 @@ static void P_NetArchivePlayers(void) WRITEUINT32(save_p, K_GetWaypointHeapIndex(players[i].nextwaypoint)); WRITEUINT8(save_p, players[i].botvars.difficulty); + WRITEUINT8(save_p, players[i].botvars.diffincrease); + WRITEUINT8(save_p, players[i].botvars.rival); WRITEUINT32(save_p, players[i].botvars.itemdelay); WRITEUINT32(save_p, players[i].botvars.itemconfirm); WRITEINT16(save_p, players[i].botvars.lastturn); @@ -448,6 +450,8 @@ static void P_NetUnArchivePlayers(void) players[i].nextwaypoint = (waypoint_t *)(size_t)READUINT32(save_p); players[i].botvars.difficulty = READUINT8(save_p); + players[i].botvars.diffincrease = READUINT8(save_p); + players[i].botvars.rival = (boolean)READUINT8(save_p); players[i].botvars.itemdelay = READUINT32(save_p); players[i].botvars.itemconfirm = READUINT32(save_p); players[i].botvars.lastturn = READINT16(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 5228f7ed8..b2d14b456 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3375,6 +3375,10 @@ boolean P_SetupLevel(boolean skipprecip) K_InitGrandPrixBots(); grandprixinfo.initalize = false; } + else + { + K_UpdateGrandPrixBots(); + } } else if (!modeattacking) { diff --git a/src/p_user.c b/src/p_user.c index 34fa8642f..733df81b1 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1735,10 +1735,18 @@ void P_DoPlayerExit(player_t *player) else player->exiting = raceexittime+2; // Accidental death safeguard??? - if (grandprixinfo.roundnum > 0 && !losing && !player->bot) + if (grandprixinfo.roundnum > 0) { - // YOU WIN - grandprixinfo.wonround = true; + if (player->bot) + { + // Bots are going to get harder... :) + K_IncreaseBotDifficulty(player); + } + else if (!losing) + { + // YOU WIN + grandprixinfo.wonround = true; + } } player->powers[pw_underwater] = 0; From 7bdb1e945879c114580acc7f2500baddcbd645eb Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 14 May 2020 02:23:03 -0400 Subject: [PATCH 29/65] Rival always rubberbands to the front of the pack --- src/k_bot.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/k_bot.c b/src/k_bot.c index c4acf481e..f9173aabd 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -212,6 +212,12 @@ static UINT32 K_BotRubberbandDistance(player_t *player) UINT8 pos = 0; UINT8 i; + if (player->botvars.rival) + { + // The rival should always try to be the front runner for the race. + return 0; + } + for (i = 0; i < MAXPLAYERS; i++) { if (i == portpriority) From c2dd06e053f7157e1283571ced7ab21995bec659 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 3 Jun 2020 11:06:27 -0400 Subject: [PATCH 30/65] Add graphic for netplay bots --- src/k_kart.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index 9b1119341..8624e9486 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8032,6 +8032,8 @@ static patch_t *kp_itemminimap; static patch_t *kp_alagles[10]; static patch_t *kp_blagles[6]; +static patch_t *kp_cpu; + void K_LoadKartHUDGraphics(void) { INT32 i, j; @@ -8389,6 +8391,8 @@ void K_LoadKartHUDGraphics(void) buffer[7] = '0'+i; kp_blagles[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); } + + kp_cpu = (patch_t *) W_CachePatchName("K_CPU", PU_HUDGFX); } // For the item toggle menu @@ -9433,7 +9437,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I { if (players[tab[i].num].bot) { - ; // TODO: Put a graphic here to indicate this player is a bot! + V_DrawScaledPatch(x + ((i < 8) ? -25 : rightoffset + 3), y-2, 0, kp_cpu); } else if (tab[i].num != serverplayer || !server_lagless) { From 30df376d04d43fb3b0e85823eca9c90e2a31b1c7 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 3 Jun 2020 14:02:14 -0400 Subject: [PATCH 31/65] Higher friction while rubberbanding They are now x2 as difficult :) --- src/k_bot.c | 19 +++++++++++++++++++ src/k_bot.h | 17 +++++++++++++++++ src/k_kart.c | 24 +++++++++++++++++++----- 3 files changed, 55 insertions(+), 5 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 84bc8b4ee..38b9bff5a 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -453,6 +453,25 @@ fixed_t K_BotTopSpeedRubberband(player_t *player) return rubberband; } +/*-------------------------------------------------- + fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) + + See header file for description. +--------------------------------------------------*/ +fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict) +{ + fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; + + if (rubberband <= 0) + { + // Never get stronger than normal friction + return frict; + } + + // 128 is a magic number that felt good in-game + return FixedDiv(frict, FRACUNIT + (rubberband / 2)); +} + /*-------------------------------------------------- fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy) diff --git a/src/k_bot.h b/src/k_bot.h index 87d5fc716..d6d7f8338 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -95,6 +95,23 @@ fixed_t K_BotRubberband(player_t *player); fixed_t K_BotTopSpeedRubberband(player_t *player); +/*-------------------------------------------------- + fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict); + + Gives a multiplier for a bot's rubberbanding. + Adjusted from K_BotRubberband to be used for friction. + + Input Arguments:- + player - Player to check. + frict - Friction value to adjust. + + Return:- + The new friction value. +--------------------------------------------------*/ + +fixed_t K_BotFrictionRubberband(player_t *player, fixed_t frict); + + /*-------------------------------------------------- fixed_t K_DistanceOfLineFromPoint(fixed_t v1x, fixed_t v1y, fixed_t v2x, fixed_t v2y, fixed_t cx, fixed_t cy); diff --git a/src/k_kart.c b/src/k_kart.c index 8624e9486..7a7ae79da 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2412,19 +2412,25 @@ UINT16 K_GetKartFlashing(player_t *player) fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove) { - fixed_t accelmax = 4000; + const fixed_t accelmax = 4000; + const fixed_t p_speed = K_GetKartSpeed(player, true); + const fixed_t p_accel = K_GetKartAccel(player); fixed_t newspeed, oldspeed, finalspeed; - fixed_t p_speed = K_GetKartSpeed(player, true); - fixed_t p_accel = K_GetKartAccel(player); + fixed_t orig = ORIG_FRICTION; if (!onground) return 0; // If the player isn't on the ground, there is no change in speed + if (K_PlayerUsesBotMovement(player)) + { + orig = K_BotFrictionRubberband(player, ORIG_FRICTION); + } + // ACCELCODE!!!1!11! oldspeed = R_PointToDist2(0, 0, player->rmomx, player->rmomy); // FixedMul(P_AproxDistance(player->rmomx, player->rmomy), player->mo->scale); // Don't calculate the acceleration as ever being above top speed if (oldspeed > p_speed) oldspeed = p_speed; - newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), ORIG_FRICTION); + newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) + FixedMul(p_speed, p_accel), accelmax), orig); if (player->kartstuff[k_pogospring]) // Pogo Spring minimum/maximum thrust { @@ -7683,7 +7689,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground) player->mo->friction += 4608; } - if (player->speed > 0 && cmd->forwardmove < 0) // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel + // change friction while braking no matter what, otherwise it's not any more effective than just letting go off accel + if (player->speed > 0 && cmd->forwardmove < 0) player->mo->friction -= 2048; // Karma ice physics @@ -7720,6 +7727,13 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (player->mo->movefactor < 32) player->mo->movefactor = 32; } + + // Don't go too far above your top speed when rubberbanding + // Down here, because we do NOT want to modify movefactor + if (K_PlayerUsesBotMovement(player)) + { + player->mo->friction = K_BotFrictionRubberband(player, player->mo->friction); + } } K_KartDrift(player, P_IsObjectOnGround(player->mo)); // Not using onground, since we don't want this affected by spring pads From fac8f7f5388540fe34ebebca5bbaa4b9b2d292a0 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 3 Jun 2020 16:34:23 -0400 Subject: [PATCH 32/65] You get 1ups for carrying rings to the goal --- src/g_game.c | 2 ++ src/p_setup.c | 4 +++- src/p_user.c | 20 +++++++++++++++++--- 3 files changed, 22 insertions(+), 4 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index bd9ebfcf6..2716dcd3d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4642,6 +4642,8 @@ void G_InitNew(UINT8 pencoremode, const char *mapname, boolean resetplayer, bool if (resetplayer && !(multiplayer && demo.playback)) // SRB2Kart { players[i].lives = 3; + players[i].xtralife = 0; + players[i].totalring = 0; players[i].score = 0; } } diff --git a/src/p_setup.c b/src/p_setup.c index 2f73f38cb..b975e1238 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2421,6 +2421,8 @@ static void P_LevelInitStuff(void) if (grandprixinfo.roundnum == 0) { players[i].lives = 3; + players[i].xtralife = 0; + players[i].totalring = 0; } players[i].realtime = racecountdown = exitcountdown = 0; @@ -2429,7 +2431,7 @@ static void P_LevelInitStuff(void) players[i].lostlife = false; players[i].gotcontinue = false; - players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0; + players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0; players[i].health = 1; players[i].aiming = 0; players[i].pflags &= ~PF_TIMEOVER; diff --git a/src/p_user.c b/src/p_user.c index 7ff53aaed..259d156a8 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -949,7 +949,6 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) return; player->kartstuff[k_rings] += num_rings; - //player->totalring += num_rings; // Used for GP lives later if (player->kartstuff[k_rings] > 20) player->kartstuff[k_rings] = 20; // Caps at 20 rings, sorry! @@ -967,8 +966,8 @@ void P_GivePlayerLives(player_t *player, INT32 numlives) { player->lives += numlives; - if (player->lives > 99) - player->lives = 99; + if (player->lives > 9) + player->lives = 9; else if (player->lives < 1) player->lives = 1; } @@ -1748,8 +1747,23 @@ void P_DoPlayerExit(player_t *player) } else if (!losing) { + const UINT8 lifethreshold = 20; + UINT8 extra = 0; + // YOU WIN grandprixinfo.wonround = true; + + // Increase your total rings + player->totalring += RINGTOTAL(player); + + extra = player->totalring / lifethreshold; + + if (extra > player->xtralife) + { + P_GivePlayerLives(player, extra - player->xtralife); + S_StartSound(NULL, sfx_cdfm73); + player->xtralife = extra; + } } } From 3ca0e4a48b27cf427e06f14ebf328c4dd9cafe82 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 3 Jun 2020 19:49:21 -0400 Subject: [PATCH 33/65] Proper object HUD tracking -- apply to CHECK and Rival tag --- src/k_kart.c | 361 ++++++++++++++++++++++++++++++++++++++++++-------- src/lua_hud.h | 1 + 2 files changed, 304 insertions(+), 58 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 7a7ae79da..dae1deb28 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8024,6 +8024,8 @@ static patch_t *kp_sadface[2]; static patch_t *kp_check[6]; +static patch_t *kp_rival[2]; + static patch_t *kp_eggnum[4]; static patch_t *kp_flameshieldmeter[104][2]; @@ -8318,6 +8320,14 @@ void K_LoadKartHUDGraphics(void) kp_check[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); } + // Rival indicators + sprintf(buffer, "K_RIVALx"); + for (i = 0; i < 2; i++) + { + buffer[7] = '1'+i; + kp_rival[i] = (patch_t *) W_CachePatchName(buffer, PU_HUDGFX); + } + // Eggman warning numbers sprintf(buffer, "K_EGGNx"); for (i = 0; i < 4; i++) @@ -9904,34 +9914,6 @@ static void K_drawKartBumpersOrKarma(void) } } -static fixed_t K_FindCheckX(fixed_t px, fixed_t py, angle_t ang, fixed_t mx, fixed_t my) -{ - fixed_t dist, x; - fixed_t range = RING_DIST/3; - angle_t diff; - - range *= gamespeed+1; - - dist = abs(R_PointToDist2(px, py, mx, my)); - if (dist > range) - return -320; - - diff = R_PointToAngle2(px, py, mx, my) - ang; - - if (diff < ANGLE_90 || diff > ANGLE_270) - return -320; - else - x = (FixedMul(FINETANGENT(((diff+ANGLE_90)>>ANGLETOFINESHIFT) & 4095), 160<>FRACBITS; - - if (encoremode) - x = 320-x; - - if (r_splitscreen > 1) - x /= 2; - - return x; -} - static void K_drawKartWanted(void) { UINT8 i, numwanted = 0; @@ -10004,52 +9986,309 @@ static void K_drawKartWanted(void) } } +static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, angle_t camang, angle_t camaim, UINT8 camnum, vertex_t *point) +{ + const INT32 swhalf = (BASEVIDWIDTH / 2); + const fixed_t swhalffixed = swhalf * FRACUNIT; + + const INT32 shhalf = (BASEVIDHEIGHT / 2); + const fixed_t shhalffixed = shhalf * FRACUNIT; + + INT32 anglediff = (signed)(camang - R_PointToAngle2(campos->x, campos->y, point->x, point->y)); + fixed_t distance = R_PointToDist2(campos->x, campos->y, point->x, point->y); + fixed_t factor = INT32_MAX; + + if (abs(anglediff) > ANGLE_90) + { + if (hud_x != NULL) + { + *hud_x = -BASEVIDWIDTH * FRACUNIT; + } + + if (hud_y != NULL) + { + *hud_y = -BASEVIDWIDTH * FRACUNIT; + } + + //*hud_scale = FRACUNIT; + return; + } + + factor = max(1, FINECOSINE(anglediff >> ANGLETOFINESHIFT)); + +#define NEWTAN(n) FINETANGENT(((n + ANGLE_90) >> ANGLETOFINESHIFT) & 4095) + + if (hud_x != NULL) + { + *hud_x = FixedMul(NEWTAN(anglediff), swhalffixed) + swhalffixed; + + if (r_splitscreen >= 2) + { + *hud_x /= 2; + + if (camnum & 1) + { + *hud_x += swhalffixed; + } + } + } + + if (hud_y != NULL) + { + *hud_y = campos->z - point->z; + *hud_y = FixedDiv(*hud_y, FixedMul(factor, distance)); + *hud_y = (*hud_y * swhalf) + shhalffixed; + *hud_y = *hud_y + NEWTAN(camaim) * swhalf; + + if (r_splitscreen >= 1) + { + *hud_y /= 2; + + if (camnum > 1) + { + *hud_y += shhalffixed; + } + } + } + + //*hud_scale = FixedDiv(swhalffixed, FixedMul(factor, distance)); + +#undef NEWTAN +} + static void K_drawKartPlayerCheck(void) { - INT32 i; - UINT8 *colormap; - INT32 x; - + const fixed_t maxdistance = FixedMul(1280 * mapobjectscale, K_GetKartGameSpeedScalar(gamespeed)); + camera_t *thiscam; + vertex_t c; + UINT8 cnum = 0; + UINT8 i; INT32 splitflags = K_calcSplitFlags(V_SNAPTOBOTTOM); - if (!stplyr->mo || stplyr->spectator) + if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) + { return; + } - if (stplyr->awayviewtics) + if (stplyr->spectator || stplyr->awayviewtics) + { return; + } - if (( stplyr->cmd.buttons & BT_LOOKBACK )) + if (stplyr->cmd.buttons & BT_LOOKBACK) + { return; + } + + if (r_splitscreen) + { + for (i = 1; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]]) + { + cnum = i; + break; + } + } + } + + thiscam = &camera[cnum]; + + c.x = thiscam->x; + c.y = thiscam->y; + c.z = thiscam->z; for (i = 0; i < MAXPLAYERS; i++) { + player_t *checkplayer = &players[i]; + fixed_t distance = maxdistance+1; UINT8 pnum = 0; + fixed_t x = 0; + vertex_t v; - if (&players[i] == stplyr) - continue; - if (!playeringame[i] || players[i].spectator) - continue; - if (!players[i].mo) - continue; - - if ((players[i].kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2)) - pnum++; // white frames - - if (players[i].kartstuff[k_itemtype] == KITEM_GROW || players[i].kartstuff[k_growshrinktimer] > 0) - pnum += 4; - else if (players[i].kartstuff[k_itemtype] == KITEM_INVINCIBILITY || players[i].kartstuff[k_invincibilitytimer]) - pnum += 2; - - x = K_FindCheckX(stplyr->mo->x, stplyr->mo->y, stplyr->mo->angle, players[i].mo->x, players[i].mo->y); - if (x <= 320 && x >= 0) + if (!playeringame[i] || checkplayer->spectator) { - if (x < 14) - x = 14; - else if (x > 306) - x = 306; + // Not in-game + continue; + } - colormap = R_GetTranslationColormap(TC_DEFAULT, players[i].mo->color, GTC_CACHE); - V_DrawMappedPatch(x, CHEK_Y, V_HUDTRANS|splitflags, kp_check[pnum], colormap); + if (checkplayer->mo == NULL || P_MobjWasRemoved(checkplayer->mo)) + { + // No object + continue; + } + + if (checkplayer == stplyr) + { + // This is you! + continue; + } + + v.x = checkplayer->mo->x; + v.y = checkplayer->mo->y; + v.z = checkplayer->mo->z; + + distance = R_PointToDist2(c.x, c.y, v.x, v.y); + + if (distance > maxdistance) + { + // Too far away + continue; + } + + if ((checkplayer->kartstuff[k_invincibilitytimer] <= 0) && (leveltime & 2)) + { + pnum++; // white frames + } + + if (checkplayer->kartstuff[k_itemtype] == KITEM_GROW || checkplayer->kartstuff[k_growshrinktimer] > 0) + { + pnum += 4; + } + else if (checkplayer->kartstuff[k_itemtype] == KITEM_INVINCIBILITY || checkplayer->kartstuff[k_invincibilitytimer]) + { + pnum += 2; + } + + K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, cnum, &v); + + if (x <= 320*FRACUNIT && x >= 0) + { + UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE); + + if (x < 14*FRACUNIT) + { + x = 14*FRACUNIT; + } + else if (x > 306*FRACUNIT) + { + x = 306*FRACUNIT; + } + + V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|splitflags, kp_check[pnum], colormap); + } + } +} + +static void K_drawKartNameTags(void) +{ + const fixed_t maxdistance = 4096*mapobjectscale; + camera_t *thiscam; + vertex_t c; + UINT8 cnum = 0; + UINT8 i; + + if (stplyr == NULL || stplyr->mo == NULL || P_MobjWasRemoved(stplyr->mo)) + { + return; + } + + if (stplyr->awayviewtics) + { + return; + } + + if (r_splitscreen) + { + for (i = 1; i <= r_splitscreen; i++) + { + if (stplyr == &players[displayplayers[i]]) + { + cnum = i; + break; + } + } + } + + thiscam = &camera[cnum]; + + c.x = thiscam->x; + c.y = thiscam->y; + c.z = thiscam->z; + + for (i = 0; i < MAXPLAYERS; i++) + { + player_t *ntplayer = &players[i]; + fixed_t distance = maxdistance+1; + + fixed_t x = -BASEVIDWIDTH * FRACUNIT; + fixed_t y = -BASEVIDWIDTH * FRACUNIT; + + vertex_t v; + UINT8 j; + + if (!playeringame[i] || ntplayer->spectator) + { + // Not in-game + continue; + } + + if (ntplayer->mo == NULL || P_MobjWasRemoved(ntplayer->mo)) + { + // No object + continue; + } + + if (!P_CheckSight(stplyr->mo, ntplayer->mo)) + { + // Can't see + continue; + } + + for (j = 0; j <= r_splitscreen; j++) + { + if (ntplayer == &players[displayplayers[j]]) + { + break; + } + } + + if (j < r_splitscreen) + { + // Is a player that's being shown on this computer + continue; + } + + v.x = ntplayer->mo->x; + v.y = ntplayer->mo->y; + v.z = ntplayer->mo->z; + + if (!(ntplayer->mo->eflags & MFE_VERTICALFLIP)) + { + v.z += ntplayer->mo->height; + } + + distance = R_PointToDist2(c.x, c.y, v.x, v.y); + + if (distance > maxdistance) + { + // Too far away + continue; + } + + K_ObjectTracking(&x, &y, &c, thiscam->angle, thiscam->aiming, cnum, &v); + + if (x == -BASEVIDWIDTH * FRACUNIT) + { + // Off-screen + continue; + } + + if (ntplayer->bot) + { + if (ntplayer->botvars.rival == true) + { + UINT8 blink = ((leveltime / 7) & 1); + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL); + } + } + else + { + if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-1) + && (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+1)) + { + ; // TODO: Draw a cool name tag for online + } } } } @@ -11063,6 +11302,12 @@ void K_drawKartHUD(void) if (cv_kartcheck.value && !splitscreen && !players[displayplayers[0]].exiting && !freecam) K_drawKartPlayerCheck(); + // nametags +#ifdef HAVE_BLUA + if (LUA_HudEnabled(hud_names)) +#endif + K_drawKartNameTags(); + // Draw WANTED status if (G_BattleGametype()) { diff --git a/src/lua_hud.h b/src/lua_hud.h index 960195cda..286700544 100644 --- a/src/lua_hud.h +++ b/src/lua_hud.h @@ -19,6 +19,7 @@ enum hud { hud_minimap, hud_item, hud_position, + hud_names, // online nametags hud_check, // "CHECK" f-zero indicator hud_minirankings, // Rankings to the left hud_battlebumpers, // mini rankings battle bumpers. From cdf4ad9f6b85befbd1dddacf4a84a0b483406c62 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 3 Jun 2020 20:03:17 -0400 Subject: [PATCH 34/65] Draw nametag stem (need to merge something in) --- src/k_kart.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index dae1deb28..7b4ac57f1 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -8050,6 +8050,8 @@ static patch_t *kp_blagles[6]; static patch_t *kp_cpu; +static patch_t *kp_nametagstem; + void K_LoadKartHUDGraphics(void) { INT32 i, j; @@ -8417,6 +8419,8 @@ void K_LoadKartHUDGraphics(void) } kp_cpu = (patch_t *) W_CachePatchName("K_CPU", PU_HUDGFX); + + kp_nametagstem = (patch_t *) W_CachePatchName("K_NAMEST", PU_HUDGFX); } // For the item toggle menu @@ -10287,7 +10291,9 @@ static void K_drawKartNameTags(void) if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-1) && (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+1)) { - ; // TODO: Draw a cool name tag for online + INT32 namelen = V_ThinStringWidth(str, V_6WIDTHSPACE); + + V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_nametagstem, NULL); } } } From 390191c85a648d0d53b96b5534d26f88650cfa56 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 3 Jun 2020 20:19:47 -0400 Subject: [PATCH 35/65] I swear to god please just push my entire merge --- src/k_kart.c | 32 ++++++++++++++++---------------- src/lua_hudlib.c | 1 + src/v_video.c | 1 + 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 2fe9075a1..a35405c6c 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9647,14 +9647,14 @@ static void K_drawKartLapsAndRings(void) ln[0] = ((stplyr->laps / 10) % 10); ln[1] = (stplyr->laps % 10); - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); ln[0] = ((abs(cv_numlaps.value) / 10) % 10); ln[1] = (abs(cv_numlaps.value) % 10); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); } else { @@ -9677,8 +9677,8 @@ static void K_drawKartLapsAndRings(void) if (stplyr->kartstuff[k_rings] < 0) // Draw the minus for ring debt V_DrawMappedPatch(fr+7, fy-10, V_HUDTRANS|splitflags, kp_ringdebtminussmall, ringmap); - V_DrawMappedPatch(fr+11, fy-10, V_HUDTRANS|splitflags, pingnum[rn[0]], ringmap); - V_DrawMappedPatch(fr+15, fy-10, V_HUDTRANS|splitflags, pingnum[rn[1]], ringmap); + V_DrawMappedPatch(fr+11, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[0]], ringmap); + V_DrawMappedPatch(fr+15, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[rn[1]], ringmap); // SPB ring lock if (stplyr->kartstuff[k_ringlock]) @@ -9689,7 +9689,7 @@ static void K_drawKartLapsAndRings(void) { UINT8 *colormap = R_GetTranslationColormap(stplyr->skin, stplyr->skincolor, GTC_CACHE); V_DrawMappedPatch(fr+21, fy-13, V_HUDTRANS|splitflags, facemmapprefix[stplyr->skin], colormap); - V_DrawScaledPatch(fr+34, fy-10, V_HUDTRANS|splitflags, pingnum[(stplyr->lives % 10)]); // make sure this doesn't overflow + V_DrawScaledPatch(fr+34, fy-10, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[(stplyr->lives % 10)]); // make sure this doesn't overflow } } else @@ -9835,14 +9835,14 @@ static void K_drawKartBumpersOrKarma(void) ln[0] = ((numtargets / 10) % 10); ln[1] = (numtargets % 10); - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); ln[0] = ((maptargets / 10) % 10); ln[1] = (maptargets % 10); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); } else { @@ -9869,14 +9869,14 @@ static void K_drawKartBumpersOrKarma(void) ln[0] = ((abs(stplyr->kartstuff[k_bumper]) / 10) % 10); ln[1] = (abs(stplyr->kartstuff[k_bumper]) % 10); - V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); - V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); + V_DrawScaledPatch(fx+13, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+17, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); ln[0] = ((abs(maxbumper) / 10) % 10); ln[1] = (abs(maxbumper) % 10); - V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, pingnum[ln[0]]); - V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, pingnum[ln[1]]); + V_DrawScaledPatch(fx+27, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[0]]); + V_DrawScaledPatch(fx+31, fy, V_HUDTRANS|splitflags, fontv[PINGNUM_FONT].font[ln[1]]); } else { @@ -10291,7 +10291,7 @@ static void K_drawKartNameTags(void) if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-1) && (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+1)) { - INT32 namelen = V_ThinStringWidth(str, V_6WIDTHSPACE); + //INT32 namelen = V_ThinStringWidth(str, V_6WIDTHSPACE); V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_nametagstem, NULL); } diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 1ba21532f..8410cd55f 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -22,6 +22,7 @@ #include "v_video.h" #include "w_wad.h" #include "z_zone.h" +#include "hu_stuff.h" #include "lua_script.h" #include "lua_libs.h" diff --git a/src/v_video.c b/src/v_video.c index 51e57c207..20931291f 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -1695,6 +1695,7 @@ static inline fixed_t FixedCharacterDim( INT32 dupx, fixed_t * cwp) { + (void)scale; (void)hchw; (void)dupx; (*cwp) = chw; From a3ec4f22a15deb659a18932de2e79770d303ee6a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Wed, 3 Jun 2020 23:29:43 -0400 Subject: [PATCH 36/65] Finished nametags --- src/hu_stuff.c | 163 ++++------------------------------------------ src/k_color.c | 171 +++++++++++++++++++++++++++++++++++++++++++++---- src/k_color.h | 15 +++++ src/k_kart.c | 44 +++++++++++-- src/v_video.h | 3 + 5 files changed, 225 insertions(+), 171 deletions(-) diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 5a550c2c1..1044dee19 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -54,6 +54,7 @@ #include "s_sound.h" // song credits #include "k_kart.h" +#include "k_color.h" // coords are scaled #define HU_INPUTX 0 @@ -739,168 +740,27 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) const char *prefix = "", *cstart = "", *cend = "", *adminchar = "\x82~\x83", *remotechar = "\x82@\x83", *fmt2, *textcolor = "\x80"; char *tempchar = NULL; - // player is a spectator? - if (players[playernum].spectator) + if (players[playernum].spectator) { - cstart = "\x86"; // grey name - textcolor = "\x86"; + // grey text + cstart = textcolor = "\x86"; } else if (target == -1) // say team { - if (players[playernum].ctfteam == 1) // red + if (players[playernum].ctfteam == 1) { - cstart = "\x85"; - textcolor = "\x85"; + // red text + cstart = textcolor = "\x85"; } - else // blue + else { - cstart = "\x84"; - textcolor = "\x84"; + // blue text + cstart = textcolor = "\x84"; } } else { - const UINT8 color = players[playernum].skincolor; - - cstart = "\x83"; - - switch (color) - { - case SKINCOLOR_WHITE: - case SKINCOLOR_SILVER: - case SKINCOLOR_SLATE: - cstart = "\x80"; // White - break; - case SKINCOLOR_GREY: - case SKINCOLOR_NICKEL: - case SKINCOLOR_BLACK: - case SKINCOLOR_SKUNK: - case SKINCOLOR_PLATINUM: - case SKINCOLOR_JET: - cstart = "\x86"; // V_GRAYMAP - break; - case SKINCOLOR_SEPIA: - case SKINCOLOR_BEIGE: - case SKINCOLOR_CARAMEL: - case SKINCOLOR_PEACH: - case SKINCOLOR_BROWN: - case SKINCOLOR_LEATHER: - case SKINCOLOR_RUST: - case SKINCOLOR_WRISTWATCH: - cstart = "\x8e"; // V_BROWNMAP - break; - case SKINCOLOR_FAIRY: - case SKINCOLOR_PINK: - case SKINCOLOR_ROSE: - case SKINCOLOR_LEMONADE: - case SKINCOLOR_LILAC: - case SKINCOLOR_BLOSSOM: - case SKINCOLOR_TAFFY: - cstart = "\x8d"; // V_PINKMAP - break; - case SKINCOLOR_CINNAMON: - case SKINCOLOR_RUBY: - case SKINCOLOR_RASPBERRY: - case SKINCOLOR_RED: - case SKINCOLOR_CRIMSON: - case SKINCOLOR_MAROON: - case SKINCOLOR_SCARLET: - case SKINCOLOR_KETCHUP: - cstart = "\x85"; // V_REDMAP - break; - case SKINCOLOR_DAWN: - case SKINCOLOR_SUNSLAM: - case SKINCOLOR_CREAMSICLE: - case SKINCOLOR_ORANGE: - case SKINCOLOR_ROSEWOOD: - case SKINCOLOR_TANGERINE: - cstart = "\x87"; // V_ORANGEMAP - break; - case SKINCOLOR_TAN: - case SKINCOLOR_CREAM: - cstart = "\x8f"; // V_TANMAP - break; - case SKINCOLOR_GOLD: - case SKINCOLOR_ROYAL: - case SKINCOLOR_BRONZE: - case SKINCOLOR_COPPER: - case SKINCOLOR_THUNDER: - cstart = "\x8A"; // V_GOLDMAP - break; - case SKINCOLOR_POPCORN: - case SKINCOLOR_YELLOW: - case SKINCOLOR_MUSTARD: - case SKINCOLOR_BANANA: - case SKINCOLOR_OLIVE: - case SKINCOLOR_CROCODILE: - cstart = "\x82"; // V_YELLOWMAP - break; - case SKINCOLOR_ARTICHOKE: - case SKINCOLOR_PERIDOT: - case SKINCOLOR_VOMIT: - case SKINCOLOR_GARDEN: - case SKINCOLOR_LIME: - case SKINCOLOR_HANDHELD: - case SKINCOLOR_TEA: - case SKINCOLOR_PISTACHIO: - case SKINCOLOR_MOSS: - case SKINCOLOR_CAMOUFLAGE: - case SKINCOLOR_MINT: - case SKINCOLOR_GREEN: - case SKINCOLOR_PINETREE: - case SKINCOLOR_TURTLE: - case SKINCOLOR_SWAMP: - case SKINCOLOR_DREAM: - case SKINCOLOR_PLAGUE: - case SKINCOLOR_EMERALD: - case SKINCOLOR_ALGAE: - cstart = "\x83"; // V_GREENMAP - break; - case SKINCOLOR_AQUAMARINE: - case SKINCOLOR_TURQUOISE: - case SKINCOLOR_TEAL: - cstart = "\x8b"; // V_AQUAMAP - break; - case SKINCOLOR_PIGEON: - case SKINCOLOR_ROBIN: - case SKINCOLOR_CYAN: - case SKINCOLOR_JAWZ: - case SKINCOLOR_CERULEAN: - case SKINCOLOR_NAVY: - case SKINCOLOR_SAPPHIRE: - cstart = "\x88"; // V_SKYMAP - break; - case SKINCOLOR_STEEL: - case SKINCOLOR_ULTRAMARINE: - case SKINCOLOR_PERIWINKLE: - case SKINCOLOR_BLUE: - case SKINCOLOR_MIDNIGHT: - case SKINCOLOR_BLUEBERRY: - case SKINCOLOR_NOVA: - cstart = "\x84"; // V_BLUEMAP - break; - case SKINCOLOR_THISTLE: - case SKINCOLOR_PURPLE: - case SKINCOLOR_PASTEL: - cstart = "\x81"; // V_PURPLEMAP - break; - case SKINCOLOR_MAGENTA: - case SKINCOLOR_FUCHSIA: - case SKINCOLOR_MOONSET: - case SKINCOLOR_VIOLET: - cstart = "\x8c"; // V_MAGENTAMAP - break; - case SKINCOLOR_DUSK: - case SKINCOLOR_TOXIC: - case SKINCOLOR_MAUVE: - case SKINCOLOR_LAVENDER: - case SKINCOLOR_BYZANTIUM: - case SKINCOLOR_POMEGRANATE: - cstart = "\x89"; // V_LAVENDERMAP - break; - default: - break; - } + cstart = "\x80" + (K_SkincolorToTextColor(players[playernum].skincolor) >> V_CHARCOLORSHIFT); } prefix = cstart; @@ -910,6 +770,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) tempchar = (char *)Z_Calloc(strlen(cstart) + strlen(adminchar) + 1, PU_STATIC, NULL); else if (IsPlayerAdmin(playernum)) tempchar = (char *)Z_Calloc(strlen(cstart) + strlen(remotechar) + 1, PU_STATIC, NULL); + if (tempchar) { if (playernum == serverplayer) diff --git a/src/k_color.c b/src/k_color.c index 89112db29..ee3fbc626 100644 --- a/src/k_color.c +++ b/src/k_color.c @@ -380,12 +380,160 @@ UINT8 colortranslations[MAXTRANSLATIONS][16] = { { 0, 0, 96, 96, 97, 98, 98, 99, 81, 81, 69, 71, 73, 75, 77, 79}, // SKINCOLOR_CSUPER5 }; +/*-------------------------------------------------- + INT32 K_SkincolorToTextColor(UINT8 skincolor) + + See header file for description. +--------------------------------------------------*/ +INT32 K_SkincolorToTextColor(UINT8 skincolor) +{ + INT32 textcolor = 0; + + switch (skincolor) + { + default: // default to white? + case SKINCOLOR_WHITE: + case SKINCOLOR_SILVER: + case SKINCOLOR_SLATE: + textcolor = 0; // No color is white + break; + case SKINCOLOR_GREY: + case SKINCOLOR_NICKEL: + case SKINCOLOR_BLACK: + case SKINCOLOR_SKUNK: + case SKINCOLOR_PLATINUM: + case SKINCOLOR_JET: + textcolor = V_GRAYMAP; + break; + case SKINCOLOR_SEPIA: + case SKINCOLOR_BEIGE: + case SKINCOLOR_CARAMEL: + case SKINCOLOR_PEACH: + case SKINCOLOR_BROWN: + case SKINCOLOR_LEATHER: + case SKINCOLOR_RUST: + case SKINCOLOR_WRISTWATCH: + textcolor = V_BROWNMAP; + break; + case SKINCOLOR_FAIRY: + case SKINCOLOR_PINK: + case SKINCOLOR_ROSE: + case SKINCOLOR_LEMONADE: + case SKINCOLOR_LILAC: + case SKINCOLOR_BLOSSOM: + case SKINCOLOR_TAFFY: + textcolor = V_PINKMAP; + break; + case SKINCOLOR_CINNAMON: + case SKINCOLOR_RUBY: + case SKINCOLOR_RASPBERRY: + case SKINCOLOR_RED: + case SKINCOLOR_CRIMSON: + case SKINCOLOR_MAROON: + case SKINCOLOR_SCARLET: + case SKINCOLOR_KETCHUP: + textcolor = V_REDMAP; + break; + case SKINCOLOR_DAWN: + case SKINCOLOR_SUNSLAM: + case SKINCOLOR_CREAMSICLE: + case SKINCOLOR_ORANGE: + case SKINCOLOR_ROSEWOOD: + case SKINCOLOR_TANGERINE: + textcolor = V_ORANGEMAP; + break; + case SKINCOLOR_TAN: + case SKINCOLOR_CREAM: + textcolor = V_TANMAP; + break; + case SKINCOLOR_GOLD: + case SKINCOLOR_ROYAL: + case SKINCOLOR_BRONZE: + case SKINCOLOR_COPPER: + case SKINCOLOR_THUNDER: + textcolor = V_GOLDMAP; + break; + case SKINCOLOR_POPCORN: + case SKINCOLOR_YELLOW: + case SKINCOLOR_MUSTARD: + case SKINCOLOR_BANANA: + case SKINCOLOR_OLIVE: + case SKINCOLOR_CROCODILE: + textcolor = V_YELLOWMAP; + break; + case SKINCOLOR_ARTICHOKE: + case SKINCOLOR_PERIDOT: + case SKINCOLOR_VOMIT: + case SKINCOLOR_GARDEN: + case SKINCOLOR_LIME: + case SKINCOLOR_HANDHELD: + case SKINCOLOR_TEA: + case SKINCOLOR_PISTACHIO: + case SKINCOLOR_MOSS: + case SKINCOLOR_CAMOUFLAGE: + case SKINCOLOR_MINT: + case SKINCOLOR_GREEN: + case SKINCOLOR_PINETREE: + case SKINCOLOR_TURTLE: + case SKINCOLOR_SWAMP: + case SKINCOLOR_DREAM: + case SKINCOLOR_PLAGUE: + case SKINCOLOR_EMERALD: + case SKINCOLOR_ALGAE: + textcolor = V_GREENMAP; + break; + case SKINCOLOR_AQUAMARINE: + case SKINCOLOR_TURQUOISE: + case SKINCOLOR_TEAL: + textcolor = V_AQUAMAP; + break; + case SKINCOLOR_PIGEON: + case SKINCOLOR_ROBIN: + case SKINCOLOR_CYAN: + case SKINCOLOR_JAWZ: + case SKINCOLOR_CERULEAN: + case SKINCOLOR_NAVY: + case SKINCOLOR_SAPPHIRE: + textcolor = V_SKYMAP; + break; + case SKINCOLOR_STEEL: + case SKINCOLOR_ULTRAMARINE: + case SKINCOLOR_PERIWINKLE: + case SKINCOLOR_BLUE: + case SKINCOLOR_MIDNIGHT: + case SKINCOLOR_BLUEBERRY: + case SKINCOLOR_NOVA: + textcolor = V_BLUEMAP; + break; + case SKINCOLOR_THISTLE: + case SKINCOLOR_PURPLE: + case SKINCOLOR_PASTEL: + textcolor = V_PURPLEMAP; + break; + case SKINCOLOR_MAGENTA: + case SKINCOLOR_FUCHSIA: + case SKINCOLOR_MOONSET: + case SKINCOLOR_VIOLET: + textcolor = V_MAGENTAMAP; + break; + case SKINCOLOR_DUSK: + case SKINCOLOR_TOXIC: + case SKINCOLOR_MAUVE: + case SKINCOLOR_LAVENDER: + case SKINCOLOR_BYZANTIUM: + case SKINCOLOR_POMEGRANATE: + textcolor = V_LAVENDERMAP; + break; + } + + return textcolor; +} + /*-------------------------------------------------- UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b) See header file for description. --------------------------------------------------*/ - UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b) { UINT32 redweight = 1063 * r; @@ -400,7 +548,6 @@ UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b) See header file for description. --------------------------------------------------*/ - void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor) { INT32 i; @@ -444,14 +591,11 @@ void K_RainbowColormap(UINT8 *dest_colormap, UINT8 skincolor) } } -/** \brief Generates a translation colormap for Kart, to replace R_GenerateTranslationColormap in r_draw.c +/*-------------------------------------------------- + void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color) - \param dest_colormap colormap to populate - \param skinnum number of skin, TC_DEFAULT or TC_BOSS - \param color translation color - - \return void -*/ + See header file for description. +--------------------------------------------------*/ void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color) { INT32 i; @@ -505,12 +649,11 @@ void K_GenerateKartColormap(UINT8 *dest_colormap, INT32 skinnum, UINT8 color) } } -/** \brief Pulls kart color by name, to replace R_GetColorByName in r_draw.c +/*-------------------------------------------------- + UINT8 K_GetKartColorByName(const char *name) - \param name color name - - \return 0 -*/ + See header file for description. +--------------------------------------------------*/ UINT8 K_GetKartColorByName(const char *name) { UINT8 color = (UINT8)atoi(name); diff --git a/src/k_color.h b/src/k_color.h index 2a21473e2..85164623d 100644 --- a/src/k_color.h +++ b/src/k_color.h @@ -23,6 +23,21 @@ extern UINT8 colortranslations[MAXTRANSLATIONS][16]; extern const char *KartColor_Names[MAXSKINCOLORS]; extern const UINT8 KartColor_Opposite[MAXSKINCOLORS*2]; +/*-------------------------------------------------- + INT32 K_SkincolorToTextColor(UINT8 skincolor); + + Gives you the "text" color (V_ constants) from a skincolor (SKINCOLOR_ constants). + Used primarily by the chat system. + + Input Arguments:- + skincolor - SKINCOLOR_ constant + + Return:- + V_ constant for font coloring +--------------------------------------------------*/ +INT32 K_SkincolorToTextColor(UINT8 skincolor); + + /*-------------------------------------------------- UINT8 K_ColorRelativeLuminance(UINT8 r, UINT8 g, UINT8 b); diff --git a/src/k_kart.c b/src/k_kart.c index a35405c6c..d7a503ea3 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10176,7 +10176,7 @@ static void K_drawKartPlayerCheck(void) static void K_drawKartNameTags(void) { - const fixed_t maxdistance = 4096*mapobjectscale; + const fixed_t maxdistance = 8192*mapobjectscale; camera_t *thiscam; vertex_t c; UINT8 cnum = 0; @@ -10247,7 +10247,7 @@ static void K_drawKartNameTags(void) } } - if (j < r_splitscreen) + if (j <= r_splitscreen) { // Is a player that's being shown on this computer continue; @@ -10286,14 +10286,46 @@ static void K_drawKartNameTags(void) V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_rival[blink], NULL); } } - else + else if (netgame) { if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-1) - && (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+1)) + && (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+2)) { - //INT32 namelen = V_ThinStringWidth(str, V_6WIDTHSPACE); + INT32 namelen = V_ThinStringWidth(player_names[i], V_6WIDTHSPACE|V_ALLOWLOWERCASE); + INT32 clr = K_SkincolorToTextColor(ntplayer->skincolor); + UINT8 *colormap = V_GetStringColormap(clr); + INT32 barx = 0, bary = 0, barw = 0; - V_DrawFixedPatch(x, y, FRACUNIT, V_HUDTRANS, kp_nametagstem, NULL); + // Since there's no "V_DrawFixedFill", and I don't feel like making it, + // fuck it, we're gonna just V_NOSCALESTART hack it + barw = (namelen * vid.dupx); + + barx = (x * vid.dupx) / FRACUNIT; + bary = (y * vid.dupy) / FRACUNIT; + + barx += (6 * vid.dupx); + bary -= (16 * vid.dupx); + + // Center it if necessary + if (vid.width != BASEVIDWIDTH * vid.dupx) + { + barx += (vid.width - (BASEVIDWIDTH * vid.dupx)) / 2; + } + + if (vid.height != BASEVIDHEIGHT * vid.dupy) + { + bary += (vid.height - (BASEVIDHEIGHT * vid.dupy)) / 2; + } + + V_DrawFill(barx, bary, barw, (3 * vid.dupy), colormap[31]|V_NOSCALESTART); + V_DrawFill(barx, bary + vid.dupy, barw, vid.dupy, colormap[0]|V_NOSCALESTART); + // END DRAWFILL DUMBNESS + + // Draw the stem + V_DrawFixedPatch(x, y, FRACUNIT, 0, kp_nametagstem, colormap); + + // Draw the name itself + V_DrawThinStringAtFixed(x + (5*FRACUNIT), y - (26*FRACUNIT), V_6WIDTHSPACE|V_ALLOWLOWERCASE|clr, player_names[i]); } } } diff --git a/src/v_video.h b/src/v_video.h index d162d5c50..2066d1399 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -225,6 +225,9 @@ void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *st #define V_DrawStringAtFixed( x,y,option,string ) \ V__DrawOneScaleString (x,y,FRACUNIT,option,HU_FONT,string) +#define V_DrawThinStringAtFixed( x,y,option,string ) \ + V__DrawOneScaleString (x,y,FRACUNIT,option,TINY_FONT,string) + // Draw tall nums, used for menu, HUD, intermission void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num); void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits); From d10bfe1110bbc9ef54b560455382f7429b4e6668 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 4 Jun 2020 00:09:30 -0400 Subject: [PATCH 37/65] Encore mode support --- src/k_kart.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index d7a503ea3..c0b748a04 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10026,6 +10026,11 @@ static void K_ObjectTracking(fixed_t *hud_x, fixed_t *hud_y, vertex_t *campos, a { *hud_x = FixedMul(NEWTAN(anglediff), swhalffixed) + swhalffixed; + if (encoremode) + { + *hud_x = (BASEVIDWIDTH * FRACUNIT) - *hud_x; + } + if (r_splitscreen >= 2) { *hud_x /= 2; From 4fd9c891243ed6da4880662c9eb47cc06f8b53e6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 4 Jun 2020 00:10:23 -0400 Subject: [PATCH 38/65] Use player object again for CHECK --- src/k_kart.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index c0b748a04..4bc5d4896 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -10103,9 +10103,9 @@ static void K_drawKartPlayerCheck(void) thiscam = &camera[cnum]; - c.x = thiscam->x; - c.y = thiscam->y; - c.z = thiscam->z; + c.x = stplyr->mo->x; + c.y = stplyr->mo->y; + c.z = stplyr->mo->z; for (i = 0; i < MAXPLAYERS; i++) { From 889cf581d12ee991d0f68a94ef9d1578108880d5 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 4 Jun 2020 15:17:59 -0400 Subject: [PATCH 39/65] Fix bot air speed cap adjustments being reversed --- src/p_user.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index d808002a3..4c8015948 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -4185,23 +4185,21 @@ static void P_3dMovement(player_t *player) const fixed_t airspeedcap = (50*mapobjectscale); const fixed_t speed = R_PointToDist2(0, 0, player->mo->momx, player->mo->momy); + // If you're going too fast in the air, ease back down to a certain speed. + // Helps lots of jumps from breaking when using speed items, since you can't move in the air. if (speed > airspeedcap) { fixed_t div = 32*FRACUNIT; fixed_t newspeed; + // Make rubberbanding bots slow down faster if (K_PlayerUsesBotMovement(player)) { - fixed_t baserubberband = K_BotRubberband(player); - fixed_t rubberband = FixedMul(baserubberband, - FixedMul(baserubberband, - FixedMul(baserubberband, - baserubberband - ))); // This looks extremely goofy, but we need this really high, but at the same time, proportional. + fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; - if (rubberband > FRACUNIT) + if (rubberband > 0) { - div = FixedMul(div, rubberband); + div = FixedDiv(div, FRACUNIT + (rubberband * 2)); } } From 4005288d4008bd1d9d10aebd3a81d00a67f6f22e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 4 Jun 2020 15:59:51 -0400 Subject: [PATCH 40/65] Adjust max/min rubberband multiplier with bot difficulty Lv. 1: Ranges from x0.75 to x1.0 (never speeds up, only slows down) Lv. 5: Ranges from x0.875 to x1.5 (completely average) Lv. 9: Ranges from x1.0 to x2.0 (never slows down, only speeds up) --- src/k_bot.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 38b9bff5a..2f2511f86 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -342,6 +342,7 @@ static UINT32 K_BotRubberbandDistance(player_t *player) fixed_t K_BotRubberband(player_t *player) { fixed_t rubberband = FRACUNIT; + fixed_t max, min; player_t *firstplace = NULL; UINT8 i; @@ -388,13 +389,23 @@ fixed_t K_BotRubberband(player_t *player) } } - if (rubberband > 2*FRACUNIT) + // Lv. 1: x1.0 max + // Lv. 5: x1.5 max + // Lv. 9: x2.0 max + max = FRACUNIT + ((FRACUNIT * (player->botvars.difficulty - 1)) / (MAXBOTDIFFICULTY - 1)); + + // Lv. 1: x0.75 min + // Lv. 5: x0.875 min + // Lv. 9: x1.0 min + min = FRACUNIT - (((FRACUNIT/4) * (MAXBOTDIFFICULTY - player->botvars.difficulty)) / (MAXBOTDIFFICULTY - 1)); + + if (rubberband > max) { - rubberband = 2*FRACUNIT; + rubberband = max; } - else if (rubberband < 7*FRACUNIT/8) + else if (rubberband < min) { - rubberband = 7*FRACUNIT/8; + rubberband = min; } return rubberband; From 4f0d49c87b974c6b21deab225fa80f42fc0c3405 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 4 Jun 2020 16:01:15 -0400 Subject: [PATCH 41/65] Disable rubberbanding on acceleration This makes hitting bots feel more meaningful, and makes bots with the accel stat not as obsolete when every bot's rubberbanding :p --- src/k_kart.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 6c5a354e7..677e1c2ad 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2031,10 +2031,13 @@ fixed_t K_GetKartAccel(player_t *player) //k_accel += 3 * (9 - kartspeed); // 36 - 60 k_accel += 4 * (9 - kartspeed); // 32 - 64 +#if 0 + // Rubberbanding acceleration is disabled since it makes hits feel more meaningful if (K_PlayerUsesBotMovement(player)) { k_accel = FixedMul(k_accel, K_BotRubberband(player)); } +#endif return FixedMul(k_accel, FRACUNIT+player->kartstuff[k_accelboost]); } From 9603c5fea7761cba7de229483704b22f047f3a9e Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 4 Jun 2020 16:52:24 -0400 Subject: [PATCH 42/65] Revise rival score system, so that it's much more likely to make the rival status switch places --- src/k_bot.c | 1 + src/k_grandprix.c | 34 ++++++++++++++++++++++++++++------ 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 2f2511f86..5bee12b18 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -348,6 +348,7 @@ fixed_t K_BotRubberband(player_t *player) if (player->exiting) { + // You're done, we don't need to rubberband anymore. return FRACUNIT; } diff --git a/src/k_grandprix.c b/src/k_grandprix.c index e82f34560..9b01af1c4 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -253,11 +253,33 @@ static INT16 K_RivalScore(player_t *bot) const UINT16 difficulty = bot->botvars.difficulty; const UINT16 score = bot->score; const SINT8 roundsleft = grandprixinfo.cup->numlevels - grandprixinfo.roundnum; + UINT16 lowestscore = UINT16_MAX; + UINT8 lowestdifficulty = MAXBOTDIFFICULTY; + UINT8 i; - // In the early game, difficulty is more important for long-term challenge. - // When we're running low on matches left though, we need to focus more on score. + for (i = 0; i < MAXPLAYERS; i++) + { + if (!playeringame[i] || players[i].spectator) + { + continue; + } - return (difficulty * roundsleft) + (score * grandprixinfo.roundnum); + if (players[i].score < lowestscore) + { + lowestscore = players[i].score; + } + + if (players[i].bot == true && players[i].botvars.difficulty < lowestdifficulty) + { + lowestdifficulty = players[i].botvars.difficulty; + } + } + + // In the early game, difficulty is more important. + // This will try to influence the higher difficulty bots to get rival more often & get even more points. + // However, when we're running low on matches left, we need to focus more on raw score! + + return ((difficulty - lowestdifficulty) * roundsleft) + ((score - lowestscore) * grandprixinfo.roundnum); } void K_UpdateGrandPrixBots(void) @@ -322,15 +344,16 @@ void K_UpdateGrandPrixBots(void) } } - // Even if there's a new rival, we want to make sure that they're a better fit than the current one! + // Even if there's a new rival, we want to make sure that they're a better fit than the current one. if (oldrival != newrival) { if (oldrival != NULL) { UINT16 os = K_RivalScore(oldrival); - if (newrivalscore <= os + 100) + if (newrivalscore < os + 5) { + // This rival's only *slightly* better, no need to fire the old one. // Our current rival's just fine, thank you very much. return; } @@ -341,7 +364,6 @@ void K_UpdateGrandPrixBots(void) // Set our new rival! newrival->botvars.rival = true; - CONS_Printf("Rival is now %s\n", player_names[newrival - players]); } } From d4153ca7aad104ea96affad60487d152ef00cd53 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 4 Jun 2020 16:52:54 -0400 Subject: [PATCH 43/65] Increase GP bonus points --- src/k_grandprix.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 9b01af1c4..7f1ba8899 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -54,9 +54,9 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers) // This rounds out the point gain when you get 1st every race, // and gives bots able to catch up in points if a player gets an early lead. // The maximum points you can get in a cup is: ((number of players - 1) + (max extra points)) * (number of races) - // 8P: (7 + 3) * 5 = 50 maximum points - // 12P: (11 + 3) * 5 = 70 maximum points - // 16P: (15 + 3) * 5 = 90 maximum points + // 8P: (7 + 5) * 5 = 60 maximum points + // 12P: (11 + 5) * 5 = 80 maximum points + // 16P: (15 + 5) * 5 = 100 maximum points switch (numplayers) { case 0: case 1: case 2: // 1v1 @@ -64,13 +64,13 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers) case 3: case 4: // 3-4P if (position == 1) { points += 1; } // 1st gets +1 extra point break; - case 5: case 6: - if (position == 1) { points += 2; } // 1st gets +2 extra points + case 5: case 6: // 5-6P + if (position == 1) { points += 3; } // 1st gets +3 extra points else if (position == 2) { points += 1; } // 2nd gets +1 extra point break; default: // Normal matches - if (position == 1) { points += 3; } // 1st gets +3 extra points - else if (position == 2) { points += 2; } // 2nd gets +2 extra points + if (position == 1) { points += 5; } // 1st gets +5 extra points + else if (position == 2) { points += 3; } // 2nd gets +3 extra points else if (position == 3) { points += 1; } // 3rd gets +1 extra point break; } From 42419f7bc621f791c55a5af2e2f2530d0b163391 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 4 Jun 2020 17:14:14 -0400 Subject: [PATCH 44/65] Add comments for functions --- src/k_bot.c | 1 + src/k_bot.h | 1 + src/k_botitem.c | 1 + src/k_botsearch.c | 1 + src/k_grandprix.c | 67 +++++++++++++++++++++++++-- src/k_grandprix.h | 115 +++++++++++++++++++++++++++++++++++++++++++--- 6 files changed, 175 insertions(+), 11 deletions(-) diff --git a/src/k_bot.c b/src/k_bot.c index 5bee12b18..ebc1b4869 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -1,5 +1,6 @@ // SONIC ROBO BLAST 2 KART //----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour // Copyright (C) 2018-2020 by Kart Krew // // This program is free software distributed under the diff --git a/src/k_bot.h b/src/k_bot.h index d6d7f8338..2cb1ae460 100644 --- a/src/k_bot.h +++ b/src/k_bot.h @@ -1,5 +1,6 @@ // SONIC ROBO BLAST 2 KART //----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour // Copyright (C) 2018-2020 by Kart Krew // // This program is free software distributed under the diff --git a/src/k_botitem.c b/src/k_botitem.c index 55f8e15e4..94f0b8e10 100644 --- a/src/k_botitem.c +++ b/src/k_botitem.c @@ -1,5 +1,6 @@ // SONIC ROBO BLAST 2 KART //----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour // Copyright (C) 2018-2020 by Kart Krew // // This program is free software distributed under the diff --git a/src/k_botsearch.c b/src/k_botsearch.c index 28e08f3cf..4dd55cc42 100644 --- a/src/k_botsearch.c +++ b/src/k_botsearch.c @@ -1,5 +1,6 @@ // SONIC ROBO BLAST 2 KART //----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour // Copyright (C) 2018-2020 by Kart Krew // // This program is free software distributed under the diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 7f1ba8899..971866476 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -1,14 +1,14 @@ -// SONIC ROBO BLAST 2 +// SONIC ROBO BLAST 2 KART //----------------------------------------------------------------------------- -// Copyright (C) 2007-2016 by John "JTE" Muniz. -// Copyright (C) 2011-2018 by Sonic Team Junior. +// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2018-2020 by Kart Krew // // This program is free software distributed under the // terms of the GNU General Public License, version 2. // See the 'LICENSE' file for more details. //----------------------------------------------------------------------------- /// \file k_grandprix.c -/// \brief Grand Prix mode specific code +/// \brief Grand Prix mode game logic & bot behaviors #include "k_grandprix.h" #include "doomdef.h" @@ -21,6 +21,11 @@ struct grandprixinfo grandprixinfo; +/*-------------------------------------------------- + UINT8 K_BotStartingDifficulty(SINT8 value) + + See header file for description. +--------------------------------------------------*/ UINT8 K_BotStartingDifficulty(SINT8 value) { // startingdifficulty: Easy = 3, Normal = 6, Hard = 9 @@ -38,6 +43,11 @@ UINT8 K_BotStartingDifficulty(SINT8 value) return difficulty; } +/*-------------------------------------------------- + INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers) + + See header file for description. +--------------------------------------------------*/ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers) { INT16 points; @@ -84,6 +94,11 @@ INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers) return points; } +/*-------------------------------------------------- + void K_InitGrandPrixBots(void) + + See header file for description. +--------------------------------------------------*/ void K_InitGrandPrixBots(void) { const char *defaultbotskinname = "eggrobo"; @@ -248,6 +263,18 @@ void K_InitGrandPrixBots(void) } } +/*-------------------------------------------------- + static INT16 K_RivalScore(player_t *bot) + + Creates a "rival score" for a bot, used to determine which bot is the + most deserving of the rival status. + + Input Arguments:- + bot - Player to check. + + Return:- + "Rival score" value. +--------------------------------------------------*/ static INT16 K_RivalScore(player_t *bot) { const UINT16 difficulty = bot->botvars.difficulty; @@ -282,6 +309,11 @@ static INT16 K_RivalScore(player_t *bot) return ((difficulty - lowestdifficulty) * roundsleft) + ((score - lowestscore) * grandprixinfo.roundnum); } +/*-------------------------------------------------- + void K_UpdateGrandPrixBots(void) + + See header file for description. +--------------------------------------------------*/ void K_UpdateGrandPrixBots(void) { player_t *oldrival = NULL; @@ -367,6 +399,18 @@ void K_UpdateGrandPrixBots(void) } } +/*-------------------------------------------------- + static UINT8 K_BotExpectedStanding(player_t *bot) + + Predicts what placement a bot was expected to be in. + Used for determining if a bot's difficulty should raise. + + Input Arguments:- + bot - Player to check. + + Return:- + Position number the bot was expected to be in. +--------------------------------------------------*/ static UINT8 K_BotExpectedStanding(player_t *bot) { UINT8 pos = 1; @@ -403,6 +447,11 @@ static UINT8 K_BotExpectedStanding(player_t *bot) return pos; } +/*-------------------------------------------------- + void K_IncreaseBotDifficulty(player_t *bot) + + See header file for description. +--------------------------------------------------*/ void K_IncreaseBotDifficulty(player_t *bot) { UINT8 expectedstanding; @@ -443,6 +492,11 @@ void K_IncreaseBotDifficulty(player_t *bot) } } +/*-------------------------------------------------- + void K_FakeBotResults(player_t *bot) + + See header file for description. +--------------------------------------------------*/ void K_FakeBotResults(player_t *bot) { const UINT32 distfactor = FixedMul(32 * bot->mo->scale, K_GetKartGameSpeedScalar(gamespeed)) / FRACUNIT; @@ -496,6 +550,11 @@ void K_FakeBotResults(player_t *bot) K_IncreaseBotDifficulty(bot); } +/*-------------------------------------------------- + void K_PlayerLoseLife(player_t *player) + + See header file for description. +--------------------------------------------------*/ void K_PlayerLoseLife(player_t *player) { if (!G_GametypeUsesLives()) diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 7368b96f7..3deccabd1 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -1,3 +1,15 @@ +// SONIC ROBO BLAST 2 KART +//----------------------------------------------------------------------------- +// Copyright (C) 2018-2020 by Sally "TehRealSalt" Cochenour +// Copyright (C) 2018-2020 by Kart Krew +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- +/// \file k_grandprix.h +/// \brief Grand Prix mode game logic & bot behaviors + #ifndef __K_GRANDPRIX__ #define __K_GRANDPRIX__ @@ -6,21 +18,110 @@ extern struct grandprixinfo { - UINT8 roundnum; ///< Round number -- if 0, then we're not in a Grand Prix. - cupheader_t *cup; ///< Which cup are we playing? - UINT8 gamespeed; ///< Copy of gamespeed, just to make sure you can't cheat it with cvars - boolean encore; ///< Ditto, but for encore mode - boolean masterbots; ///< If true, all bots should be max difficulty (Master Mode) - boolean initalize; ///< If true, we need to initialize a new cup. - boolean wonround; ///< If false, then we retry the map instead of going to the next. + UINT8 roundnum; ///< Round number -- if 0, then we're not in a Grand Prix. + cupheader_t *cup; ///< Which cup are we playing? + UINT8 gamespeed; ///< Copy of gamespeed, just to make sure you can't cheat it with cvars + boolean encore; ///< Ditto, but for encore mode + boolean masterbots; ///< If true, all bots should be max difficulty (Master Mode) + boolean initalize; ///< If true, we need to initialize a new session. + boolean wonround; ///< If false, then we retry the map instead of going to the next. } grandprixinfo; + +/*-------------------------------------------------- + UINT8 K_BotStartingDifficulty(SINT8 value); + + Determines the starting difficulty of the bots + for a specific game speed. + + Input Arguments:- + value - Game speed value to use. + + Return:- + Bot difficulty level. +--------------------------------------------------*/ + UINT8 K_BotStartingDifficulty(SINT8 value); + + +/*-------------------------------------------------- + INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers); + + Calculates the number of points that a player would + recieve if they won the round. + + Input Arguments:- + position - Finishing position. + numplayers - Number of players in the game. + + Return:- + Number of points to give. +--------------------------------------------------*/ + INT16 K_CalculateGPRankPoints(UINT8 position, UINT8 numplayers); + + +/*-------------------------------------------------- + void K_InitGrandPrixBots(void); + + Spawns bots specifically tailored for Grand Prix mode. +--------------------------------------------------*/ + void K_InitGrandPrixBots(void); + + +/*-------------------------------------------------- + void K_UpdateGrandPrixBots(void); + + Updates bot settings based on the the results of the race. +--------------------------------------------------*/ + void K_UpdateGrandPrixBots(void); + + +/*-------------------------------------------------- + void K_IncreaseBotDifficulty(player_t *bot); + + Increases the difficulty of this bot when they finish the race. + + Input Arguments:- + bot - Player to do this for. + + Return:- + None +--------------------------------------------------*/ + void K_IncreaseBotDifficulty(player_t *bot); + + +/*-------------------------------------------------- + void K_FakeBotResults(player_t *bot); + + Fakes the results of the race, when all human players have + already finished and only bots were remaining. + + Input Arguments:- + bot - Player to do this for. + + Return:- + None +--------------------------------------------------*/ + void K_FakeBotResults(player_t *bot); + + +/*-------------------------------------------------- + void K_PlayerLoseLife(player_t *player); + + Removes a life from a human player. + + Input Arguments:- + player - Player to do this for. + + Return:- + None +--------------------------------------------------*/ + void K_PlayerLoseLife(player_t *player); #endif From 3ae7c11cf1fe3c1063215f293d40817acaf30e69 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 4 Jun 2020 17:48:05 -0400 Subject: [PATCH 45/65] Count the player's skin as used --- src/k_grandprix.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 971866476..afecf5989 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -111,7 +111,7 @@ void K_InitGrandPrixBots(void) UINT8 wantedbots = 0; UINT8 numplayers = 0; - UINT8 competitors[4]; + UINT8 competitors[MAXSPLITSCREENPLAYERS]; boolean skinusable[MAXSKINS]; UINT8 botskinlist[MAXPLAYERS]; @@ -179,6 +179,7 @@ void K_InitGrandPrixBots(void) if (numplayers < MAXSPLITSCREENPLAYERS && !players[i].spectator) { competitors[numplayers] = i; + skinusable[players[i].skin] = false; numplayers++; } else From 47d6fba4de54be3c8ebe645d2f47122f741e34b2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Thu, 4 Jun 2020 17:48:18 -0400 Subject: [PATCH 46/65] Remove duplicate in CMakeLists --- src/CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 978431383..b4a7ff490 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -157,9 +157,7 @@ set(SRB2_CORE_GAME_SOURCES p_user.c k_battle.c k_bheap.c - k_bot.c k_collide.c - k_grandprix.c k_kart.c k_pathfind.c k_pwrlv.c @@ -169,6 +167,7 @@ set(SRB2_CORE_GAME_SOURCES k_botitem.c k_botsearch.c k_respawn.c + k_grandprix.c p_local.h p_maputl.h @@ -182,9 +181,7 @@ set(SRB2_CORE_GAME_SOURCES p_tick.h k_battle.h k_bheap.h - k_bot.h k_collide.h - k_grandprix.h k_kart.h k_pathfind.h k_pwrlv.h @@ -192,6 +189,7 @@ set(SRB2_CORE_GAME_SOURCES k_color.h k_bot.h k_respawn.h + k_grandprix.h ) if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) From 0fc9844b85aee2697fd9f42548c02d7ad168ac7c Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 5 Jun 2020 00:57:47 -0400 Subject: [PATCH 47/65] Speed pads multiply your speed (line length is used as a minimum) --- src/p_spec.c | 40 ++++++++++------------------------------ 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 03c08fdee..a6efa3256 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4053,51 +4053,31 @@ DoneSection2: case 5: // Speed pad w/o spin case 6: // Speed pad w/ spin if (player->kartstuff[k_dashpadcooldown] != 0) + { + player->kartstuff[k_dashpadcooldown] = 2; break; + } i = P_FindSpecialLineFromTag(4, sector->tag, -1); if (i != -1) { - angle_t lineangle; - fixed_t linespeed; - - lineangle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y); - linespeed = P_AproxDistance(lines[i].v2->x-lines[i].v1->x, lines[i].v2->y-lines[i].v1->y); + angle_t lineangle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y); + fixed_t linespeed = P_AproxDistance(lines[i].v2->x-lines[i].v1->x, lines[i].v2->y-lines[i].v1->y); + fixed_t newspeed = 2 * P_AproxDistance(player->mo->momx, player->mo->momy); // SRB2Kart: Scale the speed you get from them! // This is scaled differently from other horizontal speed boosts from stuff like springs, because of how this is used for some ramp jumps. if (player->mo->scale > mapobjectscale) + { linespeed = FixedMul(linespeed, mapobjectscale + (player->mo->scale - mapobjectscale)); - - if (!(lines[i].flags & ML_EFFECT4)) - { - P_UnsetThingPosition(player->mo); - if (roversector) // make FOF speed pads work - { - player->mo->x = roversector->soundorg.x; - player->mo->y = roversector->soundorg.y; - } - else - { - player->mo->x = sector->soundorg.x; - player->mo->y = sector->soundorg.y; - } - P_SetThingPosition(player->mo); } - P_InstaThrust(player->mo, lineangle, linespeed); + P_InstaThrust(player->mo, lineangle, max(linespeed, newspeed)); - player->kartstuff[k_dashpadcooldown] = TICRATE/3; + player->kartstuff[k_dashpadcooldown] = 2; player->kartstuff[k_pogospring] = 0; - S_StartSound(player->mo, sfx_spdpad); - - { - sfxenum_t pick = P_RandomKey(2); // Gotta roll the RNG every time this is called for sync reasons - if (cv_kartvoices.value) - S_StartSound(player->mo, sfx_kbost1+pick); - //K_TauntVoiceTimers(player); - } + S_StartSound(player->mo, sfx_cdfm62); } break; From a9687144ece73bb9ce6fcf882a5a48991e293f4a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 5 Jun 2020 14:10:55 -0400 Subject: [PATCH 48/65] Speed pads reflect your angle like horizontal springs do --- src/k_kart.c | 42 ++++++++++++++++++++++++++++++++++++++++++ src/k_kart.h | 1 + src/p_map.c | 44 +++++--------------------------------------- src/p_spec.c | 9 +++++++-- 4 files changed, 55 insertions(+), 41 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index c24e8aeff..bd7ab22da 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -78,6 +78,48 @@ player_t *K_GetItemBoxPlayer(mobj_t *mobj) return player; } +// Angle reflection used by springs & speed pads +angle_t K_ReflectAngle(angle_t yourangle, angle_t theirangle, fixed_t yourspeed, fixed_t theirspeed) +{ + INT32 angoffset; + boolean subtract = false; + + angoffset = yourangle - theirangle; + + if ((angle_t)angoffset > ANGLE_180) + { + // Flip on wrong side + angoffset = InvAngle((angle_t)angoffset); + subtract = !subtract; + } + + // Fix going directly against the spring's angle sending you the wrong way + if ((angle_t)angoffset > ANGLE_90) + { + angoffset = ANGLE_180 - angoffset; + } + + // Offset is reduced to cap it (90 / 2 = max of 45 degrees) + angoffset /= 2; + + // Reduce further based on how slow your speed is compared to the spring's speed + // (set both to 0 to ignore this) + if (theirspeed != 0 && yourspeed != 0) + { + if (theirspeed > yourspeed) + { + angoffset = FixedDiv(angoffset, FixedDiv(theirspeed, yourspeed)); + } + } + + if (subtract) + angoffset = (signed)(theirangle) - angoffset; + else + angoffset = (signed)(theirangle) + angoffset; + + return (angle_t)angoffset; +} + //{ SRB2kart Net Variables void K_RegisterKartStuff(void) diff --git a/src/k_kart.h b/src/k_kart.h index 7a3a3581c..af247029d 100644 --- a/src/k_kart.h +++ b/src/k_kart.h @@ -13,6 +13,7 @@ #define KART_FULLTURN 800 player_t *K_GetItemBoxPlayer(mobj_t *mobj); +angle_t K_ReflectAngle(angle_t angle, angle_t against, fixed_t maxspeed, fixed_t yourspeed); void K_RegisterKartStuff(void); diff --git a/src/p_map.c b/src/p_map.c index 00d93efe7..ae61cd639 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -308,12 +308,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) { angle_t finalAngle = spring->angle; fixed_t finalSpeed = FixedMul(horizspeed, FixedSqrt(FixedMul(hscale, spring->scale))); - fixed_t objectSpeed; - - if (object->player) - objectSpeed = object->player->speed; - else - objectSpeed = R_PointToDist2(0, 0, savemomx, savemomy); + fixed_t objectSpeed = R_PointToDist2(0, 0, savemomx, savemomy); if (!vertispeed) { @@ -324,39 +319,10 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) // This makes it a bit more interesting & unique than just being a speed boost in a pre-defined direction if (savemomx || savemomy) { - angle_t momang; - INT32 angoffset; - boolean subtract = false; - - momang = R_PointToAngle2(0, 0, savemomx, savemomy); - - angoffset = momang; - angoffset -= spring->angle; // Subtract - - // Flip on wrong side - if ((angle_t)angoffset > ANGLE_180) - { - angoffset = InvAngle((angle_t)angoffset); - subtract = !subtract; - } - - // Fix going directly against the spring's angle sending you the wrong way - if ((spring->angle - momang) > ANGLE_90) - angoffset = ANGLE_180 - angoffset; - - // Offset is reduced to cap it (90 / 2 = max of 45 degrees) - angoffset /= 2; - - // Reduce further based on how slow your speed is compared to the spring's speed - if (finalSpeed > objectSpeed) - angoffset = FixedDiv(angoffset, FixedDiv(finalSpeed, objectSpeed)); - - if (subtract) - angoffset = (signed)(spring->angle) - angoffset; - else - angoffset = (signed)(spring->angle) + angoffset; - - finalAngle = angoffset; + finalAngle = K_ReflectAngle( + R_PointToAngle2(0, 0, savemomx, savemomy), finalAngle, + objectSpeed, finalSpeed + ); } } diff --git a/src/p_spec.c b/src/p_spec.c index a6efa3256..65dcc4aeb 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4064,7 +4064,7 @@ DoneSection2: { angle_t lineangle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y); fixed_t linespeed = P_AproxDistance(lines[i].v2->x-lines[i].v1->x, lines[i].v2->y-lines[i].v1->y); - fixed_t newspeed = 2 * P_AproxDistance(player->mo->momx, player->mo->momy); + fixed_t playerspeed = P_AproxDistance(player->mo->momx, player->mo->momy); // SRB2Kart: Scale the speed you get from them! // This is scaled differently from other horizontal speed boosts from stuff like springs, because of how this is used for some ramp jumps. @@ -4073,7 +4073,12 @@ DoneSection2: linespeed = FixedMul(linespeed, mapobjectscale + (player->mo->scale - mapobjectscale)); } - P_InstaThrust(player->mo, lineangle, max(linespeed, newspeed)); + lineangle = K_ReflectAngle( + R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy), lineangle, + playerspeed, linespeed + ); + + P_InstaThrust(player->mo, lineangle, max(linespeed, 2*playerspeed)); player->kartstuff[k_dashpadcooldown] = 2; player->kartstuff[k_pogospring] = 0; From 05ad424cac6fd881acc7c12e1f52c97d720c27b1 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 5 Jun 2020 14:21:15 -0400 Subject: [PATCH 49/65] Use k_floorboost on speed pads to keep the afterimages in-tact --- src/p_spec.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/p_spec.c b/src/p_spec.c index 65dcc4aeb..aa5d52927 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -4052,9 +4052,9 @@ DoneSection2: case 5: // Speed pad w/o spin case 6: // Speed pad w/ spin - if (player->kartstuff[k_dashpadcooldown] != 0) + if (player->kartstuff[k_floorboost] != 0) { - player->kartstuff[k_dashpadcooldown] = 2; + player->kartstuff[k_floorboost] = 2; break; } @@ -4080,8 +4080,9 @@ DoneSection2: P_InstaThrust(player->mo, lineangle, max(linespeed, 2*playerspeed)); - player->kartstuff[k_dashpadcooldown] = 2; + player->kartstuff[k_dashpadcooldown] = TICRATE/3; player->kartstuff[k_pogospring] = 0; + player->kartstuff[k_floorboost] = 2; S_StartSound(player->mo, sfx_cdfm62); } break; From 820d1f3ac643d691b04d6f93fc91b0971ff5fd1d Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 5 Jun 2020 14:52:37 -0400 Subject: [PATCH 50/65] Less obnoxious sound when using multiple boosters --- src/k_kart.c | 15 ++++++++++++++- src/p_map.c | 7 ++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index bd7ab22da..6aa1ee7fc 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3905,7 +3905,20 @@ void K_DoSneaker(player_t *player, INT32 type) if (!player->kartstuff[k_floorboost] || player->kartstuff[k_floorboost] == 3) { - S_StartSound(player->mo, sfx_cdfm01); + const sfxenum_t normalsfx = sfx_cdfm01; + const sfxenum_t smallsfx = sfx_cdfm40; + sfxenum_t sfx = normalsfx; + + if (player->kartstuff[k_speedboost] > (intendedboost/2)) + { + // Use a less annoying sound when the booster will just sustain your current speed. + sfx = smallsfx; + } + + S_StopSoundByID(player->mo, normalsfx); + S_StopSoundByID(player->mo, smallsfx); + S_StartSound(player->mo, sfx); + K_SpawnDashDustRelease(player); if (intendedboost > player->kartstuff[k_speedboost]) player->karthud[khud_destboostcam] = FixedMul(FRACUNIT, FixedDiv((intendedboost - player->kartstuff[k_speedboost]), intendedboost)); diff --git a/src/p_map.c b/src/p_map.c index ae61cd639..4ef258ebb 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -308,7 +308,12 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) { angle_t finalAngle = spring->angle; fixed_t finalSpeed = FixedMul(horizspeed, FixedSqrt(FixedMul(hscale, spring->scale))); - fixed_t objectSpeed = R_PointToDist2(0, 0, savemomx, savemomy); + fixed_t objectSpeed; + + if (object->player) + objectSpeed = object->player->speed; + else + objectSpeed = R_PointToDist2(0, 0, savemomx, savemomy); if (!vertispeed) { From 25c3774dc1eaf00f8b06e4aea9decd088d01ddd2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 5 Jun 2020 15:29:08 -0400 Subject: [PATCH 51/65] Let Sneakers stack with each other endlessly. PROBABLY REALLY CONTROVERSIAL, so it's in this other branch... but it's tamer than it sounds, and it's *really* fun. https://cdn.discordapp.com/attachments/275750804227489794/718541704822784061/kart0158.gif https://cdn.discordapp.com/attachments/275750804227489794/718545963408687155/kart0161.gif https://cdn.discordapp.com/attachments/275750804227489794/718543467847876689/kart0160.gif --- src/d_player.h | 7 ++--- src/dehacked.c | 2 +- src/g_game.c | 4 +-- src/k_kart.c | 70 +++++++++++++++++++++++++++++++++----------------- src/p_inter.c | 1 + src/p_mobj.c | 5 ++-- src/p_user.c | 2 +- 7 files changed, 56 insertions(+), 35 deletions(-) diff --git a/src/d_player.h b/src/d_player.h index def13cc71..8360714be 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -316,8 +316,8 @@ typedef enum k_stealingtimer, // You are stealing an item, this is your timer k_stolentimer, // You are being stolen from, this is your timer k_superring, // Spawn rings on top of you every tic! - k_sneakertimer, // Duration of the Sneaker Boost itself - k_levelbooster, // Duration of a level booster's boost (same as sneaker, but separated for boost stacking) + k_sneakertimer, // Duration of a Sneaker Boost (from Sneakers or level boosters) + k_numsneakers, // Number of stacked sneaker effects k_growshrinktimer, // > 0 = Big, < 0 = small k_squishedtimer, // Squished frame timer k_rocketsneakertimer, // Rocket Sneaker duration timer @@ -391,9 +391,6 @@ typedef enum NUMKARTHUD } karthudtype_t; -// QUICKLY GET EITHER SNEAKER OR LEVEL BOOSTER SINCE THEY ARE FUNCTIONALLY IDENTICAL -#define EITHERSNEAKER(p) (p->kartstuff[k_sneakertimer] || p->kartstuff[k_levelbooster]) - // QUICKLY GET RING TOTAL, INCLUDING RINGS CURRENTLY IN THE PICKUP ANIMATION #define RINGTOTAL(p) (p->kartstuff[k_rings] + p->kartstuff[k_pickuprings]) diff --git a/src/dehacked.c b/src/dehacked.c index 369be7a5c..8564e52db 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8389,7 +8389,7 @@ static const char *const KARTSTUFF_LIST[] = { "STOLENTIMER", "SUPERRING", "SNEAKERTIMER", - "LEVELBOOSTER", + "NUMSNEAKERS", "GROWSHRINKTIMER", "SQUISHEDTIMER", "ROCKETSNEAKERTIMER", diff --git a/src/g_game.c b/src/g_game.c index cca6c50ed..ebbf60e3d 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1430,7 +1430,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { // forward with key or button // SRB2kart - we use an accel/brake instead of forward/backward. axis = JoyAxis(AXISMOVE, ssplayer); - if (InputDown(gc_accelerate, ssplayer) || (gamepadjoystickmove && axis > 0) || EITHERSNEAKER(player)) + if (InputDown(gc_accelerate, ssplayer) || (gamepadjoystickmove && axis > 0) || player->kartstuff[k_sneakertimer]) { cmd->buttons |= BT_ACCELERATE; forward = forwardmove[1]; // 50 @@ -5104,7 +5104,7 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) || (players[displayplayers[0]].respawn.state != RESPAWNST_NONE) // Respawning || (players[displayplayers[0]].spectator || objectplacing)) // Not a physical player && !(players[displayplayers[0]].kartstuff[k_spinouttimer] - && (players[displayplayers[0]].kartstuff[k_sneakertimer] || players[displayplayers[0]].kartstuff[k_levelbooster]))) // Spinning and boosting cancels out spinout + && players[displayplayers[0]].kartstuff[k_sneakertimer])) // Spinning and boosting cancels out spinout localangle[0] += (cmd->angleturn<<16); if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER) diff --git a/src/k_kart.c b/src/k_kart.c index 6aa1ee7fc..b6f4a4744 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -1925,7 +1925,7 @@ void K_MomentumToFacing(player_t *player) boolean K_ApplyOffroad(player_t *player) { - if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || EITHERSNEAKER(player)) + if (player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_hyudorotimer] || player->kartstuff[k_sneakertimer]) return false; return true; } @@ -1962,29 +1962,44 @@ static void K_GetKartBoostPower(player_t *player) accelboost += (a) / numboosts; \ } - if (player->kartstuff[k_levelbooster]) // Level boosters - ADDBOOST(FRACUNIT/2, 8*FRACUNIT); // + 50% top speed, + 800% acceleration - if (player->kartstuff[k_sneakertimer]) // Sneaker - ADDBOOST(FRACUNIT/2, 8*FRACUNIT); // + 50% top speed, + 800% acceleration + { + UINT8 i; + for (i = 0; i < player->kartstuff[k_numsneakers]; i++) + { + ADDBOOST(FRACUNIT/2, 8*FRACUNIT); // + 50% top speed, + 800% acceleration + } + } if (player->kartstuff[k_invincibilitytimer]) // Invincibility - ADDBOOST((3*FRACUNIT)/8, 3*FRACUNIT); // + 37.5% top speed, + 300% acceleration + { + ADDBOOST(3*FRACUNIT/8, 3*FRACUNIT); // + 37.5% top speed, + 300% acceleration + } if (player->kartstuff[k_flamedash]) // Flame Shield dash + { ADDBOOST(K_FlameShieldDashVar(player->kartstuff[k_flamedash]), 3*FRACUNIT); // + infinite top speed, + 300% acceleration + } if (player->kartstuff[k_startboost]) // Startup Boost + { ADDBOOST(FRACUNIT/4, 6*FRACUNIT); // + 25% top speed, + 600% acceleration + } if (player->kartstuff[k_driftboost]) // Drift Boost + { ADDBOOST(FRACUNIT/4, 4*FRACUNIT); // + 25% top speed, + 400% acceleration + } if (player->kartstuff[k_ringboost]) // Ring Boost + { ADDBOOST(FRACUNIT/5, 4*FRACUNIT); // + 20% top speed, + 400% acceleration + } if (player->kartstuff[k_eggmanexplode]) // Ready-to-explode - ADDBOOST(FRACUNIT/5, FRACUNIT); // + 20% top speed, + 100% acceleration + { + ADDBOOST(3*FRACUNIT/20, FRACUNIT); // + 15% top speed, + 100% acceleration + } if (player->kartstuff[k_draftpower] > 0) // Drafting { @@ -1997,9 +2012,13 @@ static void K_GetKartBoostPower(player_t *player) // value smoothing if (speedboost > player->kartstuff[k_speedboost]) + { player->kartstuff[k_speedboost] = speedboost; + } else + { player->kartstuff[k_speedboost] += (speedboost - player->kartstuff[k_speedboost]) / (TICRATE/2); + } player->kartstuff[k_accelboost] = accelboost; player->kartstuff[k_numboosts] = numboosts; @@ -2122,7 +2141,7 @@ fixed_t K_3dKartMovement(player_t *player, boolean onground, fixed_t forwardmove // 0 with no gas, and // -25 when only braking. - if (EITHERSNEAKER(player)) + if (player->kartstuff[k_sneakertimer]) forwardmove = 50; finalspeed *= forwardmove/25; @@ -2206,7 +2225,7 @@ void K_SpinPlayer(player_t *player, mobj_t *source, INT32 type, mobj_t *inflicto K_PlayHitEmSound(source); player->kartstuff[k_sneakertimer] = 0; - //player->kartstuff[k_levelbooster] = 0; + player->kartstuff[k_numsneakers] = 0; player->kartstuff[k_driftboost] = 0; player->kartstuff[k_ringboost] = 0; @@ -2353,7 +2372,7 @@ void K_SquishPlayer(player_t *player, mobj_t *source, mobj_t *inflictor) #endif player->kartstuff[k_sneakertimer] = 0; - player->kartstuff[k_levelbooster] = 0; + player->kartstuff[k_numsneakers] = 0; player->kartstuff[k_driftboost] = 0; player->kartstuff[k_ringboost] = 0; @@ -2480,7 +2499,7 @@ void K_ExplodePlayer(player_t *player, mobj_t *source, mobj_t *inflictor) // A b player->mo->momx = player->mo->momy = 0; player->kartstuff[k_sneakertimer] = 0; - player->kartstuff[k_levelbooster] = 0; + player->kartstuff[k_numsneakers] = 0; player->kartstuff[k_driftboost] = 0; player->kartstuff[k_ringboost] = 0; @@ -3924,7 +3943,7 @@ void K_DoSneaker(player_t *player, INT32 type) player->karthud[khud_destboostcam] = FixedMul(FRACUNIT, FixedDiv((intendedboost - player->kartstuff[k_speedboost]), intendedboost)); } - if (!EITHERSNEAKER(player)) + if (!player->kartstuff[k_sneakertimer]) { if (type == 2) { @@ -3958,10 +3977,11 @@ void K_DoSneaker(player_t *player, INT32 type) { player->pflags |= PF_ATTACKDOWN; K_PlayBoostTaunt(player->mo); - player->kartstuff[k_sneakertimer] = sneakertime; + } - else - player->kartstuff[k_levelbooster] = sneakertime; + + player->kartstuff[k_sneakertimer] = sneakertime; + player->kartstuff[k_numsneakers]++; // set angle for spun out players: player->kartstuff[k_boostangle] = (INT32)player->mo->angle; @@ -4067,7 +4087,7 @@ void K_DoPogoSpring(mobj_t *mo, fixed_t vertispeed, UINT8 sound) thrust = 72<player->kartstuff[k_pogospring] != 2) { - if (EITHERSNEAKER(mo->player)) + if (mo->player->kartstuff[k_sneakertimer]) thrust = FixedMul(thrust, (5*FRACUNIT)/4); else if (mo->player->kartstuff[k_invincibilitytimer]) thrust = FixedMul(thrust, (9*FRACUNIT)/8); @@ -5270,7 +5290,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) if (player->speed > 0) { // Speed lines - if (EITHERSNEAKER(player) || player->kartstuff[k_ringboost] + if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_ringboost] || player->kartstuff[k_driftboost] || player->kartstuff[k_startboost] || player->kartstuff[k_eggmanexplode]) { @@ -5486,7 +5506,7 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) { if ((P_IsObjectOnGround(player->mo) || (player->kartstuff[k_spinouttype] != 0)) - && (!EITHERSNEAKER(player))) + && (!player->kartstuff[k_sneakertimer])) { player->kartstuff[k_spinouttimer]--; if (player->kartstuff[k_wipeoutslow] > 1) @@ -5525,15 +5545,19 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd) player->kartstuff[k_ringboost]--; if (player->kartstuff[k_sneakertimer]) + { player->kartstuff[k_sneakertimer]--; - if (player->kartstuff[k_levelbooster]) - player->kartstuff[k_levelbooster]--; + if (player->kartstuff[k_sneakertimer] <= 0) + { + player->kartstuff[k_numsneakers] = 0; + } + } if (player->kartstuff[k_flamedash]) player->kartstuff[k_flamedash]--; - if (EITHERSNEAKER(player) && player->kartstuff[k_wipeoutslow] > 0 && player->kartstuff[k_wipeoutslow] < wipeoutslowtime+1) + if (player->kartstuff[k_sneakertimer] && player->kartstuff[k_wipeoutslow] > 0 && player->kartstuff[k_wipeoutslow] < wipeoutslowtime+1) player->kartstuff[k_wipeoutslow] = wipeoutslowtime+1; if (player->kartstuff[k_floorboost]) @@ -6226,7 +6250,7 @@ INT16 K_GetKartTurnValue(player_t *player, INT16 turnvalue) return turnvalue; } - if (EITHERSNEAKER(player) || player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_growshrinktimer] > 0) + if (player->kartstuff[k_sneakertimer] || player->kartstuff[k_invincibilitytimer] || player->kartstuff[k_growshrinktimer] > 0) { turnvalue = 5*turnvalue/4; } @@ -6465,7 +6489,7 @@ static void K_KartDrift(player_t *player, boolean onground) player->kartstuff[k_driftend] = 0; } - if ((!EITHERSNEAKER(player)) + if ((!player->kartstuff[k_sneakertimer]) || (!player->cmd.driftturn) || (!player->kartstuff[k_aizdriftstrat]) || (player->cmd.driftturn > 0) != (player->kartstuff[k_aizdriftstrat] > 0)) diff --git a/src/p_inter.c b/src/p_inter.c index 49d3546ce..916f11383 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -3153,6 +3153,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da || inflictor->type == MT_SMK_THWOMP || inflictor->player)) { player->kartstuff[k_sneakertimer] = 0; + player->kartstuff[k_numsneakers] = 0; K_SpinPlayer(player, source, 1, inflictor, false); K_KartPainEnergyFling(player); diff --git a/src/p_mobj.c b/src/p_mobj.c index 935ecb14a..4754172f6 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -8348,10 +8348,9 @@ void P_MobjThinker(mobj_t *mobj) if (p) { - if (p->kartstuff[k_sneakertimer] > mobj->movecount - || p->kartstuff[k_levelbooster] > mobj->movecount) + if (p->kartstuff[k_sneakertimer] > mobj->movecount) P_SetMobjState(mobj, S_BOOSTFLAME); - mobj->movecount = max(p->kartstuff[k_sneakertimer], p->kartstuff[k_levelbooster]); + mobj->movecount = p->kartstuff[k_sneakertimer]; } } diff --git a/src/p_user.c b/src/p_user.c index cc98d46ee..ed31c1d11 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -6251,7 +6251,7 @@ static void P_MovePlayer(player_t *player) //////////////////////////// // SRB2kart - Drifting smoke and fire - if ((EITHERSNEAKER(player) || player->kartstuff[k_flamedash]) + if ((player->kartstuff[k_sneakertimer] || player->kartstuff[k_flamedash]) && onground && (leveltime & 1)) K_SpawnBoostTrail(player); From 9ae8628424520479c15b8e893cb99f42a38b0556 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 5 Jun 2020 18:54:23 -0400 Subject: [PATCH 52/65] mess with total ring calculations --- src/p_user.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/src/p_user.c b/src/p_user.c index 4c8015948..ef2c36090 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1755,15 +1755,18 @@ void P_DoPlayerExit(player_t *player) grandprixinfo.wonround = true; // Increase your total rings - player->totalring += RINGTOTAL(player); - - extra = player->totalring / lifethreshold; - - if (extra > player->xtralife) + if (RINGTOTAL(player) > 0) { - P_GivePlayerLives(player, extra - player->xtralife); - S_StartSound(NULL, sfx_cdfm73); - player->xtralife = extra; + player->totalring += RINGTOTAL(player); + + extra = player->totalring / lifethreshold; + + if (extra > player->xtralife) + { + P_GivePlayerLives(player, extra - player->xtralife); + S_StartSound(NULL, sfx_cdfm73); + player->xtralife = extra; + } } } } From a889898eebdf855ce3bf2518cace2fabeb0658a8 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 5 Jun 2020 18:55:58 -0400 Subject: [PATCH 53/65] Bring back accel rubberbanding, but make it weaker --- src/k_kart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 677e1c2ad..a0d79efc2 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2031,13 +2031,13 @@ fixed_t K_GetKartAccel(player_t *player) //k_accel += 3 * (9 - kartspeed); // 36 - 60 k_accel += 4 * (9 - kartspeed); // 32 - 64 -#if 0 - // Rubberbanding acceleration is disabled since it makes hits feel more meaningful + if (K_PlayerUsesBotMovement(player)) { - k_accel = FixedMul(k_accel, K_BotRubberband(player)); + // Rubberbanding acceleration is waekened since it makes hits feel more meaningful + fixed_t rubberband = K_BotRubberband(player) - FRACUNIT; + k_accel = FixedMul(k_accel, FRACUNIT + (rubberband/2)); } -#endif return FixedMul(k_accel, FRACUNIT+player->kartstuff[k_accelboost]); } From 2a30fc365fc00227b4569b5a568f90b6161b07db Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Fri, 5 Jun 2020 19:29:00 -0400 Subject: [PATCH 54/65] Fix totalring/xtralife getting reset --- src/g_game.c | 3 +++ src/p_setup.c | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/g_game.c b/src/g_game.c index 698a548db..ca3fc4a9e 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2596,6 +2596,7 @@ void G_PlayerReborn(INT32 player) UINT8 botdiffincrease; boolean botrival; SINT8 pity; + SINT8 xtralife; // SRB2kart respawnvars_t respawn; @@ -2646,6 +2647,7 @@ void G_PlayerReborn(INT32 player) botdiffincrease = players[player].botvars.diffincrease; botrival = players[player].botvars.rival; pity = players[player].pity; + xtralife = players[player].xtralife; // SRB2kart if (leveltime <= starttime) @@ -2726,6 +2728,7 @@ void G_PlayerReborn(INT32 player) p->botvars.diffincrease = botdiffincrease; p->botvars.rival = botrival; p->pity = pity; + p->xtralife = xtralife; // SRB2kart p->kartstuff[k_itemroulette] = itemroulette; diff --git a/src/p_setup.c b/src/p_setup.c index cbaef63b4..1071f5da3 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2431,7 +2431,7 @@ static void P_LevelInitStuff(void) players[i].lostlife = false; players[i].gotcontinue = false; - players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0; + players[i].deadtimer = players[i].numboxes = players[i].laps = 0; players[i].health = 1; players[i].aiming = 0; players[i].pflags &= ~PF_TIMEOVER; From d3175f2427f7f1576ce87c202d7564d92d293107 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 6 Jun 2020 00:44:29 -0400 Subject: [PATCH 55/65] Increase rocket sneaker cost --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index b6f4a4744..e7182d707 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6785,7 +6785,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground) { K_DoSneaker(player, 2); K_PlayBoostTaunt(player->mo); - player->kartstuff[k_rocketsneakertimer] -= 2*TICRATE; + player->kartstuff[k_rocketsneakertimer] -= 3*TICRATE; if (player->kartstuff[k_rocketsneakertimer] < 1) player->kartstuff[k_rocketsneakertimer] = 1; } From f701c11daab6a7b53a19c0f5a28011c0efaf4a78 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 6 Jun 2020 02:08:46 -0400 Subject: [PATCH 56/65] Don't increment numsneakers too much on floor boosts --- src/k_kart.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index e7182d707..079fb32ea 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3941,6 +3941,8 @@ void K_DoSneaker(player_t *player, INT32 type) K_SpawnDashDustRelease(player); if (intendedboost > player->kartstuff[k_speedboost]) player->karthud[khud_destboostcam] = FixedMul(FRACUNIT, FixedDiv((intendedboost - player->kartstuff[k_speedboost]), intendedboost)); + + player->kartstuff[k_numsneakers]++; } if (!player->kartstuff[k_sneakertimer]) @@ -3981,7 +3983,6 @@ void K_DoSneaker(player_t *player, INT32 type) } player->kartstuff[k_sneakertimer] = sneakertime; - player->kartstuff[k_numsneakers]++; // set angle for spun out players: player->kartstuff[k_boostangle] = (INT32)player->mo->angle; From 009b47bd6a5a74598f2872cbf03bfacc1ac1efbc Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 6 Jun 2020 03:04:30 -0400 Subject: [PATCH 57/65] Use the alt sound when stacking sneakers --- src/k_kart.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 079fb32ea..7d341d5c5 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -3928,9 +3928,9 @@ void K_DoSneaker(player_t *player, INT32 type) const sfxenum_t smallsfx = sfx_cdfm40; sfxenum_t sfx = normalsfx; - if (player->kartstuff[k_speedboost] > (intendedboost/2)) + if (player->kartstuff[k_numsneakers]) { - // Use a less annoying sound when the booster will just sustain your current speed. + // Use a less annoying sound when stacking sneakers. sfx = smallsfx; } From dc1ae10cd8cc75dd87af663628be3da5f6b01b03 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 6 Jun 2020 16:06:55 -0400 Subject: [PATCH 58/65] Combine loops --- src/k_grandprix.c | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index afecf5989..09f61252b 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -516,20 +516,7 @@ void K_FakeBotResults(player_t *bot) { besttime = players[i].realtime; } - } - } - if (besttime == UINT32_MAX) - { - // No one finished, so you don't finish either. - bot->pflags |= PF_TIMEOVER; - return; - } - - for (i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i] && !players[i].spectator) - { if (players[i].distancetofinish > worstdist) { worstdist = players[i].distancetofinish; @@ -537,9 +524,9 @@ void K_FakeBotResults(player_t *bot) } } - if (bot->distancetofinish >= worstdist) + if (besttime == UINT32_MAX // No one finished, so you don't finish either. + || bot->distancetofinish >= worstdist) // Last place, you aren't going to finish. { - // Last place, you aren't going to finish. bot->pflags |= PF_TIMEOVER; return; } From 76b41f5a3b0d7a1426c6f56da40a414980bb7bd2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 6 Jun 2020 16:14:39 -0400 Subject: [PATCH 59/65] Remove capping --- src/k_kart.c | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index a0d79efc2..65cf8af15 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9732,6 +9732,7 @@ static void K_drawKartPlayerCheck(void) { player_t *checkplayer = &players[i]; fixed_t distance = maxdistance+1; + UINT8 *colormap = NULL; UINT8 pnum = 0; fixed_t x = 0; vertex_t v; @@ -9782,21 +9783,8 @@ static void K_drawKartPlayerCheck(void) K_ObjectTracking(&x, NULL, &c, thiscam->angle + ANGLE_180, 0, cnum, &v); - if (x <= 320*FRACUNIT && x >= 0) - { - UINT8 *colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE); - - if (x < 14*FRACUNIT) - { - x = 14*FRACUNIT; - } - else if (x > 306*FRACUNIT) - { - x = 306*FRACUNIT; - } - - V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|splitflags, kp_check[pnum], colormap); - } + colormap = R_GetTranslationColormap(TC_DEFAULT, checkplayer->mo->color, GTC_CACHE); + V_DrawFixedPatch(x, CHEK_Y * FRACUNIT, FRACUNIT, V_HUDTRANS|splitflags, kp_check[pnum], colormap); } } From 136bb1d252bd295d75258f06a60c96b9f2a6a528 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 6 Jun 2020 19:45:13 -0400 Subject: [PATCH 60/65] Add K_CanChangeRules, and start a "single session" GP when warping to a map in single player --- src/d_main.c | 61 ++++++++++++++--- src/d_netcmd.c | 170 +++++++++++++++++++++++++++++++++++----------- src/g_game.c | 26 ++++--- src/k_grandprix.c | 38 ++++++++++- src/k_grandprix.h | 19 +++++- src/k_pwrlv.c | 2 +- src/m_menu.c | 1 + src/p_inter.c | 2 +- src/p_setup.c | 6 +- src/p_user.c | 2 +- src/y_inter.c | 2 +- 11 files changed, 262 insertions(+), 67 deletions(-) diff --git a/src/d_main.c b/src/d_main.c index c52749504..fe7e312ee 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -1136,7 +1136,24 @@ void D_SRB2Main(void) else { if (!M_CheckParm("-server")) + { G_SetGameModified(true, true); + + // Start up a "minor" grand prix session + memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); + + grandprixinfo.gamespeed = KARTSPEED_NORMAL; + grandprixinfo.encore = false; + grandprixinfo.masterbots = false; + + grandprixinfo.gp = true; + grandprixinfo.roundnum = 0; + grandprixinfo.cup = NULL; + grandprixinfo.wonround = false; + + grandprixinfo.initalize = true; + } + autostart = true; } } @@ -1519,18 +1536,46 @@ void D_SRB2Main(void) INT16 newskill = -1; const char *sskill = M_GetNextParm(); - for (j = 0; kartspeed_cons_t[j].strvalue; j++) - if (!strcasecmp(kartspeed_cons_t[j].strvalue, sskill)) + const UINT8 master = KARTSPEED_HARD+1; + const char *masterstr = "Master"; + + if (!strcasecmp(masterstr, sskill)) + { + newskill = master; + } + else + { + for (j = 0; kartspeed_cons_t[j].strvalue; j++) { - newskill = (INT16)kartspeed_cons_t[j].value; - break; + if (!strcasecmp(kartspeed_cons_t[j].strvalue, sskill)) + { + newskill = (INT16)kartspeed_cons_t[j].value; + break; + } } - if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match + if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match + { + j = atoi(sskill); // assume they gave us a skill number, which is okay too + if (j >= KARTSPEED_EASY && j <= master) + newskill = (INT16)j; + } + } + + if (grandprixinfo.gp == true) { - j = atoi(sskill); // assume they gave us a skill number, which is okay too - if (j >= KARTSPEED_EASY && j <= KARTSPEED_HARD) - newskill = (INT16)j; + if (newskill == master) + { + grandprixinfo.masterbots = true; + newskill = KARTSPEED_HARD; + } + + grandprixinfo.gamespeed = newskill; + } + else if (newskill == master) + { + grandprixinfo.masterbots = true; + newskill = KARTSPEED_HARD; } if (newskill != -1) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 2ac42910f..3ce214aab 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2769,7 +2769,7 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pencoremode, boolean r if (netgame || multiplayer) FLS = false; - if (grandprixinfo.roundnum != 0) + if (grandprixinfo.gp == true) { // Too lazy to change the input value for every instance of this function....... pencoremode = grandprixinfo.encore; @@ -2922,6 +2922,7 @@ static void Command_Map_f(void) INT32 newmapnum; boolean newresetplayers, newencoremode; INT32 newgametype = gametype; + boolean startgp = false; // max length of command: map map03 -gametype race -noresetplayers -force -encore // 1 2 3 4 5 6 7 @@ -2952,6 +2953,7 @@ static void Command_Map_f(void) if (COM_CheckParm("-force")) { G_SetGameModified(false, true); + startgp = true; } else { @@ -3044,6 +3046,69 @@ static void Command_Map_f(void) return; } + if (startgp) + { + i = COM_CheckParm("-skill"); + + grandprixinfo.gamespeed = (cv_kartspeed.value == KARTSPEED_AUTO ? KARTSPEED_NORMAL : cv_kartspeed.value); + grandprixinfo.masterbots = false; + + if (i) + { + const UINT8 master = KARTSPEED_HARD+1; + const char *masterstr = "Master"; + const char *skillname = COM_Argv(i+1); + INT32 newskill = -1; + INT32 j; + + if (!strcasecmp(masterstr, skillname)) + { + newskill = master; + } + else + { + for (j = 0; kartspeed_cons_t[j].strvalue; j++) + { + if (!strcasecmp(kartspeed_cons_t[j].strvalue, skillname)) + { + newskill = (INT16)kartspeed_cons_t[j].value; + break; + } + } + + if (!kartspeed_cons_t[j].strvalue) // reached end of the list with no match + { + j = atoi(COM_Argv(i+1)); // assume they gave us a skill number, which is okay too + if (j >= KARTSPEED_EASY && j <= master) + newskill = (INT16)j; + } + } + + if (newskill != -1) + { + if (newskill == master) + { + grandprixinfo.gamespeed = KARTSPEED_HARD; + grandprixinfo.masterbots = true; + } + else + { + grandprixinfo.gamespeed = newskill; + grandprixinfo.masterbots = false; + } + } + } + + grandprixinfo.encore = newencoremode; + + grandprixinfo.gp = true; + grandprixinfo.roundnum = 0; + grandprixinfo.cup = NULL; + grandprixinfo.wonround = false; + + grandprixinfo.initalize = true; + } + fromlevelselect = false; D_MapChange(newmapnum, newgametype, newencoremode, newresetplayers, 0, false, false); } @@ -5067,14 +5132,16 @@ static void PointLimit_OnChange(void) static void NumLaps_OnChange(void) { - if (!G_RaceGametype() || (modeattacking || demo.playback)) + if (K_CanChangeRules() == false) + { return; + } - if (server && Playing() - && (netgame || multiplayer) - && (mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) - && (cv_numlaps.value > mapheaderinfo[gamemap - 1]->numlaps)) + if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) + && (cv_numlaps.value > mapheaderinfo[gamemap - 1]->numlaps)) + { CV_StealthSetValue(&cv_numlaps, mapheaderinfo[gamemap - 1]->numlaps); + } // Just don't be verbose CONS_Printf(M_GetText("Number of laps set to %d\n"), cv_numlaps.value); @@ -5707,7 +5774,7 @@ void Command_Retry_f(void) { CONS_Printf(M_GetText("You must be in a level to use this.\n")); } - else if (netgame || grandprixinfo.roundnum == 0) + else if (grandprixinfo.gp == false) { CONS_Printf(M_GetText("This only works in Grand Prix.\n")); } @@ -6201,24 +6268,35 @@ static void Command_ShowTime_f(void) // SRB2Kart: On change messages static void BaseNumLaps_OnChange(void) { - if (gamestate == GS_LEVEL && grandprixinfo.roundnum == 0) + if (K_CanChangeRules() == true) { - if (cv_basenumlaps.value) - CONS_Printf(M_GetText("Number of laps will be changed to %d next round.\n"), cv_basenumlaps.value); - else - CONS_Printf(M_GetText("Number of laps will be changed to map defaults next round.\n")); + const char *str = va("%d", cv_basenumlaps.value); + + if (cv_basenumlaps.value == 0) + { + str = "map defaults"; + } + + CONS_Printf(M_GetText("Number of laps will be changed to %s next round.\n"), str); } } static void KartFrantic_OnChange(void) { - if ((boolean)cv_kartfrantic.value != franticitems && gamestate == GS_LEVEL && leveltime > starttime) - CONS_Printf(M_GetText("Frantic items will be turned %s next round.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off")); + if (K_CanChangeRules() == false) + { + return; + } + + if (leveltime < starttime) + { + CONS_Printf(M_GetText("Frantic items has been set to %s.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off")); + franticitems = (boolean)cv_kartfrantic.value; + } else { - CONS_Printf(M_GetText("Frantic items has been turned %s.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off")); - franticitems = (boolean)cv_kartfrantic.value; + CONS_Printf(M_GetText("Frantic items will be turned %s next round.\n"), cv_kartfrantic.value ? M_GetText("on") : M_GetText("off")); } } @@ -6231,47 +6309,59 @@ static void KartSpeed_OnChange(void) return; } - if (G_RaceGametype() && grandprixinfo.roundnum == 0) + if (K_CanChangeRules() == false) { - if ((gamestate == GS_LEVEL && leveltime < starttime) && (cv_kartspeed.value != KARTSPEED_AUTO)) - { - CONS_Printf(M_GetText("Game speed has been changed to \"%s\".\n"), cv_kartspeed.string); - gamespeed = (UINT8)cv_kartspeed.value; - } - else if (cv_kartspeed.value != (signed)gamespeed) - { - CONS_Printf(M_GetText("Game speed will be changed to \"%s\" next round.\n"), cv_kartspeed.string); - } + return; + } + + if (leveltime < starttime && cv_kartspeed.value != KARTSPEED_AUTO) + { + CONS_Printf(M_GetText("Game speed has been changed to \"%s\".\n"), cv_kartspeed.string); + gamespeed = (UINT8)cv_kartspeed.value; + } + else + { + CONS_Printf(M_GetText("Game speed will be changed to \"%s\" next round.\n"), cv_kartspeed.string); } } static void KartEncore_OnChange(void) { - if (G_RaceGametype() && grandprixinfo.roundnum == 0) + if (K_CanChangeRules() == false) { - if ((cv_kartencore.value == 1) != encoremode && gamestate == GS_LEVEL /*&& leveltime > starttime*/) - CONS_Printf(M_GetText("Encore Mode will be set to %s next round.\n"), cv_kartencore.string); - else - CONS_Printf(M_GetText("Encore Mode has been set to %s.\n"), cv_kartencore.string); + return; } + + CONS_Printf(M_GetText("Encore Mode will be set to %s next round.\n"), cv_kartencore.string); } static void KartComeback_OnChange(void) { - if (G_BattleGametype()) + if (K_CanChangeRules() == false) { - if ((boolean)cv_kartcomeback.value != comeback && gamestate == GS_LEVEL && leveltime > starttime) - CONS_Printf(M_GetText("Karma Comeback will be turned %s next round.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off")); - else - { - CONS_Printf(M_GetText("Karma Comeback has been turned %s.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off")); - comeback = (boolean)cv_kartcomeback.value; - } + return; + } + + if (leveltime < starttime) + { + CONS_Printf(M_GetText("Karma Comeback has been turned %s.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off")); + comeback = (boolean)cv_kartcomeback.value; + } + else + { + CONS_Printf(M_GetText("Karma Comeback will be turned %s next round.\n"), cv_kartcomeback.value ? M_GetText("on") : M_GetText("off")); } } static void KartEliminateLast_OnChange(void) { - if (G_RaceGametype() && cv_karteliminatelast.value && grandprixinfo.roundnum == 0) + if (K_CanChangeRules() == false) + { + CV_StealthSet(&cv_karteliminatelast, cv_karteliminatelast.defaultvalue); + } + + if (G_RaceGametype()) + { P_CheckRacers(); + } } diff --git a/src/g_game.c b/src/g_game.c index ca3fc4a9e..617905a55 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -3220,7 +3220,7 @@ void G_ExitLevel(void) { if (gamestate == GS_LEVEL) { - if (grandprixinfo.roundnum > 0 && grandprixinfo.wonround != true) + if (grandprixinfo.gp == true && grandprixinfo.wonround != true) { UINT8 i; @@ -3336,7 +3336,7 @@ boolean G_IsSpecialStage(INT32 mapnum) // boolean G_GametypeUsesLives(void) { - if ((grandprixinfo.roundnum > 0) // In Grand Prix + if ((grandprixinfo.gp == true) // In Grand Prix && (gametype == GT_RACE) // NOT in bonus round && !(modeattacking) // NOT in Record Attack && !G_IsSpecialStage(gamemap)) // NOT in special stage @@ -3734,16 +3734,24 @@ static void G_DoCompleted(void) { nextmap = (INT16)(nextmapoverride-1); } - else if (grandprixinfo.roundnum != 0) + else if (grandprixinfo.gp == true) { - if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) + if (grandprixinfo.roundnum == 0 || grandprixinfo.cup == NULL) // Single session { - nextmap = 1101; // ceremonymap + nextmap = prevmap; // Same map } else { - nextmap = grandprixinfo.cup->levellist[grandprixinfo.roundnum]; - grandprixinfo.roundnum++; + if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map + { + nextmap = 1101; // ceremonymap + } + else + { + // Proceed to next map + nextmap = grandprixinfo.cup->levellist[grandprixinfo.roundnum]; + grandprixinfo.roundnum++; + } } } else @@ -3759,7 +3767,7 @@ static void G_DoCompleted(void) // a map of the proper gametype -- skip levels that don't support // the current gametype. (Helps avoid playing boss levels in Race, // for instance). - if (!modeattacking && (grandprixinfo.roundnum == 0) && (nextmap >= 0 && nextmap < NUMMAPS)) + if (!modeattacking && grandprixinfo.gp == false && (nextmap >= 0 && nextmap < NUMMAPS)) { register INT16 cm = nextmap; INT16 tolflag = G_TOLFlag(gametype); @@ -3883,7 +3891,7 @@ void G_NextLevel(void) { if (gamestate != GS_VOTING) { - if ((cv_advancemap.value == 3) && grandprixinfo.roundnum == 0 && !modeattacking && !skipstats && (multiplayer || netgame)) + if ((cv_advancemap.value == 3) && grandprixinfo.gp == false && !modeattacking && !skipstats && (multiplayer || netgame)) { UINT8 i; for (i = 0; i < MAXPLAYERS; i++) diff --git a/src/k_grandprix.c b/src/k_grandprix.c index 09f61252b..a4882147a 100644 --- a/src/k_grandprix.c +++ b/src/k_grandprix.c @@ -280,11 +280,17 @@ static INT16 K_RivalScore(player_t *bot) { const UINT16 difficulty = bot->botvars.difficulty; const UINT16 score = bot->score; - const SINT8 roundsleft = grandprixinfo.cup->numlevels - grandprixinfo.roundnum; + SINT8 roundnum = 1, roundsleft = 1; UINT16 lowestscore = UINT16_MAX; UINT8 lowestdifficulty = MAXBOTDIFFICULTY; UINT8 i; + if (grandprixinfo.cup != NULL) + { + roundnum = grandprixinfo.roundnum; + roundsleft = grandprixinfo.cup->numlevels - grandprixinfo.roundnum; + } + for (i = 0; i < MAXPLAYERS; i++) { if (!playeringame[i] || players[i].spectator) @@ -307,7 +313,7 @@ static INT16 K_RivalScore(player_t *bot) // This will try to influence the higher difficulty bots to get rival more often & get even more points. // However, when we're running low on matches left, we need to focus more on raw score! - return ((difficulty - lowestdifficulty) * roundsleft) + ((score - lowestscore) * grandprixinfo.roundnum); + return ((difficulty - lowestdifficulty) * roundsleft) + ((score - lowestscore) * roundnum); } /*-------------------------------------------------- @@ -569,3 +575,31 @@ void K_PlayerLoseLife(player_t *player) } #endif } + +/*-------------------------------------------------- + boolean K_CanChangeRules(void) + + See header file for description. +--------------------------------------------------*/ +boolean K_CanChangeRules(void) +{ + if (demo.playback) + { + // We've already got our important settings! + return false; + } + + if (grandprixinfo.gp == true && grandprixinfo.roundnum > 0) + { + // Don't cheat the rules of the GP! + return false; + } + + if (modeattacking == true) + { + // Don't cheat the rules of Time Trials! + return false; + } + + return true; +} diff --git a/src/k_grandprix.h b/src/k_grandprix.h index 3deccabd1..9f27b485b 100644 --- a/src/k_grandprix.h +++ b/src/k_grandprix.h @@ -18,7 +18,8 @@ extern struct grandprixinfo { - UINT8 roundnum; ///< Round number -- if 0, then we're not in a Grand Prix. + boolean gp; ///< If true, then we are in a Grand Prix. + UINT8 roundnum; ///< Round number. If 0, this is a single session from the warp command. cupheader_t *cup; ///< Which cup are we playing? UINT8 gamespeed; ///< Copy of gamespeed, just to make sure you can't cheat it with cvars boolean encore; ///< Ditto, but for encore mode @@ -124,4 +125,20 @@ void K_FakeBotResults(player_t *bot); void K_PlayerLoseLife(player_t *player); + +/*-------------------------------------------------- + boolean K_CanChangeRules(void); + + Returns whenver or not the server is allowed + to change the game rules. + + Input Arguments:- + None + + Return:- + None +--------------------------------------------------*/ + +boolean K_CanChangeRules(void); + #endif diff --git a/src/k_pwrlv.c b/src/k_pwrlv.c index ee7c28849..eac664408 100644 --- a/src/k_pwrlv.c +++ b/src/k_pwrlv.c @@ -30,7 +30,7 @@ SINT8 K_UsingPowerLevels(void) { SINT8 pt = PWRLV_DISABLED; - if (!cv_kartusepwrlv.value || !netgame || grandprixinfo.roundnum > 0) + if (!cv_kartusepwrlv.value || !netgame || grandprixinfo.gp == true) { return PWRLV_DISABLED; } diff --git a/src/m_menu.c b/src/m_menu.c index 72efa88a5..16cef2eb9 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -7649,6 +7649,7 @@ static void M_StartGrandPrix(INT32 choice) grandprixinfo.cup = gpcup; + grandprixinfo.gp = true; grandprixinfo.roundnum = 1; grandprixinfo.wonround = false; diff --git a/src/p_inter.c b/src/p_inter.c index c34673d80..cb662f482 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2028,7 +2028,7 @@ boolean P_CheckRacers(void) } else { - if (grandprixinfo.roundnum > 0) + if (grandprixinfo.gp == true) { // Always do this in GP eliminatelast = true; diff --git a/src/p_setup.c b/src/p_setup.c index 1071f5da3..e9a25dd84 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2418,7 +2418,7 @@ static void P_LevelInitStuff(void) for (i = 0; i < MAXPLAYERS; i++) { - if (grandprixinfo.roundnum == 0) + if (grandprixinfo.gp == false) { players[i].lives = 3; players[i].xtralife = 0; @@ -2465,7 +2465,7 @@ static void P_LevelInitStuff(void) } // SRB2Kart: map load variables - if (grandprixinfo.roundnum != 0) + if (grandprixinfo.gp == true) { if (G_BattleGametype()) { @@ -3389,7 +3389,7 @@ boolean P_SetupLevel(boolean skipprecip) // NOW you can try to spawn in the Battle capsules, if there's not enough players for a match K_SpawnBattleCapsules(); - if (grandprixinfo.roundnum != 0) + if (grandprixinfo.gp == true) { if (grandprixinfo.initalize == true) { diff --git a/src/p_user.c b/src/p_user.c index ef2c36090..efea837aa 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1739,7 +1739,7 @@ void P_DoPlayerExit(player_t *player) else player->exiting = raceexittime+2; // Accidental death safeguard??? - if (grandprixinfo.roundnum > 0) + if (grandprixinfo.gp == true) { if (player->bot) { diff --git a/src/y_inter.c b/src/y_inter.c index 7121bf404..c216497af 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -645,7 +645,7 @@ void Y_IntermissionDrawer(void) } dotimer: - if (timer && grandprixinfo.roundnum == 0) + if (timer && grandprixinfo.gp == false) { char *string; INT32 tickdown = (timer+1)/TICRATE; From 241fb0db6cb4ba53cd4a73cfa3f60cabb1ddf46a Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 6 Jun 2020 20:11:59 -0400 Subject: [PATCH 61/65] +10% speed buff for rivals --- src/k_kart.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/k_kart.c b/src/k_kart.c index 65cf8af15..725f4aaec 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -2001,6 +2001,12 @@ fixed_t K_GetKartSpeed(player_t *player, boolean doboostpower) { // Give top speed a buff for bots, since it's a fairly weak stat without drifting fixed_t speedmul = ((kartspeed-1) * FRACUNIT / 8) / 10; // +10% for speed 9 + + if (player->botvars.rival == true) + { + speedmul += FRACUNIT/10; // +10% for rival + } + finalspeed = FixedMul(finalspeed, FRACUNIT + speedmul); } From 52d95d3cd71ec9d8aa8590d02c6e5a14aee97aa6 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sat, 6 Jun 2020 23:51:32 -0400 Subject: [PATCH 62/65] Move where wonround is changed Might avoid bots from being updated multiple times --- src/p_setup.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/p_setup.c b/src/p_setup.c index e9a25dd84..655b667fd 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2375,8 +2375,6 @@ static void P_LevelInitStuff(void) memset(localaiming, 0, sizeof(localaiming)); - grandprixinfo.wonround = false; - // special stage tokens, emeralds, and ring total tokenbits = 0; runemeraldmanager = false; @@ -3396,9 +3394,10 @@ boolean P_SetupLevel(boolean skipprecip) K_InitGrandPrixBots(); grandprixinfo.initalize = false; } - else + else if (grandprixinfo.wonround == true) { K_UpdateGrandPrixBots(); + grandprixinfo.wonround = false; } } else if (!modeattacking) From fbeb52f841d81c78456c60ba3b179727ed03c7f5 Mon Sep 17 00:00:00 2001 From: James R Date: Sat, 6 Jun 2020 22:00:45 -0700 Subject: [PATCH 63/65] Don't play Invincbility or Grow music while spectating players --- src/k_kart.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/k_kart.c b/src/k_kart.c index 7d341d5c5..e9641a544 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -6852,9 +6852,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground) P_SetScale(overlay, player->mo->scale); } player->kartstuff[k_invincibilitytimer] = itemtime+(2*TICRATE); // 10 seconds - if (P_IsDisplayPlayer(player)) + if (P_IsLocalPlayer(player)) S_ChangeMusicSpecial("kinvnc"); - else + if (! P_IsDisplayPlayer(player)) S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kinvnc)); P_RestoreMusic(player); K_PlayPowerGloatSound(player->mo); @@ -7056,9 +7056,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground) if (cv_kartdebugshrink.value && !modeattacking && !player->bot) player->mo->destscale = (6*player->mo->destscale)/8; player->kartstuff[k_growshrinktimer] = itemtime+(4*TICRATE); // 12 seconds - if (P_IsDisplayPlayer(player)) + if (P_IsLocalPlayer(player)) S_ChangeMusicSpecial("kgrow"); - else + if (! P_IsDisplayPlayer(player)) S_StartSound(player->mo, (cv_kartinvinsfx.value ? sfx_alarmg : sfx_kgrow)); P_RestoreMusic(player); S_StartSound(player->mo, sfx_kc5a); From 97dfa2439c15fbc3a6554057c7b17d4966c545de Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 7 Jun 2020 01:35:13 -0400 Subject: [PATCH 64/65] Show two people behind, for lookback --- src/k_kart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_kart.c b/src/k_kart.c index ff2ac0f63..205e92a02 100644 --- a/src/k_kart.c +++ b/src/k_kart.c @@ -9997,7 +9997,7 @@ static void K_drawKartNameTags(void) } else if (netgame) { - if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-1) + if ((ntplayer->kartstuff[k_position] >= stplyr->kartstuff[k_position]-2) && (ntplayer->kartstuff[k_position] <= stplyr->kartstuff[k_position]+2)) { INT32 namelen = V_ThinStringWidth(player_names[i], V_6WIDTHSPACE|V_ALLOWLOWERCASE); From 559b879a283876d03869564951c8d41152c877e2 Mon Sep 17 00:00:00 2001 From: Sally Coolatta Date: Sun, 7 Jun 2020 03:41:56 -0400 Subject: [PATCH 65/65] move bot ticcmd to SV_Maketic --- src/d_clisrv.c | 33 +++++++++++++++++++++++++++++++-- src/g_game.c | 12 +++++++----- src/k_bot.c | 2 +- 3 files changed, 39 insertions(+), 8 deletions(-) diff --git a/src/d_clisrv.c b/src/d_clisrv.c index d9d124840..4253532b4 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -5274,20 +5274,29 @@ static void Local_Maketic(INT32 realtics) // game responder calls HU_Responder, AM_Responder, F_Responder, // and G_MapEventsToControls if (!dedicated) rendergametic = gametic; + // translate inputs (keyboard/mouse/joystick) into game controls + G_BuildTiccmd(&localcmds, realtics, 1); + localcmds.angleturn |= TICCMD_RECEIVED; + if (splitscreen) { G_BuildTiccmd(&localcmds2, realtics, 2); + localcmds2.angleturn |= TICCMD_RECEIVED; + if (splitscreen > 1) { G_BuildTiccmd(&localcmds3, realtics, 3); + localcmds3.angleturn |= TICCMD_RECEIVED; + if (splitscreen > 2) + { G_BuildTiccmd(&localcmds4, realtics, 4); + localcmds4.angleturn |= TICCMD_RECEIVED; + } } } - - localcmds.angleturn |= TICCMD_RECEIVED; } void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle) @@ -5324,11 +5333,30 @@ void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle) static void SV_Maketic(void) { INT32 j; + boolean b[MAXPLAYERS]; + + memset(b, false, sizeof (b)); + + for (j = 0; j < MAXPLAYERS; j++) + { + if (K_PlayerUsesBotMovement(&players[j])) + { + b[j] = true; + K_BuildBotTiccmd(&players[j], &netcmds[maketic%TICQUEUE][j]); + } + } for (j = 0; j < MAXNETNODES; j++) + { if (playerpernode[j]) { INT32 player = nodetoplayer[j]; + + if (b[player]) + { + continue; + } + if ((netcmds[maketic%TICQUEUE][player].angleturn & TICCMD_RECEIVED) == 0) { // we didn't receive this tic INT32 i; @@ -5349,6 +5377,7 @@ static void SV_Maketic(void) } } } + } // all tic are now proceed make the next maketic++; diff --git a/src/g_game.c b/src/g_game.c index 0c0564175..2df3be779 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2401,16 +2401,18 @@ void G_Ticker(boolean run) if (playeringame[i]) { - if (K_PlayerUsesBotMovement(&players[i])) + G_CopyTiccmd(cmd, &netcmds[buf][i], 1); + + // Use the leveltime sent in the player's ticcmd to determine control lag + if (modeattacking || K_PlayerUsesBotMovement(&players[i])) { - K_BuildBotTiccmd(&players[i], cmd); + // Never has lag cmd->latency = 0; } else { - G_CopyTiccmd(cmd, &netcmds[buf][i], 1); - // Use the leveltime sent in the player's ticcmd to determine control lag - cmd->latency = modeattacking ? 0 : min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1); //@TODO add a cvar to allow setting this max + //@TODO add a cvar to allow setting this max + cmd->latency = min(((leveltime & 0xFF) - cmd->latency) & 0xFF, MAXPREDICTTICS-1); } } } diff --git a/src/k_bot.c b/src/k_bot.c index ebc1b4869..933f6d37e 100644 --- a/src/k_bot.c +++ b/src/k_bot.c @@ -675,7 +675,7 @@ void K_BuildBotTiccmd(player_t *player, ticcmd_t *cmd) // Remove any existing controls memset(cmd, 0, sizeof(ticcmd_t)); - cmd->angleturn = (player->mo->angle >> 16) | TICCMD_RECEIVED; + cmd->angleturn = (player->mo->angle >> 16); if (gamestate != GS_LEVEL) {