mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2026-01-26 08:26:31 +00:00
Merge remote-tracking branch 'origin/master' into frey
This commit is contained in:
commit
a11602869f
43 changed files with 655 additions and 292 deletions
|
|
@ -1512,6 +1512,8 @@ void CONS_Printf(const char *fmt, ...)
|
|||
vsprintf(txt, fmt, argptr);
|
||||
va_end(argptr);
|
||||
|
||||
Lock_state();
|
||||
|
||||
// echo console prints to log file
|
||||
DEBFILE(txt);
|
||||
|
||||
|
|
@ -1521,8 +1523,6 @@ void CONS_Printf(const char *fmt, ...)
|
|||
|
||||
CON_LogMessage(txt);
|
||||
|
||||
Lock_state();
|
||||
|
||||
// make sure new text is visible
|
||||
con_scrollup = 0;
|
||||
|
||||
|
|
|
|||
|
|
@ -359,6 +359,7 @@ consvar_t cv_drawdist_precip = Player("drawdist_precip", "1024").values({
|
|||
{0, "None"},
|
||||
});
|
||||
|
||||
consvar_t cv_drawinput = Player("drawinput", "No").yes_no();
|
||||
consvar_t cv_ffloorclip = Player("ffloorclip", "On").on_off();
|
||||
|
||||
consvar_t cv_fpscap = Player("fpscap", "Match refresh rate").values({
|
||||
|
|
@ -445,7 +446,6 @@ consvar_t cv_showfocuslost = Player("showfocuslost", "Yes").yes_no();
|
|||
void R_SetViewSize(void);
|
||||
consvar_t cv_showhud = Player("showhud", "Yes").yes_no().onchange(R_SetViewSize).dont_save();
|
||||
|
||||
consvar_t cv_showinputjoy = Player("showinputjoy", "Off").on_off().dont_save();
|
||||
consvar_t cv_skybox = Player("skybox", "On").on_off();
|
||||
|
||||
// Display song credits
|
||||
|
|
@ -804,6 +804,7 @@ consvar_t cv_capsuletest = OnlineCheat("capsuletest", "Off").values(capsuletest_
|
|||
|
||||
consvar_t cv_debugcheese = OnlineCheat("debugcheese", "Off").on_off().description("Disable checks that prevent farming item boxes");
|
||||
consvar_t cv_debugencorevote = OnlineCheat("debugencorevote", "Off").on_off().description("Force encore choice to appear on vote screen");
|
||||
consvar_t cv_debuglapcheat = OnlineCheat("debuglapcheat", "Off").on_off().description("Permit far waypoint jumps and disable lap cheat prevention");
|
||||
consvar_t cv_forcebots = OnlineCheat("forcebots", "No").yes_no().description("Force bots to appear, even in wrong game modes");
|
||||
|
||||
void ForceSkin_OnChange(void);
|
||||
|
|
@ -816,6 +817,8 @@ consvar_t cv_kartdebugbots = OnlineCheat("debugbots", "Off").on_off().descriptio
|
|||
consvar_t cv_kartdebugdistribution = OnlineCheat("debugitemodds", "Off").on_off().description("Show items that the roulette can roll");
|
||||
consvar_t cv_kartdebughuddrop = OnlineCheat("debugitemdrop", "Off").on_off().description("Players drop paper items when damaged in any way");
|
||||
|
||||
consvar_t cv_kartdebugbotwhip = OnlineCheat("debugbotwhip", "Off").on_off().description("Disable bot ring and item pickups");
|
||||
|
||||
extern CV_PossibleValue_t kartdebugitem_cons_t[];
|
||||
consvar_t cv_kartdebugitem = OnlineCheat("debugitem", "None").values(kartdebugitem_cons_t).description("Force item boxes to only roll one kind of item");
|
||||
|
||||
|
|
@ -892,7 +895,6 @@ consvar_t cv_debugrender_spriteclip = PlayerCheat("debugrender_spriteclip", "Off
|
|||
consvar_t cv_debugrender_visplanes = PlayerCheat("debugrender_visplanes", "Off").on_off().description("Highlight the number of visplanes");
|
||||
consvar_t cv_devmode_screen = PlayerCheat("devmode_screen", "1").min_max(1, 4).description("Choose which splitscreen player devmode applies to");
|
||||
consvar_t cv_drawpickups = PlayerCheat("drawpickups", "Yes").yes_no().description("Hide rings, spheres, item capsules, prison capsules (visual only)");
|
||||
consvar_t cv_drawinput = PlayerCheat("drawinput", "No").yes_no().description("Draw turn inputs outside of Record Attack (turn solver debugging)");
|
||||
|
||||
void lua_profile_OnChange(void);
|
||||
consvar_t cv_lua_profile = PlayerCheat("lua_profile", "0").values(CV_Unsigned).onchange(lua_profile_OnChange).description("Show hook timings over an average of N tics");
|
||||
|
|
|
|||
|
|
@ -278,12 +278,6 @@ void D_ProcessEvents(void)
|
|||
|
||||
HandleGamepadDeviceEvents(ev);
|
||||
|
||||
if (demo.savemode == demovars_s::DSM_TITLEENTRY)
|
||||
{
|
||||
if (G_DemoTitleResponder(ev))
|
||||
continue;
|
||||
}
|
||||
|
||||
// console input
|
||||
#ifdef HAVE_THREADS
|
||||
I_lock_mutex(&con_mutex);
|
||||
|
|
|
|||
|
|
@ -2878,7 +2878,7 @@ static void Got_Mapcmd(const UINT8 **cp, INT32 playernum)
|
|||
if (demo.playback && !demo.timing)
|
||||
precache = false;
|
||||
|
||||
demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? DSM_WILLAUTOSAVE : DSM_NOTSAVING;
|
||||
demo.willsave = (cv_recordmultiplayerdemos.value == 2);
|
||||
demo.savebutton = 0;
|
||||
|
||||
G_InitNew(pencoremode, mapnumber, presetplayer, skipprecutscene);
|
||||
|
|
|
|||
|
|
@ -94,6 +94,7 @@ extern consvar_t cv_kartdebugitem, cv_kartdebugamount, cv_kartdebugdistribution,
|
|||
extern consvar_t cv_kartdebugnodes, cv_kartdebugcolorize, cv_kartdebugdirector;
|
||||
extern consvar_t cv_spbtest, cv_reducevfx, cv_screenshake;
|
||||
extern consvar_t cv_kartdebugwaypoints, cv_kartdebugbots;
|
||||
extern consvar_t cv_kartdebugbotwhip;
|
||||
extern consvar_t cv_kartdebugstart;
|
||||
extern consvar_t cv_debugrank;
|
||||
extern consvar_t cv_battletest;
|
||||
|
|
|
|||
|
|
@ -1299,6 +1299,8 @@ void readlevelheader(MYFILE *f, char * name)
|
|||
mapheaderinfo[num]->encorepal = (UINT16)i;
|
||||
else if (fastcmp(word, "NUMLAPS"))
|
||||
mapheaderinfo[num]->numlaps = (UINT8)i;
|
||||
else if (fastcmp(word, "LAPSPERSECTION"))
|
||||
mapheaderinfo[num]->lapspersection = max((UINT8)i, 1u);
|
||||
else if (fastcmp(word, "SKYBOXSCALE"))
|
||||
mapheaderinfo[num]->skybox_scalex = mapheaderinfo[num]->skybox_scaley = mapheaderinfo[num]->skybox_scalez = (INT16)i;
|
||||
else if (fastcmp(word, "SKYBOXSCALEX"))
|
||||
|
|
|
|||
|
|
@ -509,6 +509,7 @@ struct mapheader_t
|
|||
UINT16 levelflags; ///< LF_flags: merged booleans into one UINT16 for space, see below
|
||||
UINT32 typeoflevel; ///< Combination of typeoflevel flags.
|
||||
UINT8 numlaps; ///< Number of laps in circuit mode, unless overridden.
|
||||
UINT8 lapspersection; ///< Number of laps per section in hybrid section-circuit maps.
|
||||
fixed_t gravity; ///< Map-wide gravity.
|
||||
char relevantskin[SKINNAMESIZE+1]; ///< Skin to use for tutorial (if not provided, uses Eggman.)
|
||||
|
||||
|
|
@ -927,7 +928,6 @@ extern tic_t g_fast_forward;
|
|||
|
||||
#include "d_clisrv.h"
|
||||
|
||||
extern consvar_t cv_showinputjoy; // display joystick in time attack
|
||||
extern consvar_t cv_forceskin; // force clients to use the server's skin
|
||||
extern consvar_t cv_downloading; // allow clients to downloading WADs.
|
||||
extern consvar_t cv_nettimeout; // SRB2Kart: Advanced server options menu
|
||||
|
|
|
|||
|
|
@ -65,6 +65,30 @@
|
|||
#include "k_credits.h"
|
||||
#include "k_grandprix.h"
|
||||
|
||||
static menuitem_t TitleEntry[] =
|
||||
{
|
||||
{IT_NOTHING | IT_SPACE, "Save Replay", NULL,
|
||||
NULL, {NULL}, 0, 0},
|
||||
};
|
||||
|
||||
static menu_t TitleEntryDef = {
|
||||
sizeof (TitleEntry) / sizeof (menuitem_t),
|
||||
NULL,
|
||||
0,
|
||||
TitleEntry,
|
||||
0, 0,
|
||||
0, 0,
|
||||
MBF_SOUNDLESS,
|
||||
NULL,
|
||||
0, 0,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL,
|
||||
NULL
|
||||
};
|
||||
|
||||
boolean nodrawers; // for comparative timing purposes
|
||||
boolean noblit; // for comparative timing purposes
|
||||
tic_t demostarttime; // for comparative timing purposes
|
||||
|
|
@ -1752,6 +1776,9 @@ void G_ConfirmRewind(tic_t rewindtime)
|
|||
//
|
||||
void G_RecordDemo(const char *name)
|
||||
{
|
||||
if (demo.recording)
|
||||
G_CheckDemoStatus();
|
||||
|
||||
extern consvar_t cv_netdemosize;
|
||||
|
||||
INT32 maxsize;
|
||||
|
|
@ -3968,7 +3995,7 @@ boolean G_CheckDemoStatus(void)
|
|||
if (!demo.recording)
|
||||
return false;
|
||||
|
||||
if (modeattacking || demo.savemode != demovars_s::DSM_NOTSAVING)
|
||||
if (modeattacking || demo.willsave)
|
||||
{
|
||||
if (demobuf.p)
|
||||
{
|
||||
|
|
@ -3992,6 +4019,9 @@ void G_SaveDemo(void)
|
|||
UINT8 i;
|
||||
#endif
|
||||
|
||||
if (currentMenu == &TitleEntryDef)
|
||||
M_ClearMenus(true);
|
||||
|
||||
// Ensure extrainfo pointer is always available, even if no info is present.
|
||||
if (demoinfo_p && *(UINT32 *)demoinfo_p == 0)
|
||||
{
|
||||
|
|
@ -4054,14 +4084,13 @@ void G_SaveDemo(void)
|
|||
md5_buffer((char *)p+16, (demobuf.buffer + length) - (p+16), p);
|
||||
#endif
|
||||
|
||||
if (FIL_WriteFile(demoname, demobuf.buffer, demobuf.p - demobuf.buffer)) // finally output the file.
|
||||
demo.savemode = demovars_s::DSM_SAVED;
|
||||
bool saved = FIL_WriteFile(demoname, demobuf.buffer, demobuf.p - demobuf.buffer); // finally output the file.
|
||||
Z_Free(demobuf.buffer);
|
||||
demo.recording = false;
|
||||
|
||||
if (!modeattacking)
|
||||
{
|
||||
if (demo.savemode == demovars_s::DSM_SAVED)
|
||||
if (saved)
|
||||
{
|
||||
CONS_Printf(M_GetText("Demo %s recorded\n"), demoname);
|
||||
if (gamedata->eversavedreplay == false)
|
||||
|
|
@ -4076,55 +4105,6 @@ void G_SaveDemo(void)
|
|||
}
|
||||
}
|
||||
|
||||
boolean G_DemoTitleResponder(event_t *ev)
|
||||
{
|
||||
size_t len;
|
||||
INT32 ch;
|
||||
|
||||
if (ev->type != ev_keydown)
|
||||
return false;
|
||||
|
||||
ch = (INT32)ev->data1;
|
||||
|
||||
// Only ESC and non-keyboard keys abort connection
|
||||
if (ch == KEY_ESCAPE)
|
||||
{
|
||||
demo.savemode = (cv_recordmultiplayerdemos.value == 2) ? demovars_s::DSM_WILLAUTOSAVE : demovars_s::DSM_NOTSAVING;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ch == KEY_ENTER || ch >= NUMKEYS)
|
||||
{
|
||||
demo.savemode = demovars_s::DSM_WILLSAVE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && fontv[HU_FONT].font[ch-HU_FONTSTART])
|
||||
|| ch == ' ') // Allow spaces, of course
|
||||
{
|
||||
len = strlen(demo.titlename);
|
||||
if (len < 64)
|
||||
{
|
||||
demo.titlename[len+1] = 0;
|
||||
demo.titlename[len] = CON_ShiftChar(ch);
|
||||
}
|
||||
}
|
||||
else if (ch == KEY_BACKSPACE)
|
||||
{
|
||||
if (shiftdown)
|
||||
memset(demo.titlename, 0, sizeof(demo.titlename));
|
||||
else
|
||||
{
|
||||
len = strlen(demo.titlename);
|
||||
|
||||
if (len > 0)
|
||||
demo.titlename[len-1] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean G_CheckDemoTitleEntry(void)
|
||||
{
|
||||
if (menuactive || chat_on)
|
||||
|
|
@ -4133,7 +4113,18 @@ boolean G_CheckDemoTitleEntry(void)
|
|||
if (!G_PlayerInputDown(0, gc_b, 0) && !G_PlayerInputDown(0, gc_x, 0))
|
||||
return false;
|
||||
|
||||
demo.savemode = demovars_s::DSM_TITLEENTRY;
|
||||
demo.willsave = true;
|
||||
M_OpenVirtualKeyboard(
|
||||
false,
|
||||
sizeof demo.titlename,
|
||||
[](const char* replace) -> const char*
|
||||
{
|
||||
if (replace)
|
||||
strlcpy(demo.titlename, replace, sizeof demo.titlename);
|
||||
return demo.titlename;
|
||||
},
|
||||
&TitleEntryDef
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
10
src/g_demo.h
10
src/g_demo.h
|
|
@ -90,13 +90,7 @@ struct demovars_s {
|
|||
boolean netgame; // multiplayer netgame
|
||||
|
||||
tic_t savebutton; // Used to determine when the local player can choose to save the replay while the race is still going
|
||||
enum {
|
||||
DSM_NOTSAVING,
|
||||
DSM_WILLAUTOSAVE,
|
||||
DSM_TITLEENTRY,
|
||||
DSM_WILLSAVE,
|
||||
DSM_SAVED
|
||||
} savemode;
|
||||
boolean willsave;
|
||||
|
||||
boolean freecam;
|
||||
|
||||
|
|
@ -232,8 +226,6 @@ void G_DeferedPlayDemo(const char *demo);
|
|||
|
||||
void G_SaveDemo(void);
|
||||
|
||||
boolean G_DemoTitleResponder(event_t *ev);
|
||||
|
||||
boolean G_CheckDemoTitleEntry(void);
|
||||
|
||||
typedef enum
|
||||
|
|
|
|||
71
src/g_game.c
71
src/g_game.c
|
|
@ -3933,15 +3933,68 @@ void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencor
|
|||
// At the end of the Cup is a Rank-restricted treat.
|
||||
// So we append it to the end of the roundqueue.
|
||||
// (as long as it exists, of course!)
|
||||
cupLevelNum = cup->cachedlevels[CUPCACHE_SPECIAL];
|
||||
if (cupLevelNum < nummapheaders)
|
||||
{
|
||||
G_MapIntoRoundQueue(
|
||||
cupLevelNum,
|
||||
G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel),
|
||||
setencore, // if this isn't correct, Got_Mapcmd will fix it
|
||||
true // Rank-restricted!
|
||||
);
|
||||
// Of course, this last minute game design tweak
|
||||
// has to make things a little complicated. We
|
||||
// basically just make sure they're dispensed
|
||||
// at the intended difficulty sequence until
|
||||
// you've got them all, at which point they
|
||||
// become their intended order permanently.
|
||||
// ~toast 010324
|
||||
cupheader_t *emeraldcup = NULL;
|
||||
|
||||
if (gamedata->sealedswaps[GDMAX_SEALEDSWAPS-1] != NULL // all found
|
||||
|| cup->id >= basenumkartcupheaders // custom content
|
||||
|| M_SecretUnlocked(SECRET_SPECIALATTACK, false)) // true order
|
||||
{
|
||||
// Standard order.
|
||||
emeraldcup = cup;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine order from sealedswaps.
|
||||
for (i = 0; (i < GDMAX_SEALEDSWAPS && gamedata->sealedswaps[i]); i++)
|
||||
{
|
||||
if (gamedata->sealedswaps[i] != grandprixinfo.cup)
|
||||
continue;
|
||||
|
||||
// Repeat visit, grab the same ID.
|
||||
break;
|
||||
}
|
||||
|
||||
// If there's pending stars, get them from the associated cup order.
|
||||
if (i < GDMAX_SEALEDSWAPS)
|
||||
{
|
||||
emeraldcup = kartcupheaders;
|
||||
while (emeraldcup)
|
||||
{
|
||||
if (emeraldcup->id >= basenumkartcupheaders)
|
||||
{
|
||||
emeraldcup = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (emeraldcup->emeraldnum == i+1)
|
||||
break;
|
||||
|
||||
emeraldcup = emeraldcup->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (emeraldcup)
|
||||
{
|
||||
cupLevelNum = emeraldcup->cachedlevels[CUPCACHE_SPECIAL];
|
||||
if (cupLevelNum < nummapheaders)
|
||||
{
|
||||
G_MapIntoRoundQueue(
|
||||
cupLevelNum,
|
||||
G_GuessGametypeByTOL(mapheaderinfo[cupLevelNum]->typeoflevel),
|
||||
setencore, // if this isn't correct, Got_Mapcmd will fix it
|
||||
true // Rank-restricted!
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (roundqueue.size == 0)
|
||||
|
|
@ -4496,7 +4549,7 @@ void G_AfterIntermission(void)
|
|||
M_PlaybackQuit(0);
|
||||
return;
|
||||
}
|
||||
else if (demo.recording && (modeattacking || demo.savemode != DSM_NOTSAVING))
|
||||
else if (demo.recording && (modeattacking || demo.willsave))
|
||||
G_SaveDemo();
|
||||
|
||||
if (modeattacking) // End the run.
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ void srb2::save_ng_gamedata()
|
|||
ng.milestones.majorkeyskipattempted = gamedata->majorkeyskipattempted;
|
||||
ng.milestones.finishedtutorialchallenge = gamedata->finishedtutorialchallenge;
|
||||
ng.milestones.enteredtutorialchallenge = gamedata->enteredtutorialchallenge;
|
||||
ng.milestones.sealedswapalerted = gamedata->sealedswapalerted;
|
||||
ng.milestones.gonerlevel = gamedata->gonerlevel;
|
||||
ng.prisons.thisprisoneggpickup = gamedata->thisprisoneggpickup;
|
||||
ng.prisons.prisoneggstothispickup = gamedata->prisoneggstothispickup;
|
||||
|
|
@ -176,7 +177,7 @@ void srb2::save_ng_gamedata()
|
|||
}
|
||||
for (auto cup = kartcupheaders; cup; cup = cup->next)
|
||||
{
|
||||
if (cup->windata[0].best_placement == 0)
|
||||
if (cup->windata[0].best_placement == 0 && cup->windata[1].got_emerald == false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
|
@ -229,6 +230,17 @@ void srb2::save_ng_gamedata()
|
|||
ng.cups[cupdata.name] = std::move(cupdata);
|
||||
}
|
||||
|
||||
for (int i = 0; (i < GDMAX_SEALEDSWAPS && gamedata->sealedswaps[i]); i++)
|
||||
{
|
||||
srb2::GamedataSealedSwapJson sealedswap {};
|
||||
|
||||
cupheader_t* cup = gamedata->sealedswaps[i];
|
||||
|
||||
sealedswap.name = std::string(cup->name);
|
||||
|
||||
ng.sealedswaps.emplace_back(std::move(sealedswap));
|
||||
}
|
||||
|
||||
std::string gamedataname_s {gamedatafilename};
|
||||
fs::path savepath {fmt::format("{}/{}", srb2home, gamedataname_s)};
|
||||
fs::path tmpsavepath {fmt::format("{}/{}.tmp", srb2home, gamedataname_s)};
|
||||
|
|
@ -418,6 +430,7 @@ void srb2::load_ng_gamedata()
|
|||
gamedata->majorkeyskipattempted = js.milestones.majorkeyskipattempted;
|
||||
gamedata->finishedtutorialchallenge = js.milestones.finishedtutorialchallenge;
|
||||
gamedata->enteredtutorialchallenge = js.milestones.enteredtutorialchallenge;
|
||||
gamedata->sealedswapalerted = js.milestones.sealedswapalerted;
|
||||
gamedata->gonerlevel = js.milestones.gonerlevel;
|
||||
gamedata->thisprisoneggpickup = js.prisons.thisprisoneggpickup;
|
||||
gamedata->prisoneggstothispickup = js.prisons.prisoneggstothispickup;
|
||||
|
|
@ -720,6 +733,33 @@ void srb2::load_ng_gamedata()
|
|||
}
|
||||
}
|
||||
|
||||
size_t sealedswaps_size = js.sealedswaps.size();
|
||||
for (size_t i = 0; i < std::min((size_t)GDMAX_SEALEDSWAPS, sealedswaps_size); i++)
|
||||
{
|
||||
cupheader_t* cup = nullptr;
|
||||
|
||||
// Find BASE cups only
|
||||
for (cup = kartcupheaders; cup; cup = cup->next)
|
||||
{
|
||||
if (cup->id >= basenumkartcupheaders)
|
||||
{
|
||||
cup = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
std::string cupname = std::string(cup->name);
|
||||
if (cupname == js.sealedswaps[i].name)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (cup)
|
||||
{
|
||||
gamedata->sealedswaps[i] = cup;
|
||||
}
|
||||
}
|
||||
|
||||
M_FinaliseGameData();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -70,6 +70,7 @@ struct GamedataMilestonesJson final
|
|||
bool majorkeyskipattempted;
|
||||
bool finishedtutorialchallenge;
|
||||
bool enteredtutorialchallenge;
|
||||
bool sealedswapalerted;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
|
||||
GamedataMilestonesJson,
|
||||
|
|
@ -81,7 +82,8 @@ struct GamedataMilestonesJson final
|
|||
chaokeytutorial,
|
||||
majorkeyskipattempted,
|
||||
finishedtutorialchallenge,
|
||||
enteredtutorialchallenge
|
||||
enteredtutorialchallenge,
|
||||
sealedswapalerted
|
||||
)
|
||||
};
|
||||
|
||||
|
|
@ -184,6 +186,13 @@ struct GamedataCupJson final
|
|||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataCupJson, name, records)
|
||||
};
|
||||
|
||||
struct GamedataSealedSwapJson final
|
||||
{
|
||||
std::string name;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(GamedataSealedSwapJson, name)
|
||||
};
|
||||
|
||||
struct GamedataJson final
|
||||
{
|
||||
GamedataPlaytimeJson playtime;
|
||||
|
|
@ -203,6 +212,7 @@ struct GamedataJson final
|
|||
std::unordered_map<std::string, GamedataMapJson> maps;
|
||||
std::vector<GamedataSprayCanJson> spraycans;
|
||||
std::unordered_map<std::string, GamedataCupJson> cups;
|
||||
std::vector<GamedataSealedSwapJson> sealedswaps;
|
||||
|
||||
NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(
|
||||
GamedataJson,
|
||||
|
|
@ -222,7 +232,8 @@ struct GamedataJson final
|
|||
skins,
|
||||
maps,
|
||||
spraycans,
|
||||
cups
|
||||
cups,
|
||||
sealedswaps
|
||||
)
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
// See the 'LICENSE' file for more details.
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
|
@ -18,6 +19,8 @@
|
|||
#include "../i_joy.h"
|
||||
#include "../k_hud.h"
|
||||
#include "../k_kart.h"
|
||||
#include "../m_easing.h"
|
||||
#include "../p_tick.h"
|
||||
#include "../v_draw.hpp"
|
||||
|
||||
using srb2::Draw;
|
||||
|
|
@ -59,8 +62,21 @@ const char* dpad_suffix(const Vec2<float>& v)
|
|||
|
||||
}; // namespace
|
||||
|
||||
void K_DrawInputDisplay(INT32 x, INT32 y, INT32 flags, char mode, UINT8 pid, boolean local, boolean transparent)
|
||||
void K_DrawInputDisplay(float x, float y, INT32 flags, char mode, UINT8 pid, boolean local, boolean transparent)
|
||||
{
|
||||
auto fade_in = []
|
||||
{
|
||||
constexpr tic_t kStart = TICRATE;
|
||||
constexpr tic_t kDuration = TICRATE/2;
|
||||
fixed_t f = std::min(std::max(leveltime, kStart) - kStart, kDuration) * FRACUNIT / kDuration;
|
||||
return Easing_Linear(f, 0, 9);
|
||||
};
|
||||
auto alpha_to_flag = [](int alpha) { return (9 - alpha) << V_ALPHASHIFT; };
|
||||
|
||||
int alpha = fade_in();
|
||||
if (alpha == 0)
|
||||
return;
|
||||
|
||||
const ticcmd_t& cmd = players[displayplayers[pid]].cmd;
|
||||
const boolean analog = (mode == '4' || mode == '5') ? players[displayplayers[pid]].analoginput : false;
|
||||
const std::string prefix = fmt::format("PR{}", mode);
|
||||
|
|
@ -73,7 +89,8 @@ void K_DrawInputDisplay(INT32 x, INT32 y, INT32 flags, char mode, UINT8 pid, boo
|
|||
|
||||
Draw box = Draw(x, y).flags(flags);
|
||||
|
||||
box.flags(transparent ? V_TRANSLUCENT : 0).patch(gfx("CONT"));
|
||||
box.flags(alpha_to_flag(alpha / (transparent ? 2 : 1))).patch(gfx("CONT"));
|
||||
box = box.flags(alpha_to_flag(alpha));
|
||||
|
||||
Vec2<float> dpad = local ?
|
||||
Vec2<float> {
|
||||
|
|
|
|||
|
|
@ -1479,13 +1479,19 @@ static void K_BotItemInstashield(const player_t *player, ticcmd_t *cmd)
|
|||
const fixed_t radius = FixedMul(mobjinfo[MT_INSTAWHIP].radius, player->mo->scale);
|
||||
size_t i = SIZE_MAX;
|
||||
|
||||
if (K_ItemButtonWasDown(player) == true)
|
||||
{
|
||||
// Release the button, dude.
|
||||
return;
|
||||
}
|
||||
boolean nearbyThreat = false; // Someone's near enough to worry about, start charging.
|
||||
boolean attackOpportunity = false; // Someone's close enough to hit!
|
||||
boolean coastIsClear = true; // Nobody is nearby, let any pending charge go.
|
||||
|
||||
if (player->instaWhipCharge || leveltime < starttime || player->spindash)
|
||||
UINT8 stupidRating = MAXBOTDIFFICULTY - player->botvars.difficulty;
|
||||
// Weak bots take a second to react on offense.
|
||||
UINT8 reactiontime = stupidRating;
|
||||
// Weak bots misjudge their attack range. Purely accurate at Lv.MAX, 250% overestimate at Lv.1
|
||||
fixed_t radiusWithError = radius + 3*(radius * stupidRating / MAXBOTDIFFICULTY)/2;
|
||||
|
||||
// Future work: Expand threat range versus fast pursuers.
|
||||
|
||||
if (leveltime < starttime || player->spindash || player->defenseLockout)
|
||||
{
|
||||
// Instashield is on cooldown.
|
||||
return;
|
||||
|
|
@ -1517,18 +1523,41 @@ static void K_BotItemInstashield(const player_t *player, ticcmd_t *cmd)
|
|||
(player->mo->z - target->mo->z) / 4
|
||||
);
|
||||
|
||||
if (dist <= radius)
|
||||
if (dist <= 8 * radius)
|
||||
{
|
||||
K_ItemConfirmForTarget(player, cmd, target, player->botvars.difficulty * 2);
|
||||
coastIsClear = false;
|
||||
}
|
||||
|
||||
if (dist <= 5 * radius)
|
||||
{
|
||||
nearbyThreat = true;
|
||||
}
|
||||
|
||||
if (dist <= (radiusWithError + target->mo->radius))
|
||||
{
|
||||
attackOpportunity = true;
|
||||
K_ItemConfirmForTarget(player, cmd, target, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (player->botvars.itemconfirm > 10*TICRATE)
|
||||
if (player->instaWhipCharge) // Already charging, do we stay committed?
|
||||
{
|
||||
// Use it!!
|
||||
cmd->buttons |= BT_ATTACK;
|
||||
//player->botvars.itemconfirm = 0;
|
||||
cmd->buttons |= BT_ATTACK; // Keep holding, unless...
|
||||
|
||||
// ...there are no attackers that are even distantly threatening...
|
||||
if (coastIsClear)
|
||||
cmd->buttons &= ~BT_ATTACK;
|
||||
|
||||
// ...or we're ready to rock.
|
||||
if (attackOpportunity && player->instaWhipCharge >= (INSTAWHIP_CHARGETIME + reactiontime) && player->botvars.itemconfirm >= reactiontime)
|
||||
cmd->buttons &= ~BT_ATTACK;
|
||||
}
|
||||
else // When should we get spooked and start a charge?
|
||||
{
|
||||
if (nearbyThreat)
|
||||
cmd->buttons |= BT_ATTACK;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*--------------------------------------------------
|
||||
|
|
@ -1800,7 +1829,7 @@ static void K_UpdateBotGameplayVarsItemUsageMash(player_t *player)
|
|||
--------------------------------------------------*/
|
||||
void K_UpdateBotGameplayVarsItemUsage(player_t *player)
|
||||
{
|
||||
if (player->itemflags & IF_USERINGS)
|
||||
if (player->itemflags & IF_USERINGS && !player->instaWhipCharge)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
#include "p_local.h"
|
||||
#include "r_main.h"
|
||||
#include "s_sound.h"
|
||||
#include "m_easing.h"
|
||||
|
||||
/*--------------------------------------------------
|
||||
void K_AddHitLag(mobj_t *mo, INT32 tics, boolean fromDamage)
|
||||
|
|
@ -152,7 +153,11 @@ static void K_PlayHitLagSFX(mobj_t *victim, UINT8 tics)
|
|||
soundID = sfx_dmgb1;
|
||||
}
|
||||
|
||||
soundID += ((tics * (NUM_HITLAG_SOUNDS - 1)) + (MAXHITLAGTICS >> 1)) / MAXHITLAGTICS;
|
||||
soundID += Easing_Linear(
|
||||
min(FRACUNIT, FRACUNIT*tics/MAXHITLAGTICS),
|
||||
0,
|
||||
NUM_HITLAG_SOUNDS-1
|
||||
);
|
||||
S_StartSound(victim, soundID);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@
|
|||
#include "g_party.h"
|
||||
#include "k_hitlag.h"
|
||||
#include "g_input.h"
|
||||
#include "k_dialogue.h"
|
||||
|
||||
//{ Patch Definitions
|
||||
static patch_t *kp_nodraw;
|
||||
|
|
@ -5163,19 +5164,28 @@ static void K_drawKartFirstPerson(void)
|
|||
|
||||
static void K_drawInput(void)
|
||||
{
|
||||
UINT8 viewnum = R_GetViewNumber();
|
||||
boolean freecam = camera[viewnum].freecam; //disable some hud elements w/ freecam
|
||||
|
||||
if (!cv_drawinput.value && !modeattacking && gametype != GT_TUTORIAL)
|
||||
return;
|
||||
|
||||
if (stplyr->spectator || freecam || demo.attract)
|
||||
return;
|
||||
|
||||
INT32 def[4][3] = {
|
||||
{247, 156, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 1p
|
||||
{247, 56, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 2p
|
||||
{6, 52, V_SNAPTOBOTTOM | V_SNAPTOLEFT}, // 4p left
|
||||
{282 - BASEVIDWIDTH/2, 52, V_SNAPTOBOTTOM | V_SNAPTORIGHT}, // 4p right
|
||||
};
|
||||
INT32 k = r_splitscreen <= 1 ? r_splitscreen : 2 + (R_GetViewNumber() & 1);
|
||||
INT32 flags = def[k][2] | V_SPLITSCREEN | V_SLIDEIN;
|
||||
INT32 k = r_splitscreen <= 1 ? r_splitscreen : 2 + (viewnum & 1);
|
||||
INT32 flags = def[k][2] | V_SPLITSCREEN;
|
||||
char mode = ((stplyr->pflags & PF_ANALOGSTICK) ? '4' : '2') + (r_splitscreen > 1);
|
||||
bool local = !demo.playback && P_IsMachineLocalPlayer(stplyr);
|
||||
K_DrawInputDisplay(
|
||||
def[k][0],
|
||||
def[k][1],
|
||||
def[k][0] - FixedToFloat(K_GetDialogueSlide(34 * FRACUNIT)),
|
||||
def[k][1] - FixedToFloat(K_GetDialogueSlide(51 * FRACUNIT)),
|
||||
flags,
|
||||
mode,
|
||||
(local ? G_LocalSplitscreenPartyPosition : G_PartyPosition)(stplyr - players),
|
||||
|
|
@ -6062,13 +6072,6 @@ void K_drawKartHUD(void)
|
|||
K_drawRingCounter(gametypeinfoshown);
|
||||
}
|
||||
|
||||
if ((modeattacking && !bossinfo.valid) || cv_drawinput.value)
|
||||
{
|
||||
// Draw the input UI
|
||||
if (LUA_HudEnabled(hud_position))
|
||||
K_drawInput();
|
||||
}
|
||||
|
||||
// Draw the item window
|
||||
if (LUA_HudEnabled(hud_item) && !freecam)
|
||||
{
|
||||
|
|
@ -6128,7 +6131,10 @@ void K_drawKartHUD(void)
|
|||
K_drawEmeraldWin(true);
|
||||
|
||||
if (modeattacking || freecam) // everything after here is MP and debug only
|
||||
{
|
||||
K_drawInput();
|
||||
return;
|
||||
}
|
||||
|
||||
if ((gametyperules & GTR_KARMA) && !r_splitscreen && (stplyr->karthud[khud_yougotem] % 2)) // * YOU GOT EM *
|
||||
V_DrawScaledPatch(BASEVIDWIDTH/2 - (SHORT(kp_yougotem->width)/2), 32, V_HUDTRANS, kp_yougotem);
|
||||
|
|
@ -6147,6 +6153,10 @@ void K_drawKartHUD(void)
|
|||
{
|
||||
K_drawSpectatorHUD(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
K_drawInput();
|
||||
}
|
||||
|
||||
if (cv_kartdebugdistribution.value)
|
||||
K_drawDistributionDebugger();
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ void K_DrawKartPositionNumXY(
|
|||
boolean exit, boolean lastLap, boolean losing
|
||||
);
|
||||
|
||||
void K_DrawInputDisplay(INT32 x, INT32 y, INT32 flags, char mode, UINT8 pid, boolean local, boolean transparent);
|
||||
void K_DrawInputDisplay(float x, float y, INT32 flags, char mode, UINT8 pid, boolean local, boolean transparent);
|
||||
|
||||
extern patch_t *kp_capsuletarget_arrow[2][2];
|
||||
extern patch_t *kp_capsuletarget_icon[2];
|
||||
|
|
|
|||
55
src/k_kart.c
55
src/k_kart.c
|
|
@ -8534,6 +8534,17 @@ void K_KartPlayerThink(player_t *player, ticcmd_t *cmd)
|
|||
else if (player->rings < -20)
|
||||
player->rings = -20;
|
||||
|
||||
if (cv_kartdebugbotwhip.value)
|
||||
{
|
||||
if (player->bot)
|
||||
{
|
||||
player->rings = 0;
|
||||
player->itemtype = 0;
|
||||
player->itemamount = 0;
|
||||
player->itemRoulette.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (player->spheres > 40)
|
||||
player->spheres = 40;
|
||||
// where's the < 0 check? see below the following block!
|
||||
|
|
@ -9909,9 +9920,10 @@ static void K_UpdateDistanceFromFinishLine(player_t *const player)
|
|||
// correct we need to add to it the length of the entire circuit multiplied by the number of laps
|
||||
// left after this one. This will give us the total distance to the finish line, and allow item
|
||||
// distance calculation to work easily
|
||||
if ((mapheaderinfo[gamemap - 1]->levelflags & LF_SECTIONRACE) == 0U)
|
||||
const mapheader_t *mapheader = mapheaderinfo[gamemap - 1];
|
||||
if ((mapheader->levelflags & LF_SECTIONRACE) == 0U)
|
||||
{
|
||||
const UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps);
|
||||
const UINT8 numfulllapsleft = ((UINT8)numlaps - player->laps) / mapheader->lapspersection;
|
||||
player->distancetofinish += numfulllapsleft * K_GetCircuitLength();
|
||||
}
|
||||
}
|
||||
|
|
@ -9953,18 +9965,27 @@ static void K_UpdatePlayerWaypoints(player_t *const player)
|
|||
UINT32 delta = u32_delta(player->distancetofinish, player->distancetofinishprev);
|
||||
if (player->respawn.state == RESPAWNST_NONE && delta > distance_threshold && old_currentwaypoint != NULL)
|
||||
{
|
||||
CONS_Debug(DBG_GAMELOGIC, "Player %s: waypoint ID %d too far away (%u > %u)\n",
|
||||
sizeu1(player - players), K_GetWaypointID(player->nextwaypoint), delta, distance_threshold);
|
||||
extern consvar_t cv_debuglapcheat;
|
||||
#define debug_args "Player %s: waypoint ID %d too far away (%u > %u)\n", \
|
||||
sizeu1(player - players), K_GetWaypointID(player->nextwaypoint), delta, distance_threshold
|
||||
if (cv_debuglapcheat.value)
|
||||
CONS_Printf(debug_args);
|
||||
else
|
||||
CONS_Debug(DBG_GAMELOGIC, debug_args);
|
||||
#undef debug_args
|
||||
|
||||
// Distance jump is too great, keep the old waypoints and old distance.
|
||||
player->currentwaypoint = old_currentwaypoint;
|
||||
player->nextwaypoint = old_nextwaypoint;
|
||||
player->distancetofinish = player->distancetofinishprev;
|
||||
|
||||
// Start the auto respawn timer when the distance jumps.
|
||||
if (!player->bigwaypointgap)
|
||||
if (!cv_debuglapcheat.value)
|
||||
{
|
||||
player->bigwaypointgap = 35;
|
||||
// Distance jump is too great, keep the old waypoints and old distance.
|
||||
player->currentwaypoint = old_currentwaypoint;
|
||||
player->nextwaypoint = old_nextwaypoint;
|
||||
player->distancetofinish = player->distancetofinishprev;
|
||||
|
||||
// Start the auto respawn timer when the distance jumps.
|
||||
if (!player->bigwaypointgap)
|
||||
{
|
||||
player->bigwaypointgap = 35;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
|
|
@ -11382,7 +11403,7 @@ static void K_KartSpindash(player_t *player)
|
|||
|
||||
if (player->spindash > 0 && (buttons & (BT_DRIFT|BT_BRAKE|BT_ACCELERATE)) != (BT_DRIFT|BT_BRAKE|BT_ACCELERATE))
|
||||
{
|
||||
player->spindashspeed = (player->spindash * FRACUNIT) / MAXCHARGETIME;
|
||||
player->spindashspeed = (min(player->spindash, MAXCHARGETIME) * FRACUNIT) / MAXCHARGETIME;
|
||||
player->spindashboost = TICRATE;
|
||||
|
||||
// if spindash was charged enough, give a small thrust.
|
||||
|
|
@ -12003,6 +12024,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
{
|
||||
S_StartSound(player->mo, sfx_kc50);
|
||||
player->instaWhipCharge = 0;
|
||||
player->botvars.itemconfirm = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -12436,6 +12458,8 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
{
|
||||
INT32 numhogs = min((player->ballhogcharge / BALLHOGINCREMENT), player->itemamount);
|
||||
|
||||
K_SetItemOut(player); // need this to set itemscale
|
||||
|
||||
if (numhogs <= 0)
|
||||
{
|
||||
// no tapfire scams
|
||||
|
|
@ -12464,6 +12488,7 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
K_PlayAttackTaunt(player->mo);
|
||||
}
|
||||
|
||||
K_UnsetItemOut(player);
|
||||
player->ballhogcharge = 0;
|
||||
player->itemflags &= ~IF_HOLDREADY;
|
||||
player->botvars.itemconfirm = 0;
|
||||
|
|
@ -12475,7 +12500,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
|
||||
{
|
||||
player->itemamount--;
|
||||
K_SetItemOut(player);
|
||||
K_ThrowKartItem(player, true, MT_SPB, 1, 0, 0);
|
||||
K_UnsetItemOut(player);
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
player->botvars.itemconfirm = 0;
|
||||
}
|
||||
|
|
@ -12802,7 +12829,9 @@ void K_MoveKartPlayer(player_t *player, boolean onground)
|
|||
case KITEM_GACHABOM:
|
||||
if (ATTACK_IS_DOWN && !HOLDING_ITEM && NO_HYUDORO)
|
||||
{
|
||||
K_SetItemOut(player); // need this to set itemscale
|
||||
K_ThrowKartItem(player, true, MT_GACHABOM, 0, 0, 0);
|
||||
K_UnsetItemOut(player);
|
||||
K_PlayAttackTaunt(player->mo);
|
||||
player->itemamount--;
|
||||
player->roundconditions.gachabom_miser = (
|
||||
|
|
|
|||
13
src/k_menu.h
13
src/k_menu.h
|
|
@ -542,6 +542,7 @@ extern INT32 menuKey; // keyboard key pressed for menu
|
|||
extern INT16 virtualKeyboard[5][NUMVIRTUALKEYSINROW];
|
||||
extern INT16 shift_virtualKeyboard[5][NUMVIRTUALKEYSINROW];
|
||||
|
||||
typedef const char *(*vkb_query_fn_t)(const char *replace);
|
||||
extern struct menutyping_s
|
||||
{
|
||||
boolean active; // Active
|
||||
|
|
@ -554,7 +555,10 @@ extern struct menutyping_s
|
|||
boolean keyboardcapslock;
|
||||
boolean keyboardshift;
|
||||
|
||||
char cache[MAXSTRINGLENGTH]; // cached string
|
||||
vkb_query_fn_t queryfn; // callback on open and close
|
||||
menu_t *dummymenu;
|
||||
size_t cachelen;
|
||||
char *cache; // cached string
|
||||
|
||||
} menutyping;
|
||||
// While typing, we'll have a fade strongly darken the screen to overlay the typing menu instead
|
||||
|
|
@ -680,7 +684,10 @@ void M_Init(void);
|
|||
|
||||
void M_PlayMenuJam(void);
|
||||
|
||||
void M_OpenVirtualKeyboard(boolean gamepad);
|
||||
boolean M_ConsiderSealedSwapAlert(void);
|
||||
|
||||
void M_OpenVirtualKeyboard(boolean gamepad, size_t cachelen, vkb_query_fn_t queryfn, menu_t *dummymenu);
|
||||
void M_AbortVirtualKeyboard(void);
|
||||
void M_MenuTypingInput(INT32 key);
|
||||
|
||||
void M_QuitResponse(INT32 ch);
|
||||
|
|
@ -1347,6 +1354,8 @@ extern struct challengesmenu_s {
|
|||
boolean chaokeyadd, keywasadded;
|
||||
UINT8 chaokeyhold;
|
||||
|
||||
boolean considersealedswapalert;
|
||||
|
||||
boolean requestflip;
|
||||
|
||||
UINT16 unlockcount[CMC_MAX];
|
||||
|
|
|
|||
|
|
@ -495,6 +495,21 @@ static void M_DrawMenuTooltips(void)
|
|||
}
|
||||
}
|
||||
|
||||
static const char *M_MenuTypingCroppedString(void)
|
||||
{
|
||||
static char buf[36];
|
||||
const char *p = menutyping.cache;
|
||||
size_t n = strlen(p);
|
||||
if (n > sizeof buf)
|
||||
{
|
||||
p += n - sizeof buf;
|
||||
n = sizeof buf;
|
||||
}
|
||||
memcpy(buf, p, n);
|
||||
buf[n] = '\0';
|
||||
return buf;
|
||||
}
|
||||
|
||||
// Draws the typing submenu
|
||||
static void M_DrawMenuTyping(void)
|
||||
{
|
||||
|
|
@ -534,7 +549,7 @@ static void M_DrawMenuTyping(void)
|
|||
V_DrawFill(x + 4, y + 4 + 5, 1, 8+6, 121);
|
||||
V_DrawFill(x + 5 + boxwidth - 8, y + 4 + 5, 1, 8+6, 121);
|
||||
|
||||
INT32 textwidth = M_DrawCaretString(x + 8, y + 12, menutyping.cache, true);
|
||||
INT32 textwidth = M_DrawCaretString(x + 8, y + 12, M_MenuTypingCroppedString(), true);
|
||||
if (skullAnimCounter < 4
|
||||
&& menutyping.menutypingclose == false
|
||||
&& menutyping.menutypingfade == (menutyping.keyboardtyping ? 9 : 18))
|
||||
|
|
@ -2823,6 +2838,57 @@ fixed_t M_DrawCupWinData(INT32 rankx, INT32 ranky, cupheader_t *cup, UINT8 diffi
|
|||
rankx += 19 - (rankw / 2);
|
||||
|
||||
cupwindata_t *windata = &(cup->windata[difficulty]);
|
||||
UINT8 emeraldnum = UINT8_MAX;
|
||||
|
||||
if (!noemerald)
|
||||
{
|
||||
if (gamedata->sealedswaps[GDMAX_SEALEDSWAPS-1] != NULL // all found
|
||||
|| cup->id >= basenumkartcupheaders // custom content
|
||||
|| M_SecretUnlocked(SECRET_SPECIALATTACK, true)) // true order
|
||||
{
|
||||
// Standard order.
|
||||
if (windata->got_emerald == true)
|
||||
{
|
||||
emeraldnum = cup->emeraldnum;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine order from sealedswaps.
|
||||
UINT8 i;
|
||||
for (i = 0; (i < GDMAX_SEALEDSWAPS && gamedata->sealedswaps[i]); i++)
|
||||
{
|
||||
if (gamedata->sealedswaps[i] != cup)
|
||||
continue;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// If there's pending stars, get them from the associated cup order.
|
||||
if (i < GDMAX_SEALEDSWAPS)
|
||||
{
|
||||
cupheader_t *emeraldcup = kartcupheaders;
|
||||
while (emeraldcup)
|
||||
{
|
||||
if (emeraldcup->id >= basenumkartcupheaders)
|
||||
{
|
||||
emeraldcup = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (emeraldcup->emeraldnum == i+1)
|
||||
{
|
||||
if (emeraldcup->windata[difficulty].got_emerald == true)
|
||||
emeraldnum = i+1;
|
||||
break;
|
||||
}
|
||||
|
||||
emeraldcup = emeraldcup->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (windata->best_placement == 0)
|
||||
{
|
||||
if (statsmode)
|
||||
|
|
@ -2832,14 +2898,13 @@ fixed_t M_DrawCupWinData(INT32 rankx, INT32 ranky, cupheader_t *cup, UINT8 diffi
|
|||
V_DrawCharacter((14-4)/2 + rankx, ranky, '.' | V_GRAYMAP, false);
|
||||
rankx += 14 + 1;
|
||||
V_DrawCharacter((12-4)/2 + rankx, ranky, '.' | V_GRAYMAP, false);
|
||||
|
||||
if (!noemerald)
|
||||
{
|
||||
rankx += 12 + 1;
|
||||
V_DrawCharacter((12-4)/2 + rankx, ranky, '.' | V_GRAYMAP, false);
|
||||
}
|
||||
}
|
||||
return rankw;
|
||||
else
|
||||
{
|
||||
rankx += 14 + 1;
|
||||
}
|
||||
|
||||
goto windataemeraldmaybe;
|
||||
}
|
||||
|
||||
UINT8 *colormap = NULL;
|
||||
|
|
@ -2934,13 +2999,15 @@ fixed_t M_DrawCupWinData(INT32 rankx, INT32 ranky, cupheader_t *cup, UINT8 diffi
|
|||
if (charPat)
|
||||
V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, charPat, colormap);
|
||||
|
||||
windataemeraldmaybe:
|
||||
|
||||
rankx += 12 + 1;
|
||||
|
||||
if (noemerald)
|
||||
;
|
||||
else if (windata->got_emerald == true)
|
||||
else if (emeraldnum != UINT8_MAX)
|
||||
{
|
||||
if (cup->emeraldnum == 0)
|
||||
if (emeraldnum == 0)
|
||||
V_DrawCharacter(rankx+2, ranky+2, '+', false);
|
||||
else
|
||||
{
|
||||
|
|
@ -2948,14 +3015,14 @@ fixed_t M_DrawCupWinData(INT32 rankx, INT32 ranky, cupheader_t *cup, UINT8 diffi
|
|||
|
||||
if (!flash)
|
||||
{
|
||||
UINT16 col = SKINCOLOR_CHAOSEMERALD1 + (cup->emeraldnum-1) % 7;
|
||||
UINT16 col = SKINCOLOR_CHAOSEMERALD1 + (emeraldnum-1) % 7;
|
||||
|
||||
colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
|
||||
}
|
||||
|
||||
const char *emname = va(
|
||||
"%sMAP%c",
|
||||
(cup->emeraldnum > 7) ? "SUP" : "EME",
|
||||
(emeraldnum > 7) ? "SUP" : "EME",
|
||||
colormap ? '\0' : 'B'
|
||||
);
|
||||
|
||||
|
|
@ -3116,7 +3183,7 @@ void M_DrawCupSelect(void)
|
|||
y += 44; //(8 + 100) - (20 + 44)
|
||||
}
|
||||
|
||||
if (windata && windata->best_placement != 0)
|
||||
if (windata)
|
||||
{
|
||||
M_DrawCupWinData(
|
||||
x,
|
||||
|
|
|
|||
|
|
@ -188,6 +188,14 @@ static void M_ChangeCvar(INT32 choice)
|
|||
M_ChangeCvarDirect(choice, currentMenu->menuitems[itemOn].itemaction.cvar);
|
||||
}
|
||||
|
||||
static const char *M_QueryCvarAction(const char *replace)
|
||||
{
|
||||
consvar_t *cvar = currentMenu->menuitems[itemOn].itemaction.cvar;
|
||||
if (replace)
|
||||
CV_Set(cvar, replace);
|
||||
return cvar->string;
|
||||
}
|
||||
|
||||
boolean M_NextOpt(void)
|
||||
{
|
||||
INT16 oldItemOn = itemOn; // prevent infinite loop
|
||||
|
|
@ -560,6 +568,35 @@ void M_PlayMenuJam(void)
|
|||
|
||||
#undef IsCurrentlyPlaying
|
||||
|
||||
boolean M_ConsiderSealedSwapAlert(void)
|
||||
{
|
||||
if (gamedata->sealedswapalerted == true)
|
||||
return false;
|
||||
|
||||
if (gamedata->sealedswaps[GDMAX_SEALEDSWAPS-1] != NULL // all found
|
||||
|| M_SecretUnlocked(SECRET_SPECIALATTACK, true)) // true order
|
||||
{
|
||||
gamedata->sealedswapalerted = true;
|
||||
|
||||
// Don't make a message if no Sealed Stars have yet been found.
|
||||
if (gamedata->everseenspecial == false)
|
||||
return false;
|
||||
|
||||
M_StartMessage(
|
||||
"Message from the Stars",
|
||||
"As if called by fate, the Emeralds you've\n"
|
||||
"collected return to their rightful places...\n"
|
||||
"\n"
|
||||
"The Sealed Stars are now ordered via Cups!\n",
|
||||
NULL, MM_NOTHING, NULL, NULL
|
||||
);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void M_ValidateRestoreMenu(void)
|
||||
{
|
||||
if (restoreMenu == NULL || restoreMenu == &MAIN_GonerDef)
|
||||
|
|
@ -629,6 +666,11 @@ menu_t *M_SpecificMenuRestore(menu_t *torestore)
|
|||
M_SetupPlayMenu(-1);
|
||||
PLAY_CharSelectDef.prevMenu = &MainDef;
|
||||
|
||||
if (torestore != &MISC_ChallengesDef)
|
||||
{
|
||||
M_ConsiderSealedSwapAlert();
|
||||
}
|
||||
|
||||
return torestore;
|
||||
}
|
||||
|
||||
|
|
@ -797,7 +839,7 @@ void M_ClearMenus(boolean callexitmenufunc)
|
|||
D_StartTitle();
|
||||
}
|
||||
|
||||
menutyping.active = false;
|
||||
M_AbortVirtualKeyboard();
|
||||
menumessage.active = false;
|
||||
|
||||
menuactive = false;
|
||||
|
|
@ -884,6 +926,8 @@ void M_SetupNextMenu(menu_t *menudef, boolean notransition)
|
|||
|
||||
void M_GoBack(INT32 choice)
|
||||
{
|
||||
const INT16 behaviourflags = currentMenu->behaviourflags;
|
||||
|
||||
(void)choice;
|
||||
|
||||
noFurtherInput = true;
|
||||
|
|
@ -906,7 +950,8 @@ void M_GoBack(INT32 choice)
|
|||
else // No returning to the title screen.
|
||||
M_QuitSRB2(-1);
|
||||
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
if (!(behaviourflags & MBF_SOUNDLESS))
|
||||
S_StartSound(NULL, sfx_s3k5b);
|
||||
}
|
||||
|
||||
//
|
||||
|
|
@ -1090,7 +1135,8 @@ static void M_HandleMenuInput(void)
|
|||
// If we're hovering over a IT_CV_STRING option, pressing A/X opens the typing submenu
|
||||
if (M_MenuConfirmPressed(pid))
|
||||
{
|
||||
M_OpenVirtualKeyboard(thisMenuKey == -1); // If we entered this menu by pressing a menu Key, default to keyboard typing, otherwise use controller.
|
||||
// If we entered this menu by pressing a menu Key, default to keyboard typing, otherwise use controller.
|
||||
M_OpenVirtualKeyboard(thisMenuKey == -1, MAXSTRINGLENGTH, M_QueryCvarAction, NULL);
|
||||
return;
|
||||
}
|
||||
else if (M_MenuExtraPressed(pid))
|
||||
|
|
|
|||
|
|
@ -83,6 +83,7 @@ static struct podiumData_s
|
|||
sfxenum_t gradeVoice;
|
||||
|
||||
cupheader_t *cup;
|
||||
UINT8 emeraldnum;
|
||||
|
||||
boolean fastForward;
|
||||
|
||||
|
|
@ -102,6 +103,7 @@ void podiumData_s::Init(void)
|
|||
{
|
||||
rank = grandprixinfo.rank;
|
||||
cup = grandprixinfo.cup;
|
||||
emeraldnum = cup->emeraldnum;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -119,6 +121,7 @@ void podiumData_s::Init(void)
|
|||
|
||||
cup = cup->next;
|
||||
}
|
||||
emeraldnum = 0;
|
||||
|
||||
memset(&rank, 0, sizeof(gpRank_t));
|
||||
rank.skin = players[consoleplayer].skin;
|
||||
|
|
@ -628,12 +631,7 @@ void podiumData_s::Draw(void)
|
|||
case GPEVENT_SPECIAL:
|
||||
{
|
||||
srb2::Draw drawer_emerald = drawer_gametype;
|
||||
UINT8 emeraldNum = 0;
|
||||
|
||||
if (cup != nullptr)
|
||||
{
|
||||
emeraldNum = cup->emeraldnum;
|
||||
}
|
||||
UINT8 emeraldNum = g_podiumData.emeraldnum;
|
||||
|
||||
boolean useWhiteFrame = ((leveltime & 1) || !dta->gotSpecialPrize);
|
||||
patch_t *emeraldPatch = nullptr;
|
||||
|
|
@ -844,12 +842,7 @@ void podiumData_s::Draw(void)
|
|||
|
||||
if (rank.specialWon == true)
|
||||
{
|
||||
UINT8 emeraldNum = 0;
|
||||
|
||||
if (cup != nullptr)
|
||||
{
|
||||
emeraldNum = cup->emeraldnum;
|
||||
}
|
||||
UINT8 emeraldNum = g_podiumData.emeraldnum;
|
||||
|
||||
const boolean emeraldBlink = (leveltime & 1);
|
||||
patch_t *emeraldOverlay = nullptr;
|
||||
|
|
@ -1263,6 +1256,60 @@ void K_ResetCeremony(void)
|
|||
return;
|
||||
}
|
||||
|
||||
cupheader_t *emeraldcup = NULL;
|
||||
|
||||
if (gamedata->sealedswaps[GDMAX_SEALEDSWAPS-1] != NULL // all found
|
||||
|| grandprixinfo.cup->id >= basenumkartcupheaders // custom content
|
||||
|| M_SecretUnlocked(SECRET_SPECIALATTACK, false)) // true order
|
||||
{
|
||||
// Standard order.
|
||||
emeraldcup = grandprixinfo.cup;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine order from sealedswaps.
|
||||
for (i = 0; i < GDMAX_SEALEDSWAPS; i++)
|
||||
{
|
||||
if (gamedata->sealedswaps[i] == NULL)
|
||||
{
|
||||
if (g_podiumData.rank.specialWon == true)
|
||||
{
|
||||
// First visit! Mark it off.
|
||||
gamedata->sealedswaps[i] = grandprixinfo.cup;
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
if (gamedata->sealedswaps[i] != grandprixinfo.cup)
|
||||
continue;
|
||||
|
||||
// Repeat visit, grab the same ID.
|
||||
break;
|
||||
}
|
||||
|
||||
// If there's pending stars, apply them to the new cup order.
|
||||
if (i < GDMAX_SEALEDSWAPS)
|
||||
{
|
||||
emeraldcup = kartcupheaders;
|
||||
while (emeraldcup)
|
||||
{
|
||||
if (emeraldcup->id >= basenumkartcupheaders)
|
||||
{
|
||||
emeraldcup = NULL;
|
||||
break;
|
||||
}
|
||||
|
||||
if (emeraldcup->emeraldnum == i+1)
|
||||
break;
|
||||
|
||||
emeraldcup = emeraldcup->next;
|
||||
}
|
||||
|
||||
g_podiumData.emeraldnum = i+1;
|
||||
}
|
||||
}
|
||||
|
||||
// Write grade, position, and emerald-having-ness for later sessions!
|
||||
i = (grandprixinfo.masterbots) ? KARTGP_MASTER : grandprixinfo.gamespeed;
|
||||
|
||||
|
|
@ -1292,9 +1339,9 @@ void K_ResetCeremony(void)
|
|||
anymerit = true;
|
||||
}
|
||||
|
||||
if (g_podiumData.rank.specialWon == true)
|
||||
if (g_podiumData.rank.specialWon == true && emeraldcup)
|
||||
{
|
||||
grandprixinfo.cup->windata[i].got_emerald = true;
|
||||
emeraldcup->windata[i].got_emerald = true;
|
||||
anymerit = true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2535,6 +2535,8 @@ static int mapheaderinfo_get(lua_State *L)
|
|||
lua_pushinteger(L, header->palette);
|
||||
else if (fastcmp(field,"numlaps"))
|
||||
lua_pushinteger(L, header->numlaps);
|
||||
else if (fastcmp(field,"lapspersection"))
|
||||
lua_pushinteger(L, header->lapspersection);
|
||||
else if (fastcmp(field,"levelselect"))
|
||||
lua_pushinteger(L, header->levelselect);
|
||||
else if (fastcmp(field,"levelflags"))
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@ static UINT8 cheatf_warp(void)
|
|||
if (success)
|
||||
{
|
||||
gamedata->gonerlevel = GDGONER_DONE;
|
||||
gamedata->sealedswapalerted = true;
|
||||
G_SetUsedCheats();
|
||||
}
|
||||
|
||||
|
|
@ -231,6 +232,7 @@ static UINT8 cheatf_devmode(void)
|
|||
}
|
||||
|
||||
gamedata->gonerlevel = GDGONER_DONE;
|
||||
gamedata->sealedswapalerted = true;
|
||||
|
||||
M_ClearMenus(true);
|
||||
|
||||
|
|
|
|||
|
|
@ -666,6 +666,7 @@ void M_ClearStats(void)
|
|||
gamedata->majorkeyskipattempted = false;
|
||||
gamedata->enteredtutorialchallenge = false;
|
||||
gamedata->finishedtutorialchallenge = false;
|
||||
gamedata->sealedswapalerted = false;
|
||||
gamedata->musicstate = GDMUSIC_NONE;
|
||||
|
||||
gamedata->importprofilewins = false;
|
||||
|
|
@ -720,6 +721,8 @@ void M_ClearSecrets(void)
|
|||
skincolors[i].cache_spraycan = UINT16_MAX;
|
||||
}
|
||||
|
||||
memset(gamedata->sealedswaps, 0, sizeof(gamedata->sealedswaps));
|
||||
|
||||
Z_Free(gamedata->challengegrid);
|
||||
gamedata->challengegrid = NULL;
|
||||
gamedata->challengegridwidth = 0;
|
||||
|
|
|
|||
|
|
@ -291,6 +291,7 @@ typedef enum {
|
|||
// This is the largest number of 9s that will fit in UINT32 and UINT16 respectively.
|
||||
#define GDMAX_RINGS 999999999
|
||||
#define GDMAX_CHAOKEYS 9999
|
||||
#define GDMAX_SEALEDSWAPS 7
|
||||
|
||||
#define GDCONVERT_ROUNDSTOKEY 14
|
||||
|
||||
|
|
@ -371,12 +372,15 @@ struct gamedata_t
|
|||
UINT32 totalrings;
|
||||
UINT32 totaltumbletime;
|
||||
|
||||
// Chao Key condition bypass
|
||||
// CHAO KEYS AND THEIR GENERATION
|
||||
UINT32 pendingkeyrounds;
|
||||
UINT8 pendingkeyroundoffset;
|
||||
UINT16 keyspending;
|
||||
UINT16 chaokeys;
|
||||
|
||||
// EMERALD REMAPPING
|
||||
cupheader_t *sealedswaps[GDMAX_SEALEDSWAPS];
|
||||
|
||||
// SPECIFIC SPECIAL EVENTS
|
||||
boolean everloadedaddon;
|
||||
boolean everfinishedcredits;
|
||||
|
|
@ -387,6 +391,7 @@ struct gamedata_t
|
|||
boolean majorkeyskipattempted;
|
||||
boolean enteredtutorialchallenge;
|
||||
boolean finishedtutorialchallenge;
|
||||
boolean sealedswapalerted;
|
||||
gdmusic_t musicstate;
|
||||
|
||||
UINT8 gonerlevel;
|
||||
|
|
|
|||
|
|
@ -335,6 +335,7 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu)
|
|||
challengesmenu.requestnew = false;
|
||||
challengesmenu.chaokeyadd = false;
|
||||
challengesmenu.keywasadded = false;
|
||||
challengesmenu.considersealedswapalert = false;
|
||||
challengesmenu.chaokeyhold = 0;
|
||||
challengesmenu.currentunlock = MAXUNLOCKABLES;
|
||||
challengesmenu.unlockcondition = NULL;
|
||||
|
|
@ -668,10 +669,15 @@ void M_ChallengesTick(void)
|
|||
if (challengesmenu.currentunlock < MAXUNLOCKABLES
|
||||
&& challengesmenu.unlockanim == UNLOCKTIME)
|
||||
{
|
||||
unlockable_t *ref = &unlockables[challengesmenu.currentunlock];
|
||||
|
||||
// Unlock animation... also tied directly to the actual unlock!
|
||||
gamedata->unlocked[challengesmenu.currentunlock] = true;
|
||||
M_UpdateUnlockablesAndExtraEmblems(true, true);
|
||||
|
||||
if (ref->type == SECRET_SPECIALATTACK)
|
||||
challengesmenu.considersealedswapalert = true;
|
||||
|
||||
// Update shown description just in case..?
|
||||
if (challengesmenu.unlockcondition)
|
||||
Z_Free(challengesmenu.unlockcondition);
|
||||
|
|
@ -682,12 +688,10 @@ void M_ChallengesTick(void)
|
|||
|
||||
if (challengesmenu.extradata)
|
||||
{
|
||||
unlockable_t *ref;
|
||||
UINT16 bombcolor;
|
||||
|
||||
M_UpdateChallengeGridExtraData(challengesmenu.extradata);
|
||||
|
||||
ref = &unlockables[challengesmenu.currentunlock];
|
||||
bombcolor = SKINCOLOR_NONE;
|
||||
|
||||
if (ref->color != SKINCOLOR_NONE && ref->color < numskincolors)
|
||||
|
|
@ -747,7 +751,14 @@ void M_ChallengesTick(void)
|
|||
// Play music the moment control returns.
|
||||
M_PlayMenuJam();
|
||||
|
||||
if (gamedata->chaokeytutorial == false
|
||||
if (challengesmenu.considersealedswapalert == true
|
||||
&& M_ConsiderSealedSwapAlert() == true)
|
||||
{
|
||||
// No keygen tutorial in this case...
|
||||
// not ideal but at least unlikely to
|
||||
// get at same time?? :V
|
||||
}
|
||||
else if (gamedata->chaokeytutorial == false
|
||||
&& challengesmenu.keywasadded == true)
|
||||
{
|
||||
M_ChallengesTutorial(CCTUTORIAL_KEYGEN);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
#include "../m_easing.h"
|
||||
#include "../p_local.h" // cv_tilting
|
||||
|
||||
extern "C" consvar_t cv_mindelay;
|
||||
extern "C" consvar_t cv_mindelay, cv_drawinput;
|
||||
|
||||
using srb2::Draw;
|
||||
|
||||
|
|
@ -119,6 +119,9 @@ menuitem_t OPTIONS_ProfileAccessibility[] = {
|
|||
|
||||
{IT_STRING | IT_CVAR, "Screenshake", "Adjust shake intensity from hazards and offroad.",
|
||||
NULL, {.cvar = &cv_screenshake}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CVAR, "Input Display", "Show virtual controller on the HUD.",
|
||||
NULL, {.cvar = &cv_drawinput}, 0, 0},
|
||||
};
|
||||
|
||||
menu_t OPTIONS_ProfileAccessibilityDef = {
|
||||
|
|
|
|||
|
|
@ -116,6 +116,9 @@ void M_StopMessage(INT32 choice)
|
|||
|
||||
boolean M_MenuMessageTick(void)
|
||||
{
|
||||
if (menuwipe)
|
||||
return false;
|
||||
|
||||
if (menumessage.closing)
|
||||
{
|
||||
if (menumessage.closing > MENUMESSAGECLOSE)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
#include "../../s_sound.h"
|
||||
#include "../../console.h" // CON_ShiftChar
|
||||
#include "../../i_system.h" // I_Clipboard funcs
|
||||
#include "../../z_zone.h"
|
||||
|
||||
// Typing "sub"-menu
|
||||
struct menutyping_s menutyping;
|
||||
|
|
@ -91,9 +92,9 @@ boolean M_ChangeStringCvar(INT32 choice)
|
|||
const char *paste = I_ClipboardPaste();
|
||||
if (paste == NULL || paste[0] == '\0')
|
||||
;
|
||||
else if (len < MAXSTRINGLENGTH - 1)
|
||||
else if (len < menutyping.cachelen)
|
||||
{
|
||||
strlcat(menutyping.cache, paste, MAXSTRINGLENGTH);
|
||||
strlcat(menutyping.cache, paste, menutyping.cachelen + 1);
|
||||
|
||||
S_StartSound(NULL, sfx_tmxbdn); // Tails
|
||||
}
|
||||
|
|
@ -146,7 +147,7 @@ boolean M_ChangeStringCvar(INT32 choice)
|
|||
if (choice >= 32 && choice <= 127)
|
||||
{
|
||||
len = strlen(menutyping.cache);
|
||||
if (len < MAXSTRINGLENGTH - 1)
|
||||
if (len < menutyping.cachelen)
|
||||
{
|
||||
menutyping.cache[len++] = (char)choice;
|
||||
menutyping.cache[len] = 0;
|
||||
|
|
@ -180,7 +181,19 @@ static void M_ToggleVirtualShift(void)
|
|||
static void M_CloseVirtualKeyboard(void)
|
||||
{
|
||||
menutyping.menutypingclose = true; // close menu.
|
||||
CV_Set(currentMenu->menuitems[itemOn].itemaction.cvar, menutyping.cache);
|
||||
menutyping.queryfn(menutyping.cache);
|
||||
}
|
||||
|
||||
void M_AbortVirtualKeyboard(void)
|
||||
{
|
||||
if (!menutyping.active)
|
||||
return;
|
||||
|
||||
menutyping.active = false;
|
||||
Z_Free(menutyping.cache);
|
||||
|
||||
if (currentMenu == menutyping.dummymenu)
|
||||
M_GoBack(0);
|
||||
}
|
||||
|
||||
static boolean M_IsTypingKey(INT32 key)
|
||||
|
|
@ -202,7 +215,7 @@ void M_MenuTypingInput(INT32 key)
|
|||
// Closing
|
||||
menutyping.menutypingfade--;
|
||||
if (!menutyping.menutypingfade)
|
||||
menutyping.active = false;
|
||||
M_AbortVirtualKeyboard();
|
||||
|
||||
return; // prevent inputs while closing the menu.
|
||||
}
|
||||
|
|
@ -405,11 +418,28 @@ void M_MenuTypingInput(INT32 key)
|
|||
}
|
||||
}
|
||||
|
||||
void M_OpenVirtualKeyboard(boolean gamepad)
|
||||
void M_OpenVirtualKeyboard(boolean gamepad, size_t cachelen, vkb_query_fn_t queryfn, menu_t *dummymenu)
|
||||
{
|
||||
menutyping.keyboardtyping = !gamepad;
|
||||
menutyping.active = true;
|
||||
menutyping.menutypingclose = false;
|
||||
|
||||
strlcpy(menutyping.cache, currentMenu->menuitems[itemOn].itemaction.cvar->string, MAXSTRINGLENGTH);
|
||||
menutyping.queryfn = queryfn;
|
||||
menutyping.dummymenu = dummymenu;
|
||||
menutyping.cachelen = cachelen;
|
||||
Z_Malloc(cachelen + 1, PU_STATIC, &menutyping.cache);
|
||||
strlcpy(menutyping.cache, queryfn(NULL), cachelen + 1);
|
||||
|
||||
if (dummymenu)
|
||||
{
|
||||
if (!menuactive)
|
||||
{
|
||||
M_StartControlPanel();
|
||||
dummymenu->prevMenu = NULL;
|
||||
}
|
||||
else
|
||||
dummymenu->prevMenu = currentMenu;
|
||||
|
||||
M_SetupNextMenu(dummymenu, true);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -228,7 +228,7 @@ void Obj_SpawnGachaBomRebound(mobj_t* source, mobj_t* target)
|
|||
{
|
||||
mobj_t *x = P_SpawnMobjFromMobjUnscaled(source, 0, 0, target->height / 2, MT_GACHABOM_REBOUND);
|
||||
|
||||
x->color = target->color;
|
||||
x->color = target->player ? target->player->skincolor : target->color;
|
||||
x->angle = angle;
|
||||
|
||||
if (!(gametyperules & GTR_BUMPERS) || battleprisons)
|
||||
|
|
|
|||
|
|
@ -2935,7 +2935,9 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
|
|||
{
|
||||
sfx = sfx_invind;
|
||||
}
|
||||
else if (K_IsBigger(target, inflictor) == true)
|
||||
else if (K_IsBigger(target, inflictor) == true &&
|
||||
// SPB bypasses grow (K_IsBigger handles NULL check)
|
||||
(type != DMG_EXPLODE || inflictor->type != MT_SPBEXPLOSION || !inflictor->movefactor))
|
||||
{
|
||||
sfx = sfx_grownd;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11290,7 +11290,7 @@ tic_t itemrespawntime[ITEMQUESIZE];
|
|||
size_t iquehead, iquetail;
|
||||
|
||||
#ifdef PARANOIA
|
||||
#define SCRAMBLE_REMOVED // Force debug build to crash when Removed mobj is accessed
|
||||
//#define SCRAMBLE_REMOVED // Force debug build to crash when Removed mobj is accessed
|
||||
#endif
|
||||
void P_RemoveMobj(mobj_t *mobj)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -6286,6 +6286,14 @@ static boolean P_UnArchiveSPGame(savebuffer_t *save)
|
|||
{
|
||||
UINT32 val = READUINT32(save->p);
|
||||
|
||||
if (roundqueue.entries[i].rankrestricted && roundqueue.position != i+1)
|
||||
{
|
||||
// If this is a Sealed Star that hasn't yet been
|
||||
// reached, don't be picky about divergance. Just
|
||||
// use the base game without question. ~toast 010324
|
||||
continue;
|
||||
}
|
||||
|
||||
mapnum = roundqueue.entries[i].mapnum;
|
||||
if (mapnum < nummapheaders && mapheaderinfo[mapnum] != NULL)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -458,6 +458,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 num)
|
|||
mapheaderinfo[num]->palette = UINT16_MAX;
|
||||
mapheaderinfo[num]->encorepal = UINT16_MAX;
|
||||
mapheaderinfo[num]->numlaps = NUMLAPS_DEFAULT;
|
||||
mapheaderinfo[num]->lapspersection = 1;
|
||||
mapheaderinfo[num]->levelselect = 0;
|
||||
mapheaderinfo[num]->levelflags = 0;
|
||||
mapheaderinfo[num]->menuflags = 0;
|
||||
|
|
|
|||
|
|
@ -1178,9 +1178,8 @@ void P_Ticker(boolean run)
|
|||
{
|
||||
G_WriteAllGhostTics();
|
||||
|
||||
if (cv_recordmultiplayerdemos.value && (demo.savemode == DSM_NOTSAVING || demo.savemode == DSM_WILLAUTOSAVE))
|
||||
if (demo.savebutton && demo.savebutton + 3*TICRATE < leveltime)
|
||||
G_CheckDemoTitleEntry();
|
||||
if (cv_recordmultiplayerdemos.value && demo.savebutton && demo.savebutton + 3*TICRATE < leveltime)
|
||||
G_CheckDemoTitleEntry();
|
||||
}
|
||||
else if (demo.playback) // Use Ghost data for consistency checks.
|
||||
{
|
||||
|
|
|
|||
24
src/p_user.c
24
src/p_user.c
|
|
@ -296,9 +296,29 @@ UINT8 P_GetNextEmerald(void)
|
|||
{
|
||||
cupheader_t *cup = NULL;
|
||||
|
||||
if (grandprixinfo.gp == true)
|
||||
if (grandprixinfo.gp == true && grandprixinfo.cup)
|
||||
{
|
||||
cup = grandprixinfo.cup;
|
||||
if (gamedata->sealedswaps[GDMAX_SEALEDSWAPS-1] != NULL // all found
|
||||
|| grandprixinfo.cup->id >= basenumkartcupheaders // custom content
|
||||
|| M_SecretUnlocked(SECRET_SPECIALATTACK, false)) // true order
|
||||
{
|
||||
cup = grandprixinfo.cup;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Determine order from sealedswaps.
|
||||
UINT8 i;
|
||||
for (i = 0; (i < GDMAX_SEALEDSWAPS && gamedata->sealedswaps[i]); i++)
|
||||
{
|
||||
if (gamedata->sealedswaps[i] != grandprixinfo.cup)
|
||||
continue;
|
||||
|
||||
// Repeat visit, grab the same ID.
|
||||
break;
|
||||
}
|
||||
|
||||
return i+1;
|
||||
}
|
||||
}
|
||||
|
||||
if (cup == NULL)
|
||||
|
|
|
|||
|
|
@ -1617,7 +1617,11 @@ void R_RenderPlayerView(void)
|
|||
{
|
||||
if (top > bot)
|
||||
std::swap(top, bot);
|
||||
UINT8* p = &screens[0][x + top * vid.width];
|
||||
if (top < 0)
|
||||
top = 0;
|
||||
if (bot > viewheight-1)
|
||||
bot = viewheight-1;
|
||||
UINT8* p = &topleft[x + top * vid.width];
|
||||
while (top <= bot)
|
||||
{
|
||||
*p = 35;
|
||||
|
|
@ -1634,7 +1638,7 @@ void R_RenderPlayerView(void)
|
|||
INT32 bottom = pl->bottom[pl->minx];
|
||||
span(pl->minx, top, bottom);
|
||||
span(pl->maxx, pl->top[pl->maxx], pl->bottom[pl->maxx]);
|
||||
for (INT32 x = pl->minx + 1; x < pl->maxx; ++x)
|
||||
for (INT32 x = pl->minx + 1; x < std::min(pl->maxx, viewwidth); ++x)
|
||||
{
|
||||
INT32 new_top = pl->top[x];
|
||||
INT32 new_bottom = pl->bottom[x];
|
||||
|
|
@ -1668,14 +1672,14 @@ void R_RenderPlayerView(void)
|
|||
INT32 width = (portal->end - portal->start);
|
||||
INT32 i;
|
||||
|
||||
for (i = 0; i < width; ++i)
|
||||
for (i = 0; i < std::min(width, viewwidth); ++i)
|
||||
{
|
||||
INT32 yl = std::max(portal->ceilingclip[i] + 1, 0);
|
||||
INT32 yh = std::min(static_cast<INT32>(portal->floorclip[i]), viewheight);
|
||||
|
||||
for (; yl < yh; ++yl)
|
||||
{
|
||||
screens[0][portal->start + i + (yl * vid.width)] = pal;
|
||||
topleft[portal->start + i + (yl * vid.width)] = pal;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,18 @@ static void R_UpdatePlaneRipple(drawspandata_t* ds)
|
|||
|
||||
static void R_SetSlopePlaneVectors(drawspandata_t* ds, visplane_t *pl, INT32 y, fixed_t xoff, fixed_t yoff);
|
||||
|
||||
static bool R_CheckMapPlane(const char* funcname, INT32 y, INT32 x1, INT32 x2)
|
||||
{
|
||||
if (x1 == x2)
|
||||
return true;
|
||||
|
||||
if (x1 < x2 && x1 >= 0 && x2 < viewwidth && y >= 0 && y < viewheight)
|
||||
return true;
|
||||
|
||||
CONS_Debug(DBG_RENDER, "%s: x1=%d, x2=%d at y=%d\n", funcname, x1, x2, y);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void R_MapPlane(drawspandata_t *ds, spandrawfunc_t *spanfunc, INT32 y, INT32 x1, INT32 x2, boolean allow_parallel)
|
||||
{
|
||||
ZoneScoped;
|
||||
|
|
@ -133,13 +145,8 @@ static void R_MapPlane(drawspandata_t *ds, spandrawfunc_t *spanfunc, INT32 y, IN
|
|||
fixed_t distance = 0, span;
|
||||
size_t pindex;
|
||||
|
||||
#ifdef RANGECHECK
|
||||
if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y > viewheight)
|
||||
I_Error("R_MapPlane: %d, %d at %d", x1, x2, y);
|
||||
#endif
|
||||
|
||||
if (x1 >= vid.width)
|
||||
x1 = vid.width - 1;
|
||||
if (!R_CheckMapPlane(__func__, y, x1, x2))
|
||||
return;
|
||||
|
||||
angle = (ds->currentplane->viewangle + ds->currentplane->plangle)>>ANGLETOFINESHIFT;
|
||||
planecos = FINECOSINE(angle);
|
||||
|
|
@ -216,13 +223,9 @@ static void R_MapPlane(drawspandata_t *ds, spandrawfunc_t *spanfunc, INT32 y, IN
|
|||
static void R_MapTiltedPlane(drawspandata_t *ds, void(*spanfunc)(drawspandata_t*), INT32 y, INT32 x1, INT32 x2, boolean allow_parallel)
|
||||
{
|
||||
ZoneScoped;
|
||||
#ifdef RANGECHECK
|
||||
if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y >= viewheight || y < 0)
|
||||
I_Error("R_MapTiltedPlane: %d, %d at %d", x1, x2, y);
|
||||
#endif
|
||||
|
||||
if (x1 >= vid.width)
|
||||
x1 = vid.width - 1;
|
||||
if (!R_CheckMapPlane(__func__, y, x1, x2))
|
||||
return;
|
||||
|
||||
// Water ripple effect
|
||||
if (ds->planeripple.active)
|
||||
|
|
|
|||
|
|
@ -1303,36 +1303,6 @@ static void ST_overlayDrawer(void)
|
|||
K_DrawMidVote();
|
||||
}
|
||||
|
||||
void ST_DrawDemoTitleEntry(void)
|
||||
{
|
||||
static UINT8 anim = 0;
|
||||
char *nametodraw;
|
||||
|
||||
anim++;
|
||||
anim %= 8;
|
||||
|
||||
nametodraw = demo.titlename;
|
||||
while (V_StringWidth(nametodraw, 0) > MAXSTRINGLENGTH*8 - 8)
|
||||
nametodraw++;
|
||||
|
||||
#define x (BASEVIDWIDTH/2 - 139)
|
||||
#define y (BASEVIDHEIGHT/2)
|
||||
M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1);
|
||||
V_DrawString(x + 8, y + 12, 0, nametodraw);
|
||||
if (anim < 4)
|
||||
V_DrawCharacter(x + 8 + V_StringWidth(nametodraw, 0), y + 12,
|
||||
'_' | 0x80, false);
|
||||
|
||||
M_DrawTextBox(x + 30, y - 24, 26, 1);
|
||||
V_DrawString(x + 38, y - 16, 0, "Enter the name of the replay.");
|
||||
|
||||
M_DrawTextBox(x + 50, y + 20, 20, 1);
|
||||
V_DrawThinString(x + 58, y + 28, 0, "Escape - Cancel");
|
||||
V_DrawRightAlignedThinString(x + 220, y + 28, 0, "Enter - Confirm");
|
||||
#undef x
|
||||
#undef y
|
||||
}
|
||||
|
||||
// MayonakaStatic: draw Midnight Channel's TV-like borders
|
||||
static void ST_MayonakaStatic(void)
|
||||
{
|
||||
|
|
@ -1487,6 +1457,15 @@ void ST_DrawServerSplash(boolean timelimited)
|
|||
}
|
||||
}
|
||||
|
||||
void ST_DrawSaveReplayHint(INT32 flags)
|
||||
{
|
||||
V_DrawRightAlignedThinString(
|
||||
BASEVIDWIDTH - 2, 2,
|
||||
flags|V_YELLOWMAP,
|
||||
demo.willsave ? "Replay will be saved. \xAB" "Change title" : "\xAB" "or " "\xAE" "Save replay"
|
||||
);
|
||||
}
|
||||
|
||||
static fixed_t ST_CalculateFadeIn(player_t *player)
|
||||
{
|
||||
const tic_t length = TICRATE/4;
|
||||
|
|
@ -1626,26 +1605,6 @@ void ST_Drawer(void)
|
|||
INT32 flags = V_SNAPTOTOP | V_SNAPTORIGHT |
|
||||
(Easing_Linear(min(t, fadeLength) * FRACUNIT / fadeLength, 9, 0) << V_ALPHASHIFT);
|
||||
|
||||
switch (demo.savemode)
|
||||
{
|
||||
case DSM_NOTSAVING:
|
||||
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, flags|V_YELLOWMAP, "\xAB" "or " "\xAE" "Save replay");
|
||||
break;
|
||||
|
||||
case DSM_WILLAUTOSAVE:
|
||||
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, flags|V_YELLOWMAP, "Replay will be saved. \xAB" "Change title");
|
||||
break;
|
||||
|
||||
case DSM_WILLSAVE:
|
||||
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, flags|V_YELLOWMAP, "Replay will be saved.");
|
||||
break;
|
||||
|
||||
case DSM_TITLEENTRY:
|
||||
ST_DrawDemoTitleEntry();
|
||||
break;
|
||||
|
||||
default: // Don't render anything
|
||||
break;
|
||||
}
|
||||
ST_DrawSaveReplayHint(flags);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,9 +33,6 @@ extern "C" {
|
|||
// Called by main loop.
|
||||
void ST_Ticker(boolean run);
|
||||
|
||||
// Called when naming a replay.
|
||||
void ST_DrawDemoTitleEntry(void);
|
||||
|
||||
#ifdef HAVE_DISCORDRPC
|
||||
// Called when you have Discord asks
|
||||
void ST_AskToJoinEnvelope(void);
|
||||
|
|
@ -79,6 +76,7 @@ extern tic_t lt_exitticker, lt_endtime;
|
|||
extern tic_t lt_fade;
|
||||
|
||||
void ST_DrawServerSplash(boolean timelimited);
|
||||
void ST_DrawSaveReplayHint(INT32 flags);
|
||||
|
||||
// return if player a is in the same team as player b
|
||||
boolean ST_SameTeam(player_t *a, player_t *b);
|
||||
|
|
|
|||
|
|
@ -2702,9 +2702,11 @@ void V_DrawStringScaled(
|
|||
c -= font->start;
|
||||
if (V_CharacterValid(font, c) == true)
|
||||
{
|
||||
// Remove offsets from patch
|
||||
fixed_t patchxofs = SHORT (font->font[c]->leftoffset) * dupx * FRACUNIT;
|
||||
cw = SHORT (font->font[c]->width) * dupx;
|
||||
cxoff = (*fontspec.dim_fn)(scale, fontspec.chw, hchw, dupx, &cw);
|
||||
V_DrawFixedPatch(cx + cxoff, cy + cyoff, scale,
|
||||
V_DrawFixedPatch(cx + cxoff + patchxofs, cy + cyoff, scale,
|
||||
flags, font->font[c], colormap);
|
||||
cx += cw;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -80,7 +80,6 @@ static INT32 powertype = PWRLV_DISABLED;
|
|||
static INT32 intertic;
|
||||
static INT32 endtic = -1;
|
||||
static INT32 sorttic = -1;
|
||||
static INT32 replayprompttic;
|
||||
|
||||
static fixed_t mqscroll = 0;
|
||||
static fixed_t chkscroll = 0;
|
||||
|
|
@ -1690,35 +1689,8 @@ skiptallydrawer:
|
|||
}
|
||||
|
||||
finalcounter:
|
||||
{
|
||||
if ((modeattacking == ATTACKING_NONE) && (demo.recording || demo.savemode == demovars_s::DSM_SAVED) && !demo.playback)
|
||||
{
|
||||
switch (demo.savemode)
|
||||
{
|
||||
case demovars_s::DSM_NOTSAVING:
|
||||
{
|
||||
INT32 buttonx = BASEVIDWIDTH;
|
||||
INT32 buttony = 2;
|
||||
|
||||
K_drawButtonAnim(buttonx - 76, buttony, 0, kp_button_b[1], replayprompttic);
|
||||
V_DrawRightAlignedThinString(buttonx - 55, buttony, highlightflags, "or");
|
||||
K_drawButtonAnim(buttonx - 55, buttony, 0, kp_button_x[1], replayprompttic);
|
||||
V_DrawRightAlignedThinString(buttonx - 2, buttony, highlightflags, "Save replay");
|
||||
break;
|
||||
}
|
||||
case demovars_s::DSM_SAVED:
|
||||
V_DrawRightAlignedThinString(BASEVIDWIDTH - 2, 2, highlightflags, "Replay saved!");
|
||||
break;
|
||||
|
||||
case demovars_s::DSM_TITLEENTRY:
|
||||
ST_DrawDemoTitleEntry();
|
||||
break;
|
||||
|
||||
default: // Don't render any text here
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((modeattacking == ATTACKING_NONE) && demo.recording)
|
||||
ST_DrawSaveReplayHint(0);
|
||||
|
||||
if (Y_CanSkipIntermission())
|
||||
{
|
||||
|
|
@ -1754,16 +1726,7 @@ void Y_Ticker(void)
|
|||
return;
|
||||
|
||||
if (demo.recording)
|
||||
{
|
||||
if (demo.savemode == demovars_s::DSM_NOTSAVING)
|
||||
{
|
||||
replayprompttic++;
|
||||
G_CheckDemoTitleEntry();
|
||||
}
|
||||
|
||||
if (demo.savemode == demovars_s::DSM_WILLSAVE || demo.savemode == demovars_s::DSM_WILLAUTOSAVE)
|
||||
G_SaveDemo();
|
||||
}
|
||||
G_CheckDemoTitleEntry();
|
||||
|
||||
// Check for pause or menu up in single player
|
||||
if (paused || P_AutoPause())
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue