Merge branch 'master' into duel-stuff

This commit is contained in:
Sally Coolatta 2022-10-01 21:46:02 -04:00
commit d19924d96d
57 changed files with 2697 additions and 1108 deletions

View file

@ -195,6 +195,14 @@ passthru_opts:=
# separate suffix with an underscore # separate suffix with an underscore
exesuffix:=$(call _,$(EXESUFFIX)) exesuffix:=$(call _,$(EXESUFFIX))
# If there are uncommitted changes
# -uno: disregard untracked files
# Warning: this can only be accurate for comptime.c since
# that file is always recompiled!
ifneq ($(shell git status --porcelain -uno),)
opts+=-DCOMPVERSION_UNCOMMITTED
endif
include Makefile.d/platform.mk include Makefile.d/platform.mk
include Makefile.d/features.mk include Makefile.d/features.mk
include Makefile.d/versions.mk include Makefile.d/versions.mk

View file

@ -454,7 +454,7 @@ boolean AM_Responder(event_t *ev)
{ {
INT32 rc = false; INT32 rc = false;
if (devparm || cv_debug) // only automap in Debug Tails 01-19-2001 if (devparm || cht_debug) // only automap in Debug Tails 01-19-2001
{ {
if (!automapactive) if (!automapactive)
{ {
@ -625,7 +625,7 @@ static inline void AM_doFollowPlayer(void)
*/ */
void AM_Ticker(void) void AM_Ticker(void)
{ {
if (!cv_debug) if (!cht_debug)
AM_Stop(); AM_Stop();
if (dedicated || !automapactive) if (dedicated || !automapactive)

View file

@ -139,12 +139,78 @@ FUNCINLINE static ATTRINLINE UINT32 readulong(void *ptr)
#undef DEALIGNED #undef DEALIGNED
#define WRITESTRINGN(p,s,n) do { size_t tmp_i = 0; for (; tmp_i < n && s[tmp_i] != '\0'; tmp_i++) WRITECHAR(p, s[tmp_i]); if (tmp_i < n) WRITECHAR(p, '\0');} while (0) #define WRITESTRINGN(p, s, n) do { \
#define WRITESTRING(p,s) do { size_t tmp_i = 0; for (; s[tmp_i] != '\0'; tmp_i++) WRITECHAR(p, s[tmp_i]); WRITECHAR(p, '\0');} while (0) size_t tmp_i; \
#define WRITEMEM(p,s,n) do { memcpy(p, s, n); p += n; } while (0) \
for (tmp_i = 0; tmp_i < n && s[tmp_i] != '\0'; tmp_i++) \
WRITECHAR(p, s[tmp_i]); \
\
if (tmp_i < n) \
WRITECHAR(p, '\0'); \
} while (0)
#define SKIPSTRING(p) while (READCHAR(p) != '\0') #define WRITESTRINGL(p, s, n) do { \
size_t tmp_i; \
\
for (tmp_i = 0; tmp_i < n - 1 && s[tmp_i] != '\0'; tmp_i++) \
WRITECHAR(p, s[tmp_i]); \
\
WRITECHAR(p, '\0'); \
} while (0)
#define READSTRINGN(p,s,n) do { size_t tmp_i = 0; for (; tmp_i < n && (s[tmp_i] = READCHAR(p)) != '\0'; tmp_i++); s[tmp_i] = '\0';} while (0) #define WRITESTRING(p, s) do { \
#define READSTRING(p,s) do { size_t tmp_i = 0; for (; (s[tmp_i] = READCHAR(p)) != '\0'; tmp_i++); s[tmp_i] = '\0';} while (0) size_t tmp_i; \
#define READMEM(p,s,n) do { memcpy(s, p, n); p += n; } while (0) \
for (tmp_i = 0; s[tmp_i] != '\0'; tmp_i++) \
WRITECHAR(p, s[tmp_i]); \
\
WRITECHAR(p, '\0'); \
} while (0)
#define WRITEMEM(p, s, n) do { \
memcpy(p, s, n); \
p += n; \
} while (0)
#define SKIPSTRING(p) while (READCHAR(p) != '\0')
#define SKIPSTRINGN(p, n) do { \
size_t tmp_i = 0; \
\
while (tmp_i < n && READCHAR(p) != '\0') \
tmp_i++; \
} while (0)
#define SKIPSTRINGL(p, n) SKIPSTRINGN(p, n)
#define READSTRINGN(p, s, n) do { \
size_t tmp_i = 0; \
\
while (tmp_i < n && (s[tmp_i] = READCHAR(p)) != '\0') \
tmp_i++; \
\
s[tmp_i] = '\0'; \
} while (0)
#define READSTRINGL(p, s, n) do { \
size_t tmp_i = 0; \
\
while (tmp_i < n - 1 && (s[tmp_i] = READCHAR(p)) != '\0') \
tmp_i++; \
\
s[tmp_i] = '\0'; \
} while (0)
#define READSTRING(p, s) do { \
size_t tmp_i = 0; \
\
while ((s[tmp_i] = READCHAR(p)) != '\0') \
tmp_i++; \
\
s[tmp_i] = '\0'; \
} while (0)
#define READMEM(p, s, n) do { \
memcpy(s, p, n); \
p += n; \
} while (0)

View file

@ -37,6 +37,7 @@
#include "r_data.h" // Color_cons_t #include "r_data.h" // Color_cons_t
#include "r_skins.h" #include "r_skins.h"
#include "m_random.h" #include "m_random.h"
#include "p_local.h" // P_ResetPlayerCheats
//======== //========
// protos. // protos.
@ -1891,7 +1892,6 @@ void CV_CheatsChanged(void)
else else
{ {
consvar_t *cvar; consvar_t *cvar;
UINT8 i;
// Set everything back to default. // Set everything back to default.
for (cvar = consvar_vars; cvar; cvar = cvar->next) for (cvar = consvar_vars; cvar; cvar = cvar->next)
@ -1899,15 +1899,9 @@ void CV_CheatsChanged(void)
CV_SetCVar(cvar, cvar->defaultvalue, false); CV_SetCVar(cvar, cvar->defaultvalue, false);
// Reset any other cheat command effects here, as well. // Reset any other cheat command effects here, as well.
cv_debug = 0; cht_debug = 0;
for (i = 0; i < MAXPLAYERS; i++) P_ResetPlayerCheats();
{
if (!playeringame[i])
continue;
players[i].cheats = 0;
}
} }
} }

View file

@ -15,6 +15,13 @@ const char *comprevision = SRB2_COMP_REVISION;
#elif (defined(COMPVERSION)) #elif (defined(COMPVERSION))
#include "comptime.h" #include "comptime.h"
const int compuncommitted =
#if (defined(COMPVERSION_UNCOMMITTED))
1;
#else
0;
#endif
#else #else
const char *compbranch = "Unknown"; const char *compbranch = "Unknown";
const char *comprevision = "illegal"; const char *comprevision = "illegal";

View file

@ -1521,12 +1521,12 @@ void CONS_Alert(alerttype_t level, const char *fmt, ...)
CONS_Printf("%s", txt); CONS_Printf("%s", txt);
} }
void CONS_Debug(INT32 debugflags, const char *fmt, ...) void CONS_Debug(UINT32 debugflags, const char *fmt, ...)
{ {
va_list argptr; va_list argptr;
static char *txt = NULL; static char *txt = NULL;
if ((cv_debug & debugflags) != debugflags) if ((cht_debug & debugflags) != debugflags)
return; return;
if (txt == NULL) if (txt == NULL)

View file

@ -266,7 +266,7 @@ void SendNetXCmdForPlayer(UINT8 playerid, netxcmd_t id, const void *param, size_
{ {
if (localtextcmd[playerid][0]+2+nparam > MAXTEXTCMD) if (localtextcmd[playerid][0]+2+nparam > MAXTEXTCMD)
{ {
// for future reference: if (cv_debug) != debug disabled. // for future reference: if (cht_debug) != debug disabled.
CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[playerid][0], sizeu1(nparam)); CONS_Alert(CONS_ERROR, M_GetText("NetXCmd buffer full, cannot add netcmd %d! (size: %d, needed: %s)\n"), id, localtextcmd[playerid][0], sizeu1(nparam));
return; return;
} }
@ -895,8 +895,15 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
netbuffer->packettype = PT_SERVERINFO; netbuffer->packettype = PT_SERVERINFO;
netbuffer->u.serverinfo._255 = 255; netbuffer->u.serverinfo._255 = 255;
netbuffer->u.serverinfo.packetversion = PACKETVERSION; netbuffer->u.serverinfo.packetversion = PACKETVERSION;
netbuffer->u.serverinfo.version = VERSION; netbuffer->u.serverinfo.version = VERSION;
netbuffer->u.serverinfo.subversion = SUBVERSION; netbuffer->u.serverinfo.subversion = SUBVERSION;
#ifdef DEVELOP
memcpy(netbuffer->u.serverinfo.commit,
comprevision_abbrev_bin, GIT_SHA_ABBREV);
#endif
strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION, strncpy(netbuffer->u.serverinfo.application, SRB2APPLICATION,
sizeof netbuffer->u.serverinfo.application); sizeof netbuffer->u.serverinfo.application);
// return back the time value so client can compute their ping // return back the time value so client can compute their ping
@ -1681,6 +1688,35 @@ static boolean CL_ServerConnectionSearchTicker(tic_t *asksent)
if (client) if (client)
{ {
#ifdef DEVELOP
// Commits do not match? Do not connect!
if (memcmp(serverlist[i].info.commit,
comprevision_abbrev_bin,
GIT_SHA_ABBREV))
{
char theirs[GIT_SHA_ABBREV * 2 + 1];
UINT8 n;
for (n = 0; n < GIT_SHA_ABBREV; ++n)
{
sprintf(&theirs[n * 2], "%02hhx",
serverlist[i].info.commit[n]);
}
D_QuitNetGame();
CL_Reset();
D_StartTitle();
M_StartMessage(va(
"Your EXE differs from the server.\n"
" Yours: %.*s\n"
"Theirs: %s\n\n"
"Press ESC\n",
GIT_SHA_ABBREV * 2, comprevision, theirs), NULL, MM_NOTHING);
return false;
}
#endif
#ifdef HAVE_CURL #ifdef HAVE_CURL
if (serverlist[i].info.httpsource[0]) if (serverlist[i].info.httpsource[0])
strncpy(http_source, serverlist[i].info.httpsource, MAX_MIRROR_LENGTH); strncpy(http_source, serverlist[i].info.httpsource, MAX_MIRROR_LENGTH);

View file

@ -268,6 +268,9 @@ typedef struct
char application[MAXAPPLICATION]; char application[MAXAPPLICATION];
UINT8 version; UINT8 version;
UINT8 subversion; UINT8 subversion;
#ifdef DEVELOP
UINT8 commit[GIT_SHA_ABBREV];
#endif
UINT8 numberofplayer; UINT8 numberofplayer;
UINT8 maxplayer; UINT8 maxplayer;
UINT8 refusereason; // 0: joinable, 1: joins disabled, 2: full UINT8 refusereason; // 0: joinable, 1: joins disabled, 2: full

View file

@ -96,6 +96,10 @@
int VERSION; int VERSION;
int SUBVERSION; int SUBVERSION;
#ifdef DEVELOP
UINT8 comprevision_abbrev_bin[GIT_SHA_ABBREV];
#endif
#ifdef HAVE_DISCORDRPC #ifdef HAVE_DISCORDRPC
#include "discord.h" #include "discord.h"
#endif #endif
@ -962,7 +966,7 @@ void D_StartTitle(void)
splitscreen = 0; splitscreen = 0;
SplitScreen_OnChange(); SplitScreen_OnChange();
cv_debug = 0; cht_debug = 0;
emeralds = 0; emeralds = 0;
memset(&luabanks, 0, sizeof(luabanks)); memset(&luabanks, 0, sizeof(luabanks));
lastmaploaded = 0; lastmaploaded = 0;
@ -1170,6 +1174,20 @@ static void IdentifyVersion(void)
#endif #endif
} }
#ifdef DEVELOP
static void
D_AbbrevCommit (void)
{
UINT8 i;
for (i = 0; i < GIT_SHA_ABBREV; ++i)
{
sscanf(&comprevision[i * 2], "%2hhx",
&comprevision_abbrev_bin[i]);
}
}
#endif
static void static void
D_ConvertVersionNumbers (void) D_ConvertVersionNumbers (void)
{ {
@ -1194,6 +1212,10 @@ void D_SRB2Main(void)
/* break the version string into version numbers, for netplay */ /* break the version string into version numbers, for netplay */
D_ConvertVersionNumbers(); D_ConvertVersionNumbers();
#ifdef DEVELOP
D_AbbrevCommit();
#endif
// Print GPL notice for our console users (Linux) // Print GPL notice for our console users (Linux)
CONS_Printf( CONS_Printf(
"\n\nDr. Robotnik's Ring Racers\n" "\n\nDr. Robotnik's Ring Racers\n"

View file

@ -96,6 +96,7 @@ static void Got_DiscordInfo(UINT8 **cp, INT32 playernum);
static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum); static void Got_ScheduleTaskcmd(UINT8 **cp, INT32 playernum);
static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum); static void Got_ScheduleClearcmd(UINT8 **cp, INT32 playernum);
static void Got_Automatecmd(UINT8 **cp, INT32 playernum); static void Got_Automatecmd(UINT8 **cp, INT32 playernum);
static void Got_Cheat(UINT8 **cp, INT32 playernum);
static void PointLimit_OnChange(void); static void PointLimit_OnChange(void);
static void TimeLimit_OnChange(void); static void TimeLimit_OnChange(void);
@ -325,8 +326,6 @@ consvar_t cv_splitdevice = CVAR_INIT ("splitdevice", "Off", CV_SAVE, CV_OnOff, N
consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL); consvar_t cv_skipmapcheck = CVAR_INIT ("skipmapcheck", "Off", CV_SAVE, CV_OnOff, NULL);
INT32 cv_debug;
consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse); consvar_t cv_usemouse = CVAR_INIT ("use_mouse", "Off", CV_SAVE|CV_CALL,usemouse_cons_t, I_StartupMouse);
consvar_t cv_usejoystick[MAXSPLITSCREENPLAYERS] = { consvar_t cv_usejoystick[MAXSPLITSCREENPLAYERS] = {
@ -362,7 +361,6 @@ consvar_t cv_joyscale[MAXSPLITSCREENPLAYERS] = { //Alam: Dummy for save
#endif #endif
// SRB2kart // SRB2kart
consvar_t cv_superring = CVAR_INIT ("superring", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_sneaker = CVAR_INIT ("sneaker", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_sneaker = CVAR_INIT ("sneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_rocketsneaker = CVAR_INIT ("rocketsneaker", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_rocketsneaker = CVAR_INIT ("rocketsneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_invincibility = CVAR_INIT ("invincibility", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_invincibility = CVAR_INIT ("invincibility", "On", CV_NETVAR, CV_OnOff, NULL);
@ -372,7 +370,6 @@ consvar_t cv_orbinaut = CVAR_INIT ("orbinaut", "On", CV_NETVAR, CV_OnOff,
consvar_t cv_jawz = CVAR_INIT ("jawz", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_jawz = CVAR_INIT ("jawz", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_mine = CVAR_INIT ("mine", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_mine = CVAR_INIT ("mine", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_landmine = CVAR_INIT ("landmine", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_landmine = CVAR_INIT ("landmine", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_droptarget = CVAR_INIT ("droptarget", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_ballhog = CVAR_INIT ("ballhog", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_ballhog = CVAR_INIT ("ballhog", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_selfpropelledbomb = CVAR_INIT ("selfpropelledbomb", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_selfpropelledbomb = CVAR_INIT ("selfpropelledbomb", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_grow = CVAR_INIT ("grow", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_grow = CVAR_INIT ("grow", "On", CV_NETVAR, CV_OnOff, NULL);
@ -382,7 +379,10 @@ consvar_t cv_bubbleshield = CVAR_INIT ("bubbleshield", "On", CV_NETVAR, CV_O
consvar_t cv_flameshield = CVAR_INIT ("flameshield", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_flameshield = CVAR_INIT ("flameshield", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_hyudoro = CVAR_INIT ("hyudoro", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_hyudoro = CVAR_INIT ("hyudoro", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_pogospring = CVAR_INIT ("pogospring", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_pogospring = CVAR_INIT ("pogospring", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_superring = CVAR_INIT ("superring", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_kitchensink = CVAR_INIT ("kitchensink", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_kitchensink = CVAR_INIT ("kitchensink", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_droptarget = CVAR_INIT ("droptarget", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_gardentop = CVAR_INIT ("gardentop", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_dualsneaker = CVAR_INIT ("dualsneaker", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_dualsneaker = CVAR_INIT ("dualsneaker", "On", CV_NETVAR, CV_OnOff, NULL);
consvar_t cv_triplesneaker = CVAR_INIT ("triplesneaker", "On", CV_NETVAR, CV_OnOff, NULL); consvar_t cv_triplesneaker = CVAR_INIT ("triplesneaker", "On", CV_NETVAR, CV_OnOff, NULL);
@ -625,7 +625,8 @@ const char *netxcmdnames[MAXNETXCMD - 1] =
"PLAYSOUND", // XD_PLAYSOUND "PLAYSOUND", // XD_PLAYSOUND
"SCHEDULETASK", // XD_SCHEDULETASK "SCHEDULETASK", // XD_SCHEDULETASK
"SCHEDULECLEAR", // XD_SCHEDULECLEAR "SCHEDULECLEAR", // XD_SCHEDULECLEAR
"AUTOMATE" // XD_AUTOMATE "AUTOMATE", // XD_AUTOMATE
"CHEAT", // XD_CHEAT
}; };
// ========================================================================= // =========================================================================
@ -678,6 +679,8 @@ void D_RegisterServerCommands(void)
RegisterNetXCmd(XD_SCHEDULECLEAR, Got_ScheduleClearcmd); RegisterNetXCmd(XD_SCHEDULECLEAR, Got_ScheduleClearcmd);
RegisterNetXCmd(XD_AUTOMATE, Got_Automatecmd); RegisterNetXCmd(XD_AUTOMATE, Got_Automatecmd);
RegisterNetXCmd(XD_CHEAT, Got_Cheat);
// Remote Administration // Remote Administration
COM_AddCommand("password", Command_Changepassword_f); COM_AddCommand("password", Command_Changepassword_f);
COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin COM_AddCommand("login", Command_Login_f); // useful in dedicated to kick off remote admin
@ -1094,7 +1097,6 @@ void D_RegisterClientCommands(void)
COM_AddCommand("rteleport", Command_RTeleport_f); COM_AddCommand("rteleport", Command_RTeleport_f);
COM_AddCommand("skynum", Command_Skynum_f); COM_AddCommand("skynum", Command_Skynum_f);
COM_AddCommand("weather", Command_Weather_f); COM_AddCommand("weather", Command_Weather_f);
COM_AddCommand("toggletwod", Command_Toggletwod_f);
#ifdef _DEBUG #ifdef _DEBUG
COM_AddCommand("causecfail", Command_CauseCfail_f); COM_AddCommand("causecfail", Command_CauseCfail_f);
#endif #endif
@ -1383,7 +1385,7 @@ UINT8 CanChangeSkin(INT32 playernum)
return true; return true;
// Not in game, so you can change // Not in game, so you can change
if (players[playernum].spectator || players[playernum].playerstate == PST_DEAD || players[playernum].playerstate == PST_REBORN) if (players[playernum].spectator)
return true; return true;
// Check for freeeplay // Check for freeeplay
@ -1406,6 +1408,26 @@ UINT8 CanChangeSkin(INT32 playernum)
return true; return true;
} }
boolean CanChangeSkinWhilePlaying(INT32 playernum)
{
INT32 i;
// Force skin in effect.
if ((cv_forceskin.value != -1))
return false;
for (i = 0; i < MAXPLAYERS; ++i)
{
if (D_IsPlayerHumanAndGaming(i) &&
!P_IsLocalPlayer(&players[i]))
{
return CanChangeSkin(playernum);
}
}
return true;
}
static void ForceAllSkins(INT32 forcedskin) static void ForceAllSkins(INT32 forcedskin)
{ {
INT32 i, j; INT32 i, j;
@ -1731,40 +1753,65 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum)
#endif #endif
} }
void SendWeaponPref(UINT8 n) enum {
WP_KICKSTARTACCEL = 1<<0,
WP_SHRINKME = 1<<1,
};
void WeaponPref_Send(UINT8 ssplayer)
{ {
UINT8 buf[1]; UINT8 prefs = 0;
buf[0] = 0; if (cv_kickstartaccel[ssplayer].value)
prefs |= WP_KICKSTARTACCEL;
if (cv_kickstartaccel[n].value) if (cv_shrinkme[ssplayer].value)
buf[0] |= 1; prefs |= WP_SHRINKME;
if (cv_shrinkme[n].value) SendNetXCmdForPlayer(ssplayer, XD_WEAPONPREF, &prefs, 1);
buf[0] |= 2;
SendNetXCmdForPlayer(n, XD_WEAPONPREF, buf, 1);
} }
static void Got_WeaponPref(UINT8 **cp,INT32 playernum) void WeaponPref_Save(UINT8 **cp, INT32 playernum)
{ {
player_t *player = &players[playernum];
UINT8 prefs = 0;
if (player->pflags & PF_KICKSTARTACCEL)
prefs |= WP_KICKSTARTACCEL;
if (player->pflags & PF_SHRINKME)
prefs |= WP_SHRINKME;
WRITEUINT8(*cp, prefs);
}
void WeaponPref_Parse(UINT8 **cp, INT32 playernum)
{
player_t *player = &players[playernum];
UINT8 prefs = READUINT8(*cp); UINT8 prefs = READUINT8(*cp);
players[playernum].pflags &= ~(PF_KICKSTARTACCEL|PF_SHRINKME); player->pflags &= ~(PF_KICKSTARTACCEL|PF_SHRINKME);
if (prefs & 1) if (prefs & WP_KICKSTARTACCEL)
players[playernum].pflags |= PF_KICKSTARTACCEL; player->pflags |= PF_KICKSTARTACCEL;
if (prefs & 2) if (prefs & WP_SHRINKME)
players[playernum].pflags |= PF_SHRINKME; player->pflags |= PF_SHRINKME;
if (leveltime < 2) if (leveltime < 2)
{ {
// BAD HACK: No other place I tried to slot this in // BAD HACK: No other place I tried to slot this in
// made it work for the host when they initally host, // made it work for the host when they initally host,
// so this will have to do. // so this will have to do.
K_UpdateShrinkCheat(&players[playernum]); K_UpdateShrinkCheat(player);
} }
}
static void Got_WeaponPref(UINT8 **cp,INT32 playernum)
{
WeaponPref_Parse(cp, playernum);
// SEE ALSO g_demo.c // SEE ALSO g_demo.c
demo_extradata[playernum] |= DXD_WEAPONPREF; demo_extradata[playernum] |= DXD_WEAPONPREF;
@ -1952,7 +1999,7 @@ void D_SendPlayerConfig(UINT8 n)
UINT8 *p = buf; UINT8 *p = buf;
SendNameAndColor(n); SendNameAndColor(n);
SendWeaponPref(n); WeaponPref_Send(n);
if (pr != NULL) if (pr != NULL)
{ {
@ -1970,6 +2017,65 @@ void D_SendPlayerConfig(UINT8 n)
SendNetXCmdForPlayer(n, XD_POWERLEVEL, buf, p-buf); SendNetXCmdForPlayer(n, XD_POWERLEVEL, buf, p-buf);
} }
void D_Cheat(INT32 playernum, INT32 cheat, ...)
{
va_list ap;
UINT8 buf[64];
UINT8 *p = buf;
if (!CV_CheatsEnabled())
{
CONS_Printf("This cannot be used without cheats enabled.\n");
return;
}
WRITEUINT8(p, playernum);
WRITEUINT8(p, cheat);
va_start(ap, cheat);
#define COPY(writemacro, type) writemacro (p, va_arg(ap, type))
switch (cheat)
{
case CHEAT_SAVECHECKPOINT:
COPY(WRITEFIXED, fixed_t); // x
COPY(WRITEFIXED, fixed_t); // y
COPY(WRITEFIXED, fixed_t); // z
break;
case CHEAT_RINGS:
case CHEAT_LIVES:
// If you're confused why 'int' instead of
// 'SINT8', search online: 'default argument promotions'
COPY(WRITESINT8, int);
break;
case CHEAT_SCALE:
COPY(WRITEFIXED, fixed_t);
break;
case CHEAT_HURT:
COPY(WRITEINT32, INT32);
break;
case CHEAT_RELATIVE_TELEPORT:
COPY(WRITEFIXED, fixed_t);
COPY(WRITEFIXED, fixed_t);
COPY(WRITEFIXED, fixed_t);
break;
case CHEAT_DEVMODE:
COPY(WRITEUINT32, UINT32);
break;
}
#undef COPY
va_end(ap);
SendNetXCmd(XD_CHEAT, buf, p - buf);
}
// Only works for displayplayer, sorry! // Only works for displayplayer, sorry!
static void Command_ResetCamera_f(void) static void Command_ResetCamera_f(void)
{ {
@ -2805,7 +2911,7 @@ static void Command_Map_f(void)
newresetplayers = false; // if not forcing and gametypes is the same newresetplayers = false; // if not forcing and gametypes is the same
// don't use a gametype the map doesn't support // don't use a gametype the map doesn't support
if (cv_debug || option_force || cv_skipmapcheck.value) if (cht_debug || option_force || cv_skipmapcheck.value)
{ {
fromlevelselect = false; // The player wants us to trek on anyway. Do so. fromlevelselect = false; // The player wants us to trek on anyway. Do so.
} }
@ -4711,6 +4817,9 @@ static void Command_Version_f(void)
CONS_Printf("\x87" "DEVELOP " "\x80"); CONS_Printf("\x87" "DEVELOP " "\x80");
#endif #endif
if (compuncommitted)
CONS_Printf("\x85" "! UNCOMMITTED CHANGES ! " "\x80");
CONS_Printf("\n"); CONS_Printf("\n");
} }
@ -5409,6 +5518,175 @@ static void Got_Automatecmd(UINT8 **cp, INT32 playernum)
} }
} }
static void Got_Cheat(UINT8 **cp, INT32 playernum)
{
UINT8 targetPlayer = READUINT8(*cp);
cheat_t cheat = READUINT8(*cp);
player_t *player;
if (cheat >= NUMBER_OF_CHEATS || !CV_CheatsEnabled() || targetPlayer >= MAXPLAYERS ||
playernode[targetPlayer] != playernode[playernum])
{
CONS_Alert(CONS_WARNING,
M_GetText ("Illegal cheat command received from %s\n"),
player_names[playernum]);
return;
}
player = &players[targetPlayer];
switch (cheat)
{
case CHEAT_NOCLIP: {
const char *status = "on";
if (!P_MobjWasRemoved(player->mo))
{
player->mo->flags ^= MF_NOCLIP;
if (!(player->mo->flags & MF_NOCLIP))
{
status = "off";
}
}
CV_CheaterWarning(targetPlayer, va("noclip %s", status));
break;
}
case CHEAT_GOD: {
const char *status = (player->pflags & PF_GODMODE) ? "off" : "on";
player->pflags ^= PF_GODMODE;
CV_CheaterWarning(targetPlayer, va("GOD MODE %s", status));
break;
}
case CHEAT_SAVECHECKPOINT: {
fixed_t x = READFIXED(*cp);
fixed_t y = READFIXED(*cp);
fixed_t z = READFIXED(*cp);
player->respawn.pointx = x;
player->respawn.pointy = y;
player->respawn.pointz = z;
player->respawn.manual = true;
CV_CheaterWarning(targetPlayer, va("temporary checkpoint created at %d, %d, %d",
x / FRACUNIT, y / FRACUNIT, z / FRACUNIT));
break;
}
case CHEAT_RINGS: {
SINT8 rings = READSINT8(*cp);
// P_GivePlayerRings does value clamping
player->rings = 0;
P_GivePlayerRings(player, rings);
CV_CheaterWarning(targetPlayer, va("rings = %d", rings));
break;
}
case CHEAT_LIVES: {
SINT8 lives = READSINT8(*cp);
// P_GivePlayerLives does value clamping
player->lives = 0;
P_GivePlayerLives(player, lives);
CV_CheaterWarning(targetPlayer, va("lives = %d", lives));
break;
}
case CHEAT_SCALE: {
const fixed_t smin = FRACUNIT/100;
const fixed_t smax = 100*FRACUNIT;
fixed_t s = READFIXED(*cp);
float f;
s = min(max(smin, s), smax);
f = FIXED_TO_FLOAT(s);
if (!P_MobjWasRemoved(player->mo))
{
player->mo->destscale = s;
}
CV_CheaterWarning(targetPlayer, va("scale = %d%s", (int)f, M_Ftrim(FIXED_TO_FLOAT(s))));
break;
}
case CHEAT_FLIP: {
if (!P_MobjWasRemoved(player->mo))
{
player->mo->flags2 ^= MF2_OBJECTFLIP;
}
CV_CheaterWarning(targetPlayer, "invert gravity");
break;
}
case CHEAT_HURT: {
INT32 damage = READINT32(*cp);
if (!P_MobjWasRemoved(player->mo))
{
P_DamageMobj(player->mo, NULL, NULL, damage, DMG_NORMAL);
}
CV_CheaterWarning(targetPlayer, va("%d damage to me", damage));
break;
}
case CHEAT_RELATIVE_TELEPORT: {
fixed_t x = READFIXED(*cp);
fixed_t y = READFIXED(*cp);
fixed_t z = READFIXED(*cp);
float f[3] = {
FIXED_TO_FLOAT(x),
FIXED_TO_FLOAT(y),
FIXED_TO_FLOAT(z),
};
char t[3][9];
if (!P_MobjWasRemoved(player->mo))
{
P_MapStart();
P_SetOrigin(player->mo,
player->mo->x + x,
player->mo->y + y,
player->mo->z + z);
P_MapEnd();
S_StartSound(player->mo, sfx_mixup);
}
strlcpy(t[0], M_Ftrim(f[0]), sizeof t[0]);
strlcpy(t[1], M_Ftrim(f[1]), sizeof t[1]);
strlcpy(t[2], M_Ftrim(f[2]), sizeof t[2]);
CV_CheaterWarning(targetPlayer, va("relative teleport by %d%s, %d%s, %d%s",
(int)f[0], t[0], (int)f[1], t[1], (int)f[2], t[2]));
break;
}
case CHEAT_DEVMODE: {
UINT32 flags = READUINT32(*cp);
cht_debug = flags;
CV_CheaterWarning(targetPlayer, va("devmode %x", flags));
break;
}
case NUMBER_OF_CHEATS:
break;
}
}
/** Prints the number of displayplayers[0]. /** Prints the number of displayplayers[0].
* *
* \todo Possibly remove this; it was useful for debugging at one point. * \todo Possibly remove this; it was useful for debugging at one point.
@ -5464,7 +5742,7 @@ void Command_ExitGame_f(void)
splitscreen = 0; splitscreen = 0;
SplitScreen_OnChange(); SplitScreen_OnChange();
cv_debug = 0; cht_debug = 0;
emeralds = 0; emeralds = 0;
memset(&luabanks, 0, sizeof(luabanks)); memset(&luabanks, 0, sizeof(luabanks));
@ -6046,7 +6324,7 @@ static void Skin_OnChange(void)
return; return;
} }
if (CanChangeSkin(consoleplayer)) if (CanChangeSkinWhilePlaying(consoleplayer))
SendNameAndColor(0); SendNameAndColor(0);
else else
{ {
@ -6065,7 +6343,7 @@ static void Skin2_OnChange(void)
if (!Playing() || !splitscreen) if (!Playing() || !splitscreen)
return; // do whatever you want return; // do whatever you want
if (CanChangeSkin(g_localplayers[1])) if (CanChangeSkinWhilePlaying(g_localplayers[1]))
SendNameAndColor(1); SendNameAndColor(1);
else else
{ {
@ -6079,7 +6357,7 @@ static void Skin3_OnChange(void)
if (!Playing() || splitscreen < 2) if (!Playing() || splitscreen < 2)
return; // do whatever you want return; // do whatever you want
if (CanChangeSkin(g_localplayers[2])) if (CanChangeSkinWhilePlaying(g_localplayers[2]))
SendNameAndColor(2); SendNameAndColor(2);
else else
{ {
@ -6093,7 +6371,7 @@ static void Skin4_OnChange(void)
if (!Playing() || splitscreen < 3) if (!Playing() || splitscreen < 3)
return; // do whatever you want return; // do whatever you want
if (CanChangeSkin(g_localplayers[3])) if (CanChangeSkinWhilePlaying(g_localplayers[3]))
SendNameAndColor(3); SendNameAndColor(3);
else else
{ {

View file

@ -72,14 +72,38 @@ extern consvar_t cv_pause;
extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_maxplayers, cv_respawntime; extern consvar_t cv_restrictskinchange, cv_allowteamchange, cv_maxplayers, cv_respawntime;
// SRB2kart items // SRB2kart items
extern consvar_t cv_superring, cv_sneaker, cv_rocketsneaker, cv_invincibility, cv_banana; extern consvar_t
extern consvar_t cv_eggmanmonitor, cv_orbinaut, cv_jawz, cv_mine, cv_landmine, cv_droptarget; cv_sneaker,
extern consvar_t cv_ballhog, cv_selfpropelledbomb, cv_grow, cv_shrink; cv_rocketsneaker,
extern consvar_t cv_lightningshield, cv_bubbleshield, cv_flameshield; cv_invincibility,
extern consvar_t cv_hyudoro, cv_pogospring, cv_kitchensink; cv_banana,
cv_eggmanmonitor,
cv_orbinaut,
cv_jawz,
cv_mine,
cv_landmine,
cv_ballhog,
cv_selfpropelledbomb,
cv_grow,
cv_shrink,
cv_lightningshield,
cv_bubbleshield,
cv_flameshield,
cv_hyudoro,
cv_pogospring,
cv_superring,
cv_kitchensink,
cv_droptarget,
cv_gardentop;
extern consvar_t cv_dualsneaker, cv_triplesneaker, cv_triplebanana, cv_decabanana; extern consvar_t
extern consvar_t cv_tripleorbinaut, cv_quadorbinaut, cv_dualjawz; cv_dualsneaker,
cv_triplesneaker,
cv_triplebanana,
cv_decabanana,
cv_tripleorbinaut,
cv_quadorbinaut,
cv_dualjawz;
extern consvar_t cv_kartminimap; extern consvar_t cv_kartminimap;
extern consvar_t cv_kartcheck; extern consvar_t cv_kartcheck;
@ -179,6 +203,7 @@ typedef enum
XD_SCHEDULETASK, // 36 XD_SCHEDULETASK, // 36
XD_SCHEDULECLEAR, // 37 XD_SCHEDULECLEAR, // 37
XD_AUTOMATE, // 38 XD_AUTOMATE, // 38
XD_CHEAT, // 39
MAXNETXCMD MAXNETXCMD
} netxcmd_t; } netxcmd_t;
@ -230,7 +255,9 @@ void D_RegisterServerCommands(void);
void D_RegisterClientCommands(void); void D_RegisterClientCommands(void);
void CleanupPlayerName(INT32 playernum, const char *newname); void CleanupPlayerName(INT32 playernum, const char *newname);
boolean EnsurePlayerNameIsGood(char *name, INT32 playernum); boolean EnsurePlayerNameIsGood(char *name, INT32 playernum);
void SendWeaponPref(UINT8 n); void WeaponPref_Send(UINT8 ssplayer);
void WeaponPref_Save(UINT8 **cp, INT32 playernum);
void WeaponPref_Parse(UINT8 **cp, INT32 playernum);
void D_SendPlayerConfig(UINT8 n); void D_SendPlayerConfig(UINT8 n);
void Command_ExitGame_f(void); void Command_ExitGame_f(void);
void Command_Retry_f(void); void Command_Retry_f(void);
@ -278,7 +305,10 @@ void Automate_Clear(void);
extern UINT32 livestudioaudience_timer; extern UINT32 livestudioaudience_timer;
void LiveStudioAudience(void); void LiveStudioAudience(void);
void D_Cheat(INT32 playernum, INT32 cheat, ...);
// used for the player setup menu // used for the player setup menu
UINT8 CanChangeSkin(INT32 playernum); UINT8 CanChangeSkin(INT32 playernum);
boolean CanChangeSkinWhilePlaying(INT32 playernum);
#endif #endif

View file

@ -58,14 +58,15 @@ typedef enum
// //
typedef enum typedef enum
{ {
// free: 1<<0 to 1<<2 PF_GODMODE = 1<<0, // Immortal. No lightsnake from pits either
// free: 1<<1 and 1<<2
// Look back VFX has been spawned // Look back VFX has been spawned
// TODO: Is there a better way to track this? // TODO: Is there a better way to track this?
PF_GAINAX = 1<<3, PF_GAINAX = 1<<3,
// Accessibility and cheats PF_KICKSTARTACCEL = 1<<4, // Accessibility feature: Is accelerate in kickstart mode?
PF_KICKSTARTACCEL = 1<<4, // Is accelerate in kickstart mode?
// 1<<5 free // 1<<5 free
// 1<<6 free // 1<<6 free
@ -105,13 +106,6 @@ typedef enum
// up to 1<<31 is free // up to 1<<31 is free
} pflags_t; } pflags_t;
typedef enum
{
PC_GODMODE = 1,
PC_NOCLIP = 1<<1,
// up to 1<<31 is free
} pcheats_t;
typedef enum typedef enum
{ {
// Are animation frames playing? // Are animation frames playing?
@ -159,7 +153,8 @@ Run this macro, then #undef FOREACH afterward
FOREACH (POGOSPRING, 18),\ FOREACH (POGOSPRING, 18),\
FOREACH (SUPERRING, 19),\ FOREACH (SUPERRING, 19),\
FOREACH (KITCHENSINK, 20),\ FOREACH (KITCHENSINK, 20),\
FOREACH (DROPTARGET, 21) FOREACH (DROPTARGET, 21),\
FOREACH (GARDENTOP, 22)
typedef enum typedef enum
{ {
@ -187,6 +182,7 @@ typedef enum
KSHIELD_LIGHTNING = 1, KSHIELD_LIGHTNING = 1,
KSHIELD_BUBBLE = 2, KSHIELD_BUBBLE = 2,
KSHIELD_FLAME = 3, KSHIELD_FLAME = 3,
KSHIELD_TOP = 4,
NUMKARTSHIELDS NUMKARTSHIELDS
} kartshields_t; } kartshields_t;
@ -288,6 +284,8 @@ typedef enum
#define ITEMSCALE_GROW 1 #define ITEMSCALE_GROW 1
#define ITEMSCALE_SHRINK 2 #define ITEMSCALE_SHRINK 2
#define GARDENTOP_MAXGRINDTIME (45)
// player_t struct for all respawn variables // player_t struct for all respawn variables
typedef struct respawnvars_s typedef struct respawnvars_s
{ {
@ -302,6 +300,7 @@ typedef struct respawnvars_s
UINT32 distanceleft; // How far along the course to respawn you UINT32 distanceleft; // How far along the course to respawn you
tic_t dropdash; // Drop Dash charge timer tic_t dropdash; // Drop Dash charge timer
boolean truedeath; // Your soul has left your body boolean truedeath; // Your soul has left your body
boolean manual; // Respawn coords were manually set, please respawn exactly there
} respawnvars_t; } respawnvars_t;
// player_t struct for all bot variables // player_t struct for all bot variables
@ -372,7 +371,6 @@ typedef struct player_s
// Bit flags. // Bit flags.
// See pflags_t, above. // See pflags_t, above.
pflags_t pflags; pflags_t pflags;
pcheats_t cheats;
// playing animation. // playing animation.
panim_t panim; panim_t panim;
@ -446,7 +444,6 @@ typedef struct player_s
INT32 underwatertilt; INT32 underwatertilt;
fixed_t offroad; // In Super Mario Kart, going offroad has lee-way of about 1 second before you start losing speed fixed_t offroad; // In Super Mario Kart, going offroad has lee-way of about 1 second before you start losing speed
UINT8 waterskip; // Water skipping counter
UINT16 tiregrease; // Reduced friction timer after hitting a spring UINT16 tiregrease; // Reduced friction timer after hitting a spring
UINT16 springstars; // Spawn stars around a player when they hit a spring UINT16 springstars; // Spawn stars around a player when they hit a spring
@ -599,6 +596,8 @@ typedef struct player_s
UINT8 kickstartaccel; UINT8 kickstartaccel;
UINT8 stairjank; UINT8 stairjank;
UINT8 topdriftheld;
UINT8 topinfirst;
UINT8 shrinkLaserDelay; UINT8 shrinkLaserDelay;
@ -609,7 +608,4 @@ typedef struct player_s
#endif #endif
} player_t; } player_t;
// Value for infinite lives
#define INFLIVES 0x7F
#endif #endif

View file

@ -3740,6 +3740,14 @@ const char *const STATE_LIST[] = { // array length left dynamic for sanity testi
"S_FLAMESHIELDLINE3", "S_FLAMESHIELDLINE3",
"S_FLAMESHIELDFLASH", "S_FLAMESHIELDFLASH",
// Marble Garden Zone Spinning Top
"S_GARDENTOP_FLOATING",
"S_GARDENTOP_SINKING1",
"S_GARDENTOP_SINKING2",
"S_GARDENTOP_SINKING3",
"S_GARDENTOP_DEAD",
"S_GARDENTOPSPARK",
// Caked-Up Booty-Sheet Ghost // Caked-Up Booty-Sheet Ghost
"S_HYUDORO", "S_HYUDORO",
@ -5346,6 +5354,8 @@ const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity t
"MT_FLAMESHIELDUNDERLAY", "MT_FLAMESHIELDUNDERLAY",
"MT_FLAMESHIELDPAPER", "MT_FLAMESHIELDPAPER",
"MT_BUBBLESHIELDTRAP", "MT_BUBBLESHIELDTRAP",
"MT_GARDENTOP",
"MT_GARDENTOPSPARK",
"MT_HYUDORO", "MT_HYUDORO",
"MT_HYUDORO_CENTER", "MT_HYUDORO_CENTER",
@ -5651,7 +5661,7 @@ const char *const MOBJFLAG_LIST[] = {
// \tMF2_(\S+).*// (.+) --> \t"\1", // \2 // \tMF2_(\S+).*// (.+) --> \t"\1", // \2
const char *const MOBJFLAG2_LIST[] = { const char *const MOBJFLAG2_LIST[] = {
"AXIS", // It's a NiGHTS axis! (For faster checking) "AXIS", // It's a NiGHTS axis! (For faster checking)
"TWOD", // Moves like it's in a 2D level "\x01", // free: 1<<1 (name un-matchable)
"DONTRESPAWN", // Don't respawn this object! "DONTRESPAWN", // Don't respawn this object!
"\x01", // free: 1<<3 (name un-matchable) "\x01", // free: 1<<3 (name un-matchable)
"AUTOMATIC", // Thrown ring has automatic properties "AUTOMATIC", // Thrown ring has automatic properties
@ -5710,8 +5720,9 @@ const char *const MAPTHINGFLAG_LIST[4] = {
}; };
const char *const PLAYERFLAG_LIST[] = { const char *const PLAYERFLAG_LIST[] = {
// free: 1<<0 to 1<<2 (name un-matchable) "GODMODE",
"\x01",
// free: 1<<1 and 1<<2 (name un-matchable)
"\x01", "\x01",
"\x01", "\x01",
@ -6348,9 +6359,6 @@ struct int_const_s const INT_CONST[] = {
{"PA_DRIFT",PA_DRIFT}, {"PA_DRIFT",PA_DRIFT},
{"PA_HURT",PA_HURT}, {"PA_HURT",PA_HURT},
// Value for infinite lives
{"INFLIVES",INFLIVES},
// Got Flags, for player->gotflag! // Got Flags, for player->gotflag!
// Used to be MF_ for some stupid reason, now they're GF_ to stop them looking like mobjflags // Used to be MF_ for some stupid reason, now they're GF_ to stop them looking like mobjflags
{"GF_REDFLAG",GF_REDFLAG}, {"GF_REDFLAG",GF_REDFLAG},
@ -6670,6 +6678,7 @@ struct int_const_s const INT_CONST[] = {
{"KSHIELD_LIGHTNING",KSHIELD_LIGHTNING}, {"KSHIELD_LIGHTNING",KSHIELD_LIGHTNING},
{"KSHIELD_BUBBLE",KSHIELD_BUBBLE}, {"KSHIELD_BUBBLE",KSHIELD_BUBBLE},
{"KSHIELD_FLAME",KSHIELD_FLAME}, {"KSHIELD_FLAME",KSHIELD_FLAME},
{"KSHIELD_TOP",KSHIELD_TOP},
{"NUMKARTSHIELDS",NUMKARTSHIELDS}, {"NUMKARTSHIELDS",NUMKARTSHIELDS},
// kartspinoutflags_t // kartspinoutflags_t

View file

@ -480,7 +480,7 @@ typedef enum
void CONS_Printf(const char *fmt, ...) FUNCPRINTF; void CONS_Printf(const char *fmt, ...) FUNCPRINTF;
void CONS_Alert(alerttype_t level, const char *fmt, ...) FUNCDEBUG; void CONS_Alert(alerttype_t level, const char *fmt, ...) FUNCDEBUG;
void CONS_Debug(INT32 debugflags, const char *fmt, ...) FUNCDEBUG; void CONS_Debug(UINT32 debugflags, const char *fmt, ...) FUNCDEBUG;
// For help debugging functions. // For help debugging functions.
#define POTENTIALLYUNUSED CONS_Alert(CONS_WARNING, "(%s:%d) Unused code appears to be used.\n", __FILE__, __LINE__) #define POTENTIALLYUNUSED CONS_Alert(CONS_WARNING, "(%s:%d) Unused code appears to be used.\n", __FILE__, __LINE__)
@ -516,24 +516,47 @@ char *sizeu5(size_t num);
// d_main.c // d_main.c
extern int VERSION; extern int VERSION;
extern int SUBVERSION; extern int SUBVERSION;
extern boolean devparm; // development mode (-debug)
// d_netcmd.c
extern INT32 cv_debug;
#define DBG_BASIC 0x0001 #ifdef DEVELOP
#define DBG_DETAILED 0x0002 // 4 bytes handles 8 characters of a git object SHA. At
#define DBG_PLAYER 0x0004 // around 20k commits, we only need 6 characters for a unique
#define DBG_RENDER 0x0008 // abbreviation. Maybe in another 20k commits, more than 8
#define DBG_NIGHTSBASIC 0x0010 // characters will be required! =P
#define DBG_NIGHTS 0x0020 // P.S. 8 is also what comptime generates
#define DBG_POLYOBJ 0x0040 #define GIT_SHA_ABBREV (4)
#define DBG_GAMELOGIC 0x0080 extern UINT8 comprevision_abbrev_bin[GIT_SHA_ABBREV];
#define DBG_NETPLAY 0x0100 #endif
#define DBG_MEMORY 0x0200
#define DBG_SETUP 0x0400 extern boolean devparm; // development mode (-debug)
#define DBG_LUA 0x0800
#define DBG_RANDOMIZER 0x1000 // m_cheat.c
#define DBG_VIEWMORPH 0x2000 extern UINT32 cht_debug;
typedef enum
{
DBG_NONE = 0x00000000,
DBG_BASIC = 0x00000001,
DBG_DETAILED = 0x00000002,
DBG_PLAYER = 0x00000004,
DBG_RENDER = 0x00000008,
//DBG_NIGHTSBASIC = 0x00000010, // free
//DBG_NIGHTS = 0x00000020, // free
DBG_POLYOBJ = 0x00000040,
DBG_GAMELOGIC = 0x00000080,
DBG_NETPLAY = 0x00000100,
DBG_MEMORY = 0x00000200,
DBG_SETUP = 0x00000400,
DBG_LUA = 0x00000800,
DBG_RNG = 0x00001000,
} debugFlags_t;
struct debugFlagNames_s
{
const char *str;
debugFlags_t flag;
};
extern struct debugFlagNames_s const debug_flag_names[];
// ======================= // =======================
// Misc stuff for later... // Misc stuff for later...
@ -613,6 +636,7 @@ UINT32 quickncasehash (const char *p, size_t n)
// Compile date and time and revision. // Compile date and time and revision.
extern const char *compdate, *comptime, *comprevision, *compbranch; extern const char *compdate, *comptime, *comprevision, *compbranch;
extern int compuncommitted;
// Disabled code and code under testing // Disabled code and code under testing
// None of these that are disabled in the normal build are guaranteed to work perfectly // None of these that are disabled in the normal build are guaranteed to work perfectly
@ -691,10 +715,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
#undef UPDATE_ALERT #undef UPDATE_ALERT
#endif #endif
/// - SRB2Kart options -
/// Camera always has noclip.
#define NOCLIPCAM
/// Other karma comeback modes /// Other karma comeback modes
//#define OTHERKARMAMODES //#define OTHERKARMAMODES

View file

@ -866,7 +866,7 @@ boolean F_CreditResponder(event_t *event)
return false; return false;
} }
/*if (!(timesBeaten) && !(netgame || multiplayer) && !cv_debug) /*if (!(timesBeaten) && !(netgame || multiplayer) && !cht_debug)
return false;*/ return false;*/
if (key != KEY_ESCAPE && key != KEY_ENTER && key != KEY_BACKSPACE) if (key != KEY_ESCAPE && key != KEY_ENTER && key != KEY_BACKSPACE)
@ -2006,6 +2006,8 @@ void F_TitleScreenDrawer(void)
#else // Regular build #else // Regular build
addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", VERSIONSTRING)); addtext(V_ALLOWLOWERCASE|V_TRANSLUCENT, va("%s", VERSIONSTRING));
#endif #endif
if (compuncommitted)
addtext(V_REDMAP|V_STRINGDANCE, "! UNCOMMITTED CHANGES !");
} }
#undef addtext #undef addtext
} }

View file

@ -364,20 +364,7 @@ void G_ReadDemoExtraData(void)
} }
if (extradata & DXD_WEAPONPREF) if (extradata & DXD_WEAPONPREF)
{ {
i = READUINT8(demo_p); WeaponPref_Parse(&demo_p, p);
players[p].pflags &= ~(PF_KICKSTARTACCEL|PF_SHRINKME);
if (i & 1)
players[p].pflags |= PF_KICKSTARTACCEL;
if (i & 2)
players[p].pflags |= PF_SHRINKME;
if (leveltime < 2)
{
// BAD HACK: No other place I tried to slot this in
// made it work for the host when they initally host,
// so this will have to do.
K_UpdateShrinkCheat(&players[p]);
}
//CONS_Printf("weaponpref is %d for player %d\n", i, p); //CONS_Printf("weaponpref is %d for player %d\n", i, p);
} }
@ -492,12 +479,7 @@ void G_WriteDemoExtraData(void)
} }
if (demo_extradata[i] & DXD_WEAPONPREF) if (demo_extradata[i] & DXD_WEAPONPREF)
{ {
UINT8 prefs = 0; WeaponPref_Save(&demo_p, i);
if (players[i].pflags & PF_KICKSTARTACCEL)
prefs |= 1;
if (players[i].pflags & PF_SHRINKME)
prefs |= 2;
WRITEUINT8(demo_p, prefs);
} }
} }

View file

@ -1304,22 +1304,22 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n)
static void weaponPrefChange(void) static void weaponPrefChange(void)
{ {
SendWeaponPref(0); WeaponPref_Send(0);
} }
static void weaponPrefChange2(void) static void weaponPrefChange2(void)
{ {
SendWeaponPref(1); WeaponPref_Send(1);
} }
static void weaponPrefChange3(void) static void weaponPrefChange3(void)
{ {
SendWeaponPref(2); WeaponPref_Send(2);
} }
static void weaponPrefChange4(void) static void weaponPrefChange4(void)
{ {
SendWeaponPref(3); WeaponPref_Send(3);
} }
// //
@ -2219,7 +2219,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
UINT32 followitem; UINT32 followitem;
INT32 pflags; INT32 pflags;
INT32 cheats;
UINT8 ctfteam; UINT8 ctfteam;
@ -2299,7 +2298,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
botrival = players[player].botvars.rival; botrival = players[player].botvars.rival;
pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE)); pflags = (players[player].pflags & (PF_WANTSTOJOIN|PF_KICKSTARTACCEL|PF_SHRINKME|PF_SHRINKACTIVE));
cheats = 0;
// SRB2kart // SRB2kart
if (betweenmaps || leveltime < introtime) if (betweenmaps || leveltime < introtime)
@ -2374,10 +2372,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
pflags |= (players[player].pflags & (PF_STASIS|PF_ELIMINATED|PF_NOCONTEST|PF_FAULT|PF_LOSTLIFE)); pflags |= (players[player].pflags & (PF_STASIS|PF_ELIMINATED|PF_NOCONTEST|PF_FAULT|PF_LOSTLIFE));
} }
// As long as we're not in multiplayer, carry over cheatcodes from map to map
if (!(netgame || multiplayer))
cheats = players[player].cheats;
if (!betweenmaps) if (!betweenmaps)
{ {
// Obliterate follower from existence (if valid memory) // Obliterate follower from existence (if valid memory)
@ -2393,7 +2387,6 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
p->roundscore = roundscore; p->roundscore = roundscore;
p->lives = lives; p->lives = lives;
p->pflags = pflags; p->pflags = pflags;
p->cheats = cheats;
p->ctfteam = ctfteam; p->ctfteam = ctfteam;
p->jointime = jointime; p->jointime = jointime;
p->splitscreenindex = splitscreenindex; p->splitscreenindex = splitscreenindex;
@ -4597,7 +4590,7 @@ void G_SaveGame(UINT32 slot, INT16 mapnum)
gameaction = ga_nothing; gameaction = ga_nothing;
if (cv_debug && saved) if (cht_debug && saved)
CONS_Printf(M_GetText("Game saved.\n")); CONS_Printf(M_GetText("Game saved.\n"));
else if (!saved) else if (!saved)
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename)); CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename));
@ -4699,7 +4692,7 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives)
} }
cleanup: cleanup:
if (cv_debug && saved) if (cht_debug && saved)
CONS_Printf(M_GetText("Game saved.\n")); CONS_Printf(M_GetText("Game saved.\n"));
else if (!saved) else if (!saved)
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename)); CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, (marathonmode ? liveeventbackup : savegamename));
@ -4784,9 +4777,6 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr
players[i].playerstate = PST_REBORN; players[i].playerstate = PST_REBORN;
memset(&players[i].respawn, 0, sizeof (players[i].respawn)); memset(&players[i].respawn, 0, sizeof (players[i].respawn));
// Clear cheatcodes too, just in case.
players[i].cheats = 0;
players[i].roundscore = 0; players[i].roundscore = 0;
if (resetplayer && !(multiplayer && demo.playback)) // SRB2Kart if (resetplayer && !(multiplayer && demo.playback)) // SRB2Kart

View file

@ -85,9 +85,9 @@ patch_t *framecounter;
patch_t *frameslash; // framerate stuff. Used in screen.c patch_t *frameslash; // framerate stuff. Used in screen.c
static player_t *plr; static player_t *plr;
boolean chat_on; // entering a chat message?
boolean hu_keystrokes; // :) boolean hu_keystrokes; // :)
static char w_chat[HU_MAXMSGLEN]; boolean chat_on; // entering a chat message?
static char w_chat[HU_MAXMSGLEN + 1];
static size_t c_input = 0; // let's try to make the chat input less shitty. static size_t c_input = 0; // let's try to make the chat input less shitty.
static boolean headsupactive = false; static boolean headsupactive = false;
boolean hu_showscores; // draw rankings boolean hu_showscores; // draw rankings
@ -487,7 +487,7 @@ void HU_AddChatText(const char *text, boolean playsound)
static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags) static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
{ {
char buf[254]; char buf[2 + HU_MAXMSGLEN + 1];
size_t numwords, ix; size_t numwords, ix;
char *msg = &buf[2]; char *msg = &buf[2];
const size_t msgspace = sizeof buf - 2; const size_t msgspace = sizeof buf - 2;
@ -567,7 +567,7 @@ static void DoSayCommand(SINT8 target, size_t usedargs, UINT8 flags)
} }
buf[0] = target; buf[0] = target;
newmsg = msg+5+spc; newmsg = msg+5+spc;
strlcpy(msg, newmsg, 252); strlcpy(msg, newmsg, HU_MAXMSGLEN + 1);
} }
SendNetXCmd(XD_SAY, buf, strlen(msg) + 1 + msg-buf); SendNetXCmd(XD_SAY, buf, strlen(msg) + 1 + msg-buf);
@ -697,7 +697,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
target = READSINT8(*p); target = READSINT8(*p);
flags = READUINT8(*p); flags = READUINT8(*p);
msg = (char *)*p; msg = (char *)*p;
SKIPSTRING(*p); SKIPSTRINGL(*p, HU_MAXMSGLEN + 1);
if ((cv_mute.value || flags & (HU_CSAY|HU_SHOUT)) && playernum != serverplayer && !(IsPlayerAdmin(playernum))) if ((cv_mute.value || flags & (HU_CSAY|HU_SHOUT)) && playernum != serverplayer && !(IsPlayerAdmin(playernum)))
{ {
@ -916,71 +916,6 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
#endif #endif
} }
// Handles key input and string input
//
static inline boolean HU_keyInChatString(char *s, char ch)
{
size_t l;
if ((ch >= HU_FONTSTART && ch <= HU_FONTEND && fontv[HU_FONT].font[ch-HU_FONTSTART])
|| ch == ' ') // Allow spaces, of course
{
l = strlen(s);
if (l < HU_MAXMSGLEN - 1)
{
if (c_input >= strlen(s)) // don't do anything complicated
{
s[l++] = ch;
s[l]=0;
}
else
{
// move everything past c_input for new characters:
size_t m = HU_MAXMSGLEN-1;
while (m>=c_input)
{
if (s[m])
s[m+1] = (s[m]);
if (m == 0) // prevent overflow
break;
m--;
}
s[c_input] = ch; // and replace this.
}
c_input++;
return true;
}
return false;
}
else if (ch == KEY_BACKSPACE)
{
size_t i = c_input;
if (c_input <= 0)
return false;
if (!s[i-1])
return false;
if (i >= strlen(s)-1)
{
s[strlen(s)-1] = 0;
c_input--;
return false;
}
for (; (i < HU_MAXMSGLEN); i++)
{
s[i-1] = s[i];
}
c_input--;
}
else if (ch != KEY_ENTER)
return false; // did not eat key
return true; // ate the key
}
// //
// //
static void HU_TickSongCredits(void) static void HU_TickSongCredits(void)
@ -1096,154 +1031,125 @@ void HU_Ticker(void)
} }
static boolean teamtalk = false; static boolean teamtalk = false;
static boolean justscrolleddown;
static boolean justscrolledup;
static INT16 typelines = 1; // number of drawfill lines we need when drawing the chat. it's some weird hack and might be one frame off but I'm lazy to make another loop.
// It's up here since it has to be reset when we open the chat.
// Clear spaces so we don't end up with messages only made out of emptiness static boolean HU_chatboxContainsOnlySpaces(void)
static boolean HU_clearChatSpaces(void)
{ {
size_t i = 0; // Used to just check our message size_t i;
char c; // current character we're iterating.
boolean nothingbutspaces = true;
for (; i < strlen(w_chat); i++) // iterate through message and eradicate all spaces that don't belong. for (i = 0; w_chat[i]; i++)
{ if (w_chat[i] != ' ')
c = w_chat[i]; return false;
if (!c)
break; // if there's nothing, it's safe to assume our message has ended, so let's not waste any more time here.
if (c != ' ') // Isn't a space return true;
{
nothingbutspaces = false;
}
}
return nothingbutspaces;
} }
// static void HU_sendChatMessage(void)
//
static void HU_queueChatChar(INT32 c)
{ {
// send automaticly the message (no more chat char) char buf[2 + HU_MAXMSGLEN + 1];
if (c == KEY_ENTER) char *msg = &buf[2];
size_t ci;
INT32 target = 0;
// if our message was nothing but spaces, don't send it.
if (HU_chatboxContainsOnlySpaces())
return;
// copy printable characters and terminating '\0' only.
for (ci = 2; w_chat[ci-2]; ci++)
{ {
char buf[2+256]; char c = w_chat[ci-2];
char *msg = &buf[2]; if (c >= ' ' && !(c & 0x80))
size_t i; buf[ci] = c;
size_t ci = 2; };
INT32 target = 0; buf[ci] = '\0';
if (HU_clearChatSpaces()) // Avoids being able to send empty messages, or something. memset(w_chat, '\0', sizeof(w_chat));
return; // If this returns true, that means our message was NOTHING but spaces, so don't send it period. c_input = 0;
do { // last minute mute check
c = w_chat[-2+ci++]; if (CHAT_MUTE)
if (!c || (c >= ' ' && !(c & 0x80))) // copy printable characters and terminating '\0' only. {
buf[ci-1]=c; HU_AddChatText(va("%s>ERROR: The chat is muted. You can't say anything.", "\x85"), false);
} while (c); return;
i = 0; }
for (;(i<HU_MAXMSGLEN);i++)
w_chat[i] = 0; // reset this.
c_input = 0; if (strlen(msg) > 4 && strnicmp(msg, "/pm", 3) == 0) // used /pm
{
INT32 spc = 1; // used if playernum[1] is a space.
char playernum[3];
const char *newmsg;
for (;(i<HU_MAXMSGLEN);i++) // what we're gonna do now is check if the player exists
w_chat[i] = 0; // reset this. // with that logic, characters 4 and 5 are our numbers:
c_input = 0; // teamtalk can't send PMs, just don't send it, else everyone would be able to see it, and no one wants to see your sex RP sicko.
if (teamtalk)
// last minute mute check
if (CHAT_MUTE)
{ {
HU_AddChatText(va("%s>ERROR: The chat is muted. You can't say anything.", "\x85"), false); HU_AddChatText(va("%sCannot send sayto in Say-Team.", "\x85"), false);
return; return;
} }
if (strlen(msg) > 4 && strnicmp(msg, "/pm", 3) == 0) // used /pm strncpy(playernum, msg+3, 3);
// check for undesirable characters in our "number"
if (!(isdigit(playernum[0]) && isdigit(playernum[1])))
{ {
INT32 spc = 1; // used if playernum[1] is a space. // check if playernum[1] is a space
char playernum[3]; if (playernum[1] == ' ')
const char *newmsg; spc = 0;
// let it slide
// what we're gonna do now is check if the player exists
// with that logic, characters 4 and 5 are our numbers:
// teamtalk can't send PMs, just don't send it, else everyone would be able to see it, and no one wants to see your sex RP sicko.
if (teamtalk)
{
HU_AddChatText(va("%sCannot send sayto in Say-Team.", "\x85"), false);
return;
}
strncpy(playernum, msg+3, 3);
// check for undesirable characters in our "number"
if (((playernum[0] < '0') || (playernum[0] > '9')) || ((playernum[1] < '0') || (playernum[1] > '9')))
{
// check if playernum[1] is a space
if (playernum[1] == ' ')
spc = 0;
// let it slide
else
{
HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<player num> \'.", false);
return;
}
}
// I'm very bad at C, I swear I am, additional checks eww!
if (spc != 0)
{
if (msg[5] != ' ')
{
HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<player num> \'.", false);
return;
}
}
target = atoi(playernum); // turn that into a number
//CONS_Printf("%d\n", target);
// check for target player, if it doesn't exist then we can't send the message!
if (target < MAXPLAYERS && playeringame[target]) // player exists
target++; // even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
else else
{ {
HU_AddChatText(va("\x82NOTICE: \x80Player %d does not exist.", target), false); // same HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<player num> \'.", false);
return; return;
} }
// we need to get rid of the /pm<player num>
newmsg = msg+5+spc;
strlcpy(msg, newmsg, 255);
} }
if (ci > 3) // don't send target+flags+empty message. // I'm very bad at C, I swear I am, additional checks eww!
if (spc != 0 && msg[5] != ' ')
{ {
if (teamtalk) HU_AddChatText("\x82NOTICE: \x80Invalid command format. Correct format is \'/pm<player num> \'.", false);
buf[0] = -1; // target return;
else
buf[0] = target;
buf[1] = ((server || IsPlayerAdmin(consoleplayer)) && cv_autoshout.value) ? HU_SHOUT : 0; // flags
SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1);
} }
return;
target = atoi(playernum); // turn that into a number
// check for target player, if it doesn't exist then we can't send the message!
if (target < MAXPLAYERS && playeringame[target]) // player exists
target++; // even though playernums are from 0 to 31, target is 1 to 32, so up that by 1 to have it work!
else
{
HU_AddChatText(va("\x82NOTICE: \x80Player %d does not exist.", target), false); // same
return;
}
// we need to get rid of the /pm<player num>
newmsg = msg+5+spc;
strlcpy(msg, newmsg, HU_MAXMSGLEN + 1);
}
if (ci > 2) // don't send target+flags+empty message.
{
if (teamtalk)
buf[0] = -1; // target
else
buf[0] = target;
buf[1] = ((server || IsPlayerAdmin(consoleplayer)) && cv_autoshout.value) ? HU_SHOUT : 0; // flags
SendNetXCmd(XD_SAY, buf, 2 + strlen(&buf[2]) + 1);
} }
} }
void HU_clearChatChars(void) void HU_clearChatChars(void)
{ {
size_t i = 0; memset(w_chat, '\0', sizeof(w_chat));
for (;i<HU_MAXMSGLEN;i++)
w_chat[i] = 0; // reset this.
chat_on = false; chat_on = false;
c_input = 0; c_input = 0;
I_UpdateMouseGrab(); I_UpdateMouseGrab();
} }
static boolean justscrolleddown;
static boolean justscrolledup;
static INT16 typelines = 1; // number of drawfill lines we need when drawing the chat. it's some weird hack and might be one frame off but I'm lazy to make another loop.
// It's up here since it has to be reset when we open the chat.
// //
// Returns true if key eaten // Returns true if key eaten
// //
@ -1327,14 +1233,16 @@ boolean HU_Responder(event_t *ev)
c = CON_ShiftChar(c); c = CON_ShiftChar(c);
// pasting. pasting is cool. chat is a bit limited, though :( // pasting. pasting is cool. chat is a bit limited, though :(
if (((c == 'v' || c == 'V') && ctrldown) && !CHAT_MUTE) if ((c == 'v' || c == 'V') && ctrldown)
{ {
const char *paste = I_ClipboardPaste(); const char *paste;
size_t chatlen; size_t chatlen;
size_t pastelen; size_t pastelen;
// create a dummy string real quickly if (CHAT_MUTE)
return true;
paste = I_ClipboardPaste();
if (paste == NULL) if (paste == NULL)
return true; return true;
@ -1343,40 +1251,16 @@ boolean HU_Responder(event_t *ev)
if (chatlen+pastelen > HU_MAXMSGLEN) if (chatlen+pastelen > HU_MAXMSGLEN)
return true; // we can't paste this!! return true; // we can't paste this!!
if (c_input >= strlen(w_chat)) // add it at the end of the string. memmove(&w_chat[c_input + pastelen], &w_chat[c_input], pastelen);
{ memcpy(&w_chat[c_input], paste, pastelen); // copy all of that.
memcpy(&w_chat[chatlen], paste, pastelen); // copy all of that. c_input += pastelen;
c_input += pastelen; return true;
/*size_t i = 0;
for (;i<pastelen;i++)
{
HU_queueChatChar(paste[i]); // queue it so that it's actually sent. (this chat write thing is REALLY messy.)
}*/
return true;
}
else // otherwise, we need to shift everything and make space, etc etc
{
size_t i = HU_MAXMSGLEN-1;
while (i >= c_input)
{
if (w_chat[i])
w_chat[i+pastelen] = w_chat[i];
if (i == 0) // prevent overflow
break;
i--;
}
memcpy(&w_chat[c_input], paste, pastelen); // copy all of that.
c_input += pastelen;
return true;
}
} }
else if (c == KEY_ENTER)
{
if (!CHAT_MUTE)
HU_sendChatMessage();
if (!CHAT_MUTE && HU_keyInChatString(w_chat,c))
{
HU_queueChatChar(c);
}
if (c == KEY_ENTER)
{
chat_on = false; chat_on = false;
c_input = 0; // reset input cursor c_input = 0; // reset input cursor
chat_scrollmedown = true; // you hit enter, so you might wanna autoscroll to see what you just sent. :) chat_scrollmedown = true; // you hit enter, so you might wanna autoscroll to see what you just sent. :)
@ -1417,6 +1301,32 @@ boolean HU_Responder(event_t *ev)
else else
c_input++; c_input++;
} }
else if ((c >= HU_FONTSTART && c <= HU_FONTEND && fontv[HU_FONT].font[c-HU_FONTSTART])
|| c == ' ') // Allow spaces, of course
{
if (CHAT_MUTE || strlen(w_chat) >= HU_MAXMSGLEN)
return true;
memmove(&w_chat[c_input + 1], &w_chat[c_input], strlen(w_chat) - c_input + 1);
w_chat[c_input] = c;
c_input++;
}
else if (c == KEY_BACKSPACE)
{
if (CHAT_MUTE || c_input <= 0)
return true;
memmove(&w_chat[c_input - 1], &w_chat[c_input], strlen(w_chat) - c_input + 1);
c_input--;
}
else if (c == KEY_DEL)
{
if (CHAT_MUTE || c_input >= strlen(w_chat))
return true;
memmove(&w_chat[c_input], &w_chat[c_input + 1], strlen(w_chat) - c_input);
}
return true; return true;
} }
@ -1962,8 +1872,8 @@ static void HU_DrawChat_Old(void)
size_t i = 0; size_t i = 0;
const char *ntalk = "Say: ", *ttalk = "Say-Team: "; const char *ntalk = "Say: ", *ttalk = "Say-Team: ";
const char *talk = ntalk; const char *talk = ntalk;
INT32 charwidth = 8 * con_scalefactor; //(hu_font['A'-HU_FONTSTART]->width) * con_scalefactor; INT32 charwidth = 8 * con_scalefactor; //(fontv[HU_FONT].font['A'-HU_FONTSTART]->width) * con_scalefactor;
INT32 charheight = 8 * con_scalefactor; //(hu_font['A'-HU_FONTSTART]->height) * con_scalefactor; INT32 charheight = 8 * con_scalefactor; //(fontv[HU_FONT].font['A'-HU_FONTSTART]->height) * con_scalefactor;
if (teamtalk) if (teamtalk)
{ {
talk = ttalk; talk = ttalk;
@ -1984,7 +1894,7 @@ static void HU_DrawChat_Old(void)
} }
else else
{ {
//charwidth = (hu_font[talk[i]-HU_FONTSTART]->width) * con_scalefactor; //charwidth = (fontv[HU_FONT].font[talk[i]-HU_FONTSTART]->width) * con_scalefactor;
V_DrawCharacter(HU_INPUTX + c, y, talk[i++] | cv_constextsize.value | V_NOSCALESTART, true); V_DrawCharacter(HU_INPUTX + c, y, talk[i++] | cv_constextsize.value | V_NOSCALESTART, true);
} }
c += charwidth; c += charwidth;
@ -2012,7 +1922,7 @@ static void HU_DrawChat_Old(void)
} }
else else
{ {
//charwidth = (hu_font[w_chat[i]-HU_FONTSTART]->width) * con_scalefactor; //charwidth = (fontv[HU_FONT].font[w_chat[i]-HU_FONTSTART]->width) * con_scalefactor;
V_DrawCharacter(HU_INPUTX + c, y, w_chat[i++] | cv_constextsize.value | V_NOSCALESTART | t, true); V_DrawCharacter(HU_INPUTX + c, y, w_chat[i++] | cv_constextsize.value | V_NOSCALESTART | t, true);
} }

View file

@ -91,8 +91,8 @@ typedef struct
//------------------------------------ //------------------------------------
// chat stuff // chat stuff
//------------------------------------ //------------------------------------
#define HU_MAXMSGLEN 224 #define HU_MAXMSGLEN 223
#define CHAT_BUFSIZE 64 // that's enough messages, right? We'll delete the older ones when that gets out of hand. #define CHAT_BUFSIZE 64 // that's enough messages, right? We'll delete the older ones when that gets out of hand.
#define NETSPLITSCREEN // why the hell WOULDN'T we want this? #define NETSPLITSCREEN // why the hell WOULDN'T we want this?
#ifdef NETSPLITSCREEN #ifdef NETSPLITSCREEN
#define OLDCHAT (cv_consolechat.value == 1 || dedicated || vid.width < 640) #define OLDCHAT (cv_consolechat.value == 1 || dedicated || vid.width < 640)

View file

@ -4320,6 +4320,13 @@ state_t states[NUMSTATES] =
{SPR_FLML, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE|14, 7, {NULL}, 6, 1, S_NULL}, // S_FLAMESHIELDLINE3 {SPR_FLML, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE|14, 7, {NULL}, 6, 1, S_NULL}, // S_FLAMESHIELDLINE3
{SPR_FLMF, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_FLAMESHIELDFLASH {SPR_FLMF, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_FLAMESHIELDFLASH
{SPR_GTOP, FF_ANIMATE, -1, {NULL}, 5, 1, S_NULL}, // S_GARDENTOP_FLOATING
{SPR_GTOP, 0, 1, {NULL}, 5, 1, S_GARDENTOP_SINKING2}, // S_GARDENTOP_SINKING1
{SPR_GTOP, 2, 1, {NULL}, 5, 1, S_GARDENTOP_SINKING3}, // S_GARDENTOP_SINKING2
{SPR_GTOP, 4, 1, {NULL}, 5, 1, S_GARDENTOP_SINKING1}, // S_GARDENTOP_SINKING3
{SPR_GTOP, FF_ANIMATE, 100, {A_Scream}, 5, 1, S_NULL}, // S_GARDENTOP_DEAD
{SPR_BDRF, FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 5, 2, S_NULL}, // S_GARDENTOPSPARK
{SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO {SPR_HYUU, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_HYUDORO
{SPR_GRWP, FF_FULLBRIGHT|FF_ANIMATE, 13, {NULL}, 7, 1, S_NULL}, // S_GROW_PARTICLE {SPR_GRWP, FF_FULLBRIGHT|FF_ANIMATE, 13, {NULL}, 7, 1, S_NULL}, // S_GROW_PARTICLE
@ -24065,6 +24072,60 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_GARDENTOP
-1, // doomednum
S_GARDENTOP_FLOATING, // spawnstate
8, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
4, // reactiontime
sfx_s3k8b, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_GARDENTOP_DEAD, // deathstate
S_NULL, // xdeathstate
sfx_s3k7a, // deathsound
40*FRACUNIT, // speed
30*FRACUNIT, // radius
68*FRACUNIT, // height
-1, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOCLIPTHING|MF_DONTENCOREMAP, // flags
S_NULL // raisestate
},
{ // MT_GARDENTOPSPARK
-1, // doomednum
S_GARDENTOPSPARK, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
8, // speed
8*FRACUNIT, // radius
8*FRACUNIT, // height
1, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP|MF_NOSQUISH, // flags
S_NULL // raisestate
},
{ // MT_HYUDORO { // MT_HYUDORO
-1, // doomednum -1, // doomednum
S_HYUDORO, // spawnstate S_HYUDORO, // spawnstate

View file

@ -4750,6 +4750,14 @@ typedef enum state
S_FLAMESHIELDLINE3, S_FLAMESHIELDLINE3,
S_FLAMESHIELDFLASH, S_FLAMESHIELDFLASH,
// Marble Garden Zone Spinning Top
S_GARDENTOP_FLOATING,
S_GARDENTOP_SINKING1,
S_GARDENTOP_SINKING2,
S_GARDENTOP_SINKING3,
S_GARDENTOP_DEAD,
S_GARDENTOPSPARK,
// Caked-Up Booty-Sheet Ghost // Caked-Up Booty-Sheet Ghost
S_HYUDORO, S_HYUDORO,
@ -6392,6 +6400,8 @@ typedef enum mobj_type
MT_FLAMESHIELDUNDERLAY, MT_FLAMESHIELDUNDERLAY,
MT_FLAMESHIELDPAPER, MT_FLAMESHIELDPAPER,
MT_BUBBLESHIELDTRAP, MT_BUBBLESHIELDTRAP,
MT_GARDENTOP,
MT_GARDENTOPSPARK,
MT_HYUDORO, MT_HYUDORO,
MT_HYUDORO_CENTER, MT_HYUDORO_CENTER,

View file

@ -66,17 +66,22 @@ boolean K_BananaBallhogCollide(mobj_t *t1, mobj_t *t2)
if (t1->type == MT_BANANA && t1->health > 1) if (t1->type == MT_BANANA && t1->health > 1)
S_StartSound(t2, sfx_bsnipe); S_StartSound(t2, sfx_bsnipe);
damageitem = true;
if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD) if (t2->player->flamedash && t2->player->itemtype == KITEM_FLAMESHIELD)
{ {
// Melt item // Melt item
S_StartSound(t2, sfx_s3k43); S_StartSound(t2, sfx_s3k43);
} }
else if (K_IsRidingFloatingTop(t2->player))
{
// Float over silly banana
damageitem = false;
}
else else
{ {
P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL|DMG_WOMBO); P_DamageMobj(t2, t1, t1->target, 1, DMG_NORMAL|DMG_WOMBO);
} }
damageitem = true;
} }
else if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD else if (t2->type == MT_BANANA || t2->type == MT_BANANA_SHIELD
|| t2->type == MT_ORBINAUT || t2->type == MT_ORBINAUT_SHIELD || t2->type == MT_ORBINAUT || t2->type == MT_ORBINAUT_SHIELD
@ -774,11 +779,13 @@ boolean K_PvPTouchDamage(mobj_t *t1, mobj_t *t2)
// Clash instead of damage if both parties have any of these conditions // Clash instead of damage if both parties have any of these conditions
t1Condition = (K_IsBigger(t1, t2) == true) t1Condition = (K_IsBigger(t1, t2) == true)
|| (t1->player->invincibilitytimer > 0) || (t1->player->invincibilitytimer > 0)
|| (t1->player->flamedash > 0 && t1->player->itemtype == KITEM_FLAMESHIELD); || (t1->player->flamedash > 0 && t1->player->itemtype == KITEM_FLAMESHIELD)
|| (t1->player->curshield == KSHIELD_TOP && !K_IsHoldingDownTop(t1->player));
t2Condition = (K_IsBigger(t2, t1) == true) t2Condition = (K_IsBigger(t2, t1) == true)
|| (t2->player->invincibilitytimer > 0) || (t2->player->invincibilitytimer > 0)
|| (t2->player->flamedash > 0 && t2->player->itemtype == KITEM_FLAMESHIELD); || (t2->player->flamedash > 0 && t2->player->itemtype == KITEM_FLAMESHIELD)
|| (t2->player->curshield == KSHIELD_TOP && !K_IsHoldingDownTop(t2->player));
if (t1Condition == true && t2Condition == true) if (t1Condition == true && t2Condition == true)
{ {

View file

@ -111,7 +111,7 @@ static patch_t *kp_itemtimer[2];
static patch_t *kp_itemmulsticker[2]; static patch_t *kp_itemmulsticker[2];
static patch_t *kp_itemx; static patch_t *kp_itemx;
static patch_t *kp_superring[2]; static patch_t *kp_sadface[2];
static patch_t *kp_sneaker[2]; static patch_t *kp_sneaker[2];
static patch_t *kp_rocketsneaker[2]; static patch_t *kp_rocketsneaker[2];
static patch_t *kp_invincibility[13]; static patch_t *kp_invincibility[13];
@ -121,7 +121,6 @@ static patch_t *kp_orbinaut[5];
static patch_t *kp_jawz[2]; static patch_t *kp_jawz[2];
static patch_t *kp_mine[2]; static patch_t *kp_mine[2];
static patch_t *kp_landmine[2]; static patch_t *kp_landmine[2];
static patch_t *kp_droptarget[2];
static patch_t *kp_ballhog[2]; static patch_t *kp_ballhog[2];
static patch_t *kp_selfpropelledbomb[2]; static patch_t *kp_selfpropelledbomb[2];
static patch_t *kp_grow[2]; static patch_t *kp_grow[2];
@ -131,8 +130,10 @@ static patch_t *kp_bubbleshield[2];
static patch_t *kp_flameshield[2]; static patch_t *kp_flameshield[2];
static patch_t *kp_hyudoro[2]; static patch_t *kp_hyudoro[2];
static patch_t *kp_pogospring[2]; static patch_t *kp_pogospring[2];
static patch_t *kp_superring[2];
static patch_t *kp_kitchensink[2]; static patch_t *kp_kitchensink[2];
static patch_t *kp_sadface[2]; static patch_t *kp_droptarget[2];
static patch_t *kp_gardentop[2];
static patch_t *kp_check[6]; static patch_t *kp_check[6];
@ -390,7 +391,7 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_itemmulsticker[0], "K_ITMUL"); HU_UpdatePatch(&kp_itemmulsticker[0], "K_ITMUL");
HU_UpdatePatch(&kp_itemx, "K_ITX"); HU_UpdatePatch(&kp_itemx, "K_ITX");
HU_UpdatePatch(&kp_superring[0], "K_ITRING"); HU_UpdatePatch(&kp_sadface[0], "K_ITSAD");
HU_UpdatePatch(&kp_sneaker[0], "K_ITSHOE"); HU_UpdatePatch(&kp_sneaker[0], "K_ITSHOE");
HU_UpdatePatch(&kp_rocketsneaker[0], "K_ITRSHE"); HU_UpdatePatch(&kp_rocketsneaker[0], "K_ITRSHE");
@ -411,7 +412,6 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_jawz[0], "K_ITJAWZ"); HU_UpdatePatch(&kp_jawz[0], "K_ITJAWZ");
HU_UpdatePatch(&kp_mine[0], "K_ITMINE"); HU_UpdatePatch(&kp_mine[0], "K_ITMINE");
HU_UpdatePatch(&kp_landmine[0], "K_ITLNDM"); HU_UpdatePatch(&kp_landmine[0], "K_ITLNDM");
HU_UpdatePatch(&kp_droptarget[0], "K_ITDTRG");
HU_UpdatePatch(&kp_ballhog[0], "K_ITBHOG"); HU_UpdatePatch(&kp_ballhog[0], "K_ITBHOG");
HU_UpdatePatch(&kp_selfpropelledbomb[0], "K_ITSPB"); HU_UpdatePatch(&kp_selfpropelledbomb[0], "K_ITSPB");
HU_UpdatePatch(&kp_grow[0], "K_ITGROW"); HU_UpdatePatch(&kp_grow[0], "K_ITGROW");
@ -421,8 +421,10 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_flameshield[0], "K_ITFLMS"); HU_UpdatePatch(&kp_flameshield[0], "K_ITFLMS");
HU_UpdatePatch(&kp_hyudoro[0], "K_ITHYUD"); HU_UpdatePatch(&kp_hyudoro[0], "K_ITHYUD");
HU_UpdatePatch(&kp_pogospring[0], "K_ITPOGO"); HU_UpdatePatch(&kp_pogospring[0], "K_ITPOGO");
HU_UpdatePatch(&kp_superring[0], "K_ITRING");
HU_UpdatePatch(&kp_kitchensink[0], "K_ITSINK"); HU_UpdatePatch(&kp_kitchensink[0], "K_ITSINK");
HU_UpdatePatch(&kp_sadface[0], "K_ITSAD"); HU_UpdatePatch(&kp_droptarget[0], "K_ITDTRG");
HU_UpdatePatch(&kp_gardentop[0], "K_ITGTOP");
sprintf(buffer, "FSMFGxxx"); sprintf(buffer, "FSMFGxxx");
for (i = 0; i < 104; i++) for (i = 0; i < 104; i++)
@ -447,7 +449,7 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_itemtimer[1], "K_ISIMER"); HU_UpdatePatch(&kp_itemtimer[1], "K_ISIMER");
HU_UpdatePatch(&kp_itemmulsticker[1], "K_ISMUL"); HU_UpdatePatch(&kp_itemmulsticker[1], "K_ISMUL");
HU_UpdatePatch(&kp_superring[1], "K_ISRING"); HU_UpdatePatch(&kp_sadface[1], "K_ISSAD");
HU_UpdatePatch(&kp_sneaker[1], "K_ISSHOE"); HU_UpdatePatch(&kp_sneaker[1], "K_ISSHOE");
HU_UpdatePatch(&kp_rocketsneaker[1], "K_ISRSHE"); HU_UpdatePatch(&kp_rocketsneaker[1], "K_ISRSHE");
sprintf(buffer, "K_ISINVx"); sprintf(buffer, "K_ISINVx");
@ -462,7 +464,6 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_jawz[1], "K_ISJAWZ"); HU_UpdatePatch(&kp_jawz[1], "K_ISJAWZ");
HU_UpdatePatch(&kp_mine[1], "K_ISMINE"); HU_UpdatePatch(&kp_mine[1], "K_ISMINE");
HU_UpdatePatch(&kp_landmine[1], "K_ISLNDM"); HU_UpdatePatch(&kp_landmine[1], "K_ISLNDM");
HU_UpdatePatch(&kp_droptarget[1], "K_ISDTRG");
HU_UpdatePatch(&kp_ballhog[1], "K_ISBHOG"); HU_UpdatePatch(&kp_ballhog[1], "K_ISBHOG");
HU_UpdatePatch(&kp_selfpropelledbomb[1], "K_ISSPB"); HU_UpdatePatch(&kp_selfpropelledbomb[1], "K_ISSPB");
HU_UpdatePatch(&kp_grow[1], "K_ISGROW"); HU_UpdatePatch(&kp_grow[1], "K_ISGROW");
@ -472,8 +473,10 @@ void K_LoadKartHUDGraphics(void)
HU_UpdatePatch(&kp_flameshield[1], "K_ISFLMS"); HU_UpdatePatch(&kp_flameshield[1], "K_ISFLMS");
HU_UpdatePatch(&kp_hyudoro[1], "K_ISHYUD"); HU_UpdatePatch(&kp_hyudoro[1], "K_ISHYUD");
HU_UpdatePatch(&kp_pogospring[1], "K_ISPOGO"); HU_UpdatePatch(&kp_pogospring[1], "K_ISPOGO");
HU_UpdatePatch(&kp_superring[1], "K_ISRING");
HU_UpdatePatch(&kp_kitchensink[1], "K_ISSINK"); HU_UpdatePatch(&kp_kitchensink[1], "K_ISSINK");
HU_UpdatePatch(&kp_sadface[1], "K_ISSAD"); HU_UpdatePatch(&kp_droptarget[1], "K_ISDTRG");
HU_UpdatePatch(&kp_gardentop[1], "K_ISGTOP");
sprintf(buffer, "FSMFSxxx"); sprintf(buffer, "FSMFSxxx");
for (i = 0; i < 104; i++) for (i = 0; i < 104; i++)
@ -662,8 +665,6 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny)
return (tiny ? "K_ISMINE" : "K_ITMINE"); return (tiny ? "K_ISMINE" : "K_ITMINE");
case KITEM_LANDMINE: case KITEM_LANDMINE:
return (tiny ? "K_ISLNDM" : "K_ITLNDM"); return (tiny ? "K_ISLNDM" : "K_ITLNDM");
case KITEM_DROPTARGET:
return (tiny ? "K_ISDTRG" : "K_ITDTRG");
case KITEM_BALLHOG: case KITEM_BALLHOG:
return (tiny ? "K_ISBHOG" : "K_ITBHOG"); return (tiny ? "K_ISBHOG" : "K_ITBHOG");
case KITEM_SPB: case KITEM_SPB:
@ -686,6 +687,10 @@ const char *K_GetItemPatch(UINT8 item, boolean tiny)
return (tiny ? "K_ISRING" : "K_ITRING"); return (tiny ? "K_ISRING" : "K_ITRING");
case KITEM_KITCHENSINK: case KITEM_KITCHENSINK:
return (tiny ? "K_ISSINK" : "K_ITSINK"); return (tiny ? "K_ISSINK" : "K_ITSINK");
case KITEM_DROPTARGET:
return (tiny ? "K_ISDTRG" : "K_ITDTRG");
case KITEM_GARDENTOP:
return (tiny ? "K_ISGTOP" : "K_ITGTOP");
case KRITEM_TRIPLEORBINAUT: case KRITEM_TRIPLEORBINAUT:
return (tiny ? "K_ISORBN" : "K_ITORB3"); return (tiny ? "K_ISORBN" : "K_ITORB3");
case KRITEM_QUADORBINAUT: case KRITEM_QUADORBINAUT:
@ -721,6 +726,7 @@ static patch_t *K_GetCachedItemPatch(INT32 item, UINT8 offset)
kp_superring, kp_superring,
kp_kitchensink, kp_kitchensink,
kp_droptarget, kp_droptarget,
kp_gardentop,
}; };
if (item == KITEM_SAD || (item > KITEM_NONE && item < NUMKARTITEMS)) if (item == KITEM_SAD || (item > KITEM_NONE && item < NUMKARTITEMS))
@ -4456,6 +4462,7 @@ static void K_drawDistributionDebugger(void)
kp_superring[1], kp_superring[1],
kp_kitchensink[1], kp_kitchensink[1],
kp_droptarget[1], kp_droptarget[1],
kp_gardentop[1],
kp_sneaker[1], kp_sneaker[1],
kp_sneaker[1], kp_sneaker[1],

File diff suppressed because it is too large Load diff

View file

@ -69,6 +69,7 @@ void K_SpawnDashDustRelease(player_t *player);
void K_SpawnDriftBoostClip(player_t *player); void K_SpawnDriftBoostClip(player_t *player);
void K_SpawnDriftBoostClipSpark(mobj_t *clip); void K_SpawnDriftBoostClipSpark(mobj_t *clip);
void K_SpawnNormalSpeedLines(player_t *player); void K_SpawnNormalSpeedLines(player_t *player);
void K_SpawnGardenTopSpeedLines(player_t *player);
void K_SpawnInvincibilitySpeedLines(mobj_t *mo); void K_SpawnInvincibilitySpeedLines(mobj_t *mo);
void K_SpawnBumpEffect(mobj_t *mo); void K_SpawnBumpEffect(mobj_t *mo);
void K_KartMoveAnimation(player_t *player); void K_KartMoveAnimation(player_t *player);
@ -142,7 +143,13 @@ boolean K_ApplyOffroad(player_t *player);
boolean K_SlopeResistance(player_t *player); boolean K_SlopeResistance(player_t *player);
tripwirepass_t K_TripwirePassConditions(player_t *player); tripwirepass_t K_TripwirePassConditions(player_t *player);
boolean K_TripwirePass(player_t *player); boolean K_TripwirePass(player_t *player);
boolean K_WaterRun(player_t *player); boolean K_MovingHorizontally(mobj_t *mobj);
boolean K_WaterRun(mobj_t *mobj);
boolean K_WaterSkip(mobj_t *mobj);
void K_SpawnWaterRunParticles(mobj_t *mobj);
boolean K_IsRidingFloatingTop(player_t *player);
boolean K_IsHoldingDownTop(player_t *player);
mobj_t *K_GetGardenTop(player_t *player);
void K_ApplyTripWire(player_t *player, tripwirestate_t state); void K_ApplyTripWire(player_t *player, tripwirestate_t state);
INT16 K_GetSpindashChargeTime(player_t *player); INT16 K_GetSpindashChargeTime(player_t *player);
fixed_t K_GetSpindashChargeSpeed(player_t *player); fixed_t K_GetSpindashChargeSpeed(player_t *player);
@ -169,6 +176,7 @@ UINT8 K_GetOrbinautItemFrame(UINT8 count);
boolean K_IsSPBInGame(void); boolean K_IsSPBInGame(void);
void K_KartEbrakeVisuals(player_t *p); void K_KartEbrakeVisuals(player_t *p);
void K_HandleDirectionalInfluence(player_t *player); void K_HandleDirectionalInfluence(player_t *player);
fixed_t K_DefaultPlayerRadius(player_t *player);
// sound stuff for lua // sound stuff for lua
void K_PlayAttackTaunt(mobj_t *source); void K_PlayAttackTaunt(mobj_t *source);

View file

@ -2016,7 +2016,7 @@ void M_QuitResponse(INT32 ch)
if (ch == MA_YES) if (ch == MA_YES)
{ {
if (!(netgame || cv_debug)) if (!(netgame || cht_debug))
{ {
mrand = M_RandomKey(sizeof(quitsounds) / sizeof(INT32)); mrand = M_RandomKey(sizeof(quitsounds) / sizeof(INT32));
if (quitsounds[mrand]) if (quitsounds[mrand])
@ -3577,7 +3577,7 @@ void M_LevelSelectHandler(INT32 choice)
strncpy(connectedservername, cv_servername.string, MAXSERVERNAME); strncpy(connectedservername, cv_servername.string, MAXSERVERNAME);
// Still need to reset devmode // Still need to reset devmode
cv_debug = 0; cht_debug = 0;
if (demo.playback) if (demo.playback)
G_StopDemo(); G_StopDemo();
@ -3686,7 +3686,7 @@ void M_StartTimeAttack(INT32 choice)
} }
// Still need to reset devmode // Still need to reset devmode
cv_debug = 0; cht_debug = 0;
emeralds = 0; emeralds = 0;
if (demo.playback) if (demo.playback)

View file

@ -9,6 +9,14 @@ void Obj_HyudoroThink(mobj_t *actor);
void Obj_HyudoroCenterThink(mobj_t *actor); void Obj_HyudoroCenterThink(mobj_t *actor);
void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher); void Obj_HyudoroCollide(mobj_t *special, mobj_t *toucher);
/* Garden Top */
void Obj_GardenTopDeploy(mobj_t *rider);
mobj_t *Obj_GardenTopThrow(player_t *player);
mobj_t *Obj_GardenTopDestroy(player_t *player);
void Obj_GardenTopThink(mobj_t *top);
void Obj_GardenTopSparkThink(mobj_t *spark);
boolean Obj_GardenTopPlayerIsGrinding(player_t *player);
/* Shrink */ /* Shrink */
void Obj_PohbeeThinker(mobj_t *pohbee); void Obj_PohbeeThinker(mobj_t *pohbee);
void Obj_PohbeeRemoved(mobj_t *pohbee); void Obj_PohbeeRemoved(mobj_t *pohbee);

View file

@ -157,7 +157,13 @@ void K_DoIngameRespawn(player_t *player)
P_ResetPlayer(player); P_ResetPlayer(player);
// Set up respawn position if invalid // Set up respawn position if invalid
if (player->respawn.wp != NULL && leveltime >= starttime) if (player->respawn.manual == true)
{
player->respawn.distanceleft = 0;
player->respawn.pointz += K_RespawnOffset(player, player->respawn.flip);
player->respawn.manual = false; // one respawn only!
}
else if (player->respawn.wp != NULL && leveltime >= starttime)
{ {
const UINT32 dist = RESPAWN_DIST + (player->airtime * 48); const UINT32 dist = RESPAWN_DIST + (player->airtime * 48);
player->respawn.distanceleft = (dist * mapobjectscale) / FRACUNIT; player->respawn.distanceleft = (dist * mapobjectscale) / FRACUNIT;

View file

@ -1082,7 +1082,7 @@ void K_UpdateTerrainOverlay(mobj_t *mo)
fixed_t speedDiv = FRACUNIT + FixedMul(FixedDiv(speed, maxSpeed), o->speed); fixed_t speedDiv = FRACUNIT + FixedMul(FixedDiv(speed, maxSpeed), o->speed);
tic_t animSpeed = max(FixedDiv(mo->state->tics, speedDiv), 1); tic_t animSpeed = max(FixedDiv(mo->state->tics, speedDiv), 1);
mo->tics = min(mo->tics, animSpeed); mo->tics = min((tic_t)mo->tics, animSpeed);
} }
} }

View file

@ -954,12 +954,15 @@ static int lib_pCheckDeathPitCollide(lua_State *L)
static int lib_pCheckSolidLava(lua_State *L) static int lib_pCheckSolidLava(lua_State *L)
{ {
mobj_t *mo = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
ffloor_t *rover = *((ffloor_t **)luaL_checkudata(L, 2, META_FFLOOR)); ffloor_t *rover = *((ffloor_t **)luaL_checkudata(L, 2, META_FFLOOR));
//HUDSAFE //HUDSAFE
INLEVEL INLEVEL
if (!mo)
return LUA_ErrInvalid(L, "mobj_t");
if (!rover) if (!rover)
return LUA_ErrInvalid(L, "ffloor_t"); return LUA_ErrInvalid(L, "ffloor_t");
lua_pushboolean(L, P_CheckSolidLava(rover)); lua_pushboolean(L, P_CheckSolidLava(mo, rover));
return 1; return 1;
} }

View file

@ -51,7 +51,7 @@ static UINT8 lib_searchBlockmap_Objects(lua_State *L, INT32 x, INT32 y, mobj_t *
LUA_PushUserdata(L, thing, META_MOBJ); LUA_PushUserdata(L, thing, META_MOBJ);
LUA_PushUserdata(L, mobj, META_MOBJ); LUA_PushUserdata(L, mobj, META_MOBJ);
if (lua_pcall(gL, 2, 1, 0)) { if (lua_pcall(gL, 2, 1, 0)) {
if (!blockfuncerror || cv_debug & DBG_LUA) if (!blockfuncerror || cht_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1); lua_pop(gL, 1);
blockfuncerror = true; blockfuncerror = true;
@ -112,7 +112,7 @@ static UINT8 lib_searchBlockmap_Lines(lua_State *L, INT32 x, INT32 y, mobj_t *th
LUA_PushUserdata(L, thing, META_MOBJ); LUA_PushUserdata(L, thing, META_MOBJ);
LUA_PushUserdata(L, po->lines[i], META_LINE); LUA_PushUserdata(L, po->lines[i], META_LINE);
if (lua_pcall(gL, 2, 1, 0)) { if (lua_pcall(gL, 2, 1, 0)) {
if (!blockfuncerror || cv_debug & DBG_LUA) if (!blockfuncerror || cht_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1); lua_pop(gL, 1);
blockfuncerror = true; blockfuncerror = true;
@ -149,7 +149,7 @@ static UINT8 lib_searchBlockmap_Lines(lua_State *L, INT32 x, INT32 y, mobj_t *th
LUA_PushUserdata(L, thing, META_MOBJ); LUA_PushUserdata(L, thing, META_MOBJ);
LUA_PushUserdata(L, ld, META_LINE); LUA_PushUserdata(L, ld, META_LINE);
if (lua_pcall(gL, 2, 1, 0)) { if (lua_pcall(gL, 2, 1, 0)) {
if (!blockfuncerror || cv_debug & DBG_LUA) if (!blockfuncerror || cht_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1); lua_pop(gL, 1);
blockfuncerror = true; blockfuncerror = true;
@ -195,7 +195,7 @@ static UINT8 lib_searchBlockmap_PolyObjs(lua_State *L, INT32 x, INT32 y, mobj_t
LUA_PushUserdata(L, thing, META_MOBJ); LUA_PushUserdata(L, thing, META_MOBJ);
LUA_PushUserdata(L, po, META_POLYOBJ); LUA_PushUserdata(L, po, META_POLYOBJ);
if (lua_pcall(gL, 2, 1, 0)) { if (lua_pcall(gL, 2, 1, 0)) {
if (!blockfuncerror || cv_debug & DBG_LUA) if (!blockfuncerror || cht_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1); lua_pop(gL, 1);
blockfuncerror = true; blockfuncerror = true;

View file

@ -411,7 +411,7 @@ static int call_single_hook_no_copy(Hook_State *hook)
else else
{ {
/* print the error message once */ /* print the error message once */
if (cv_debug & DBG_LUA || !in_bit_array(hooksErrored, hook->id)) if (cht_debug & DBG_LUA || !in_bit_array(hooksErrored, hook->id))
{ {
CONS_Alert(CONS_WARNING, "%s\n", lua_tostring(gL, -1)); CONS_Alert(CONS_WARNING, "%s\n", lua_tostring(gL, -1));
set_bit_array(hooksErrored, hook->id); set_bit_array(hooksErrored, hook->id);

View file

@ -100,6 +100,7 @@ enum mobj_e {
mobj_spryoff, mobj_spryoff,
mobj_sprzoff, mobj_sprzoff,
mobj_hitlag, mobj_hitlag,
mobj_waterskip,
mobj_dispoffset mobj_dispoffset
}; };
@ -181,6 +182,7 @@ static const char *const mobj_opt[] = {
"spryoff", "spryoff",
"sprzoff", "sprzoff",
"hitlag", "hitlag",
"waterskip",
"dispoffset", "dispoffset",
NULL}; NULL};
@ -460,6 +462,9 @@ static int mobj_get(lua_State *L)
case mobj_hitlag: case mobj_hitlag:
lua_pushinteger(L, mo->hitlag); lua_pushinteger(L, mo->hitlag);
break; break;
case mobj_waterskip:
lua_pushinteger(L, mo->waterskip);
break;
case mobj_dispoffset: case mobj_dispoffset:
lua_pushinteger(L, mo->dispoffset); lua_pushinteger(L, mo->dispoffset);
break; break;
@ -835,6 +840,9 @@ static int mobj_set(lua_State *L)
case mobj_hitlag: case mobj_hitlag:
mo->hitlag = luaL_checkinteger(L, 3); mo->hitlag = luaL_checkinteger(L, 3);
break; break;
case mobj_waterskip:
mo->waterskip = (UINT8)luaL_checkinteger(L, 3);
break;
case mobj_dispoffset: case mobj_dispoffset:
mo->dispoffset = luaL_checkinteger(L, 3); mo->dispoffset = luaL_checkinteger(L, 3);
break; break;

View file

@ -264,8 +264,6 @@ static int player_get(lua_State *L)
lua_pushinteger(L, plr->aizdriftturn); lua_pushinteger(L, plr->aizdriftturn);
else if (fastcmp(field,"offroad")) else if (fastcmp(field,"offroad"))
lua_pushinteger(L, plr->offroad); lua_pushinteger(L, plr->offroad);
else if (fastcmp(field,"waterskip"))
lua_pushinteger(L, plr->waterskip);
else if (fastcmp(field,"tiregrease")) else if (fastcmp(field,"tiregrease"))
lua_pushinteger(L, plr->tiregrease); lua_pushinteger(L, plr->tiregrease);
else if (fastcmp(field,"springstars")) else if (fastcmp(field,"springstars"))
@ -634,8 +632,6 @@ static int player_set(lua_State *L)
plr->aizdriftturn = luaL_checkinteger(L, 3); plr->aizdriftturn = luaL_checkinteger(L, 3);
else if (fastcmp(field,"offroad")) else if (fastcmp(field,"offroad"))
plr->offroad = luaL_checkinteger(L, 3); plr->offroad = luaL_checkinteger(L, 3);
else if (fastcmp(field,"waterskip"))
plr->waterskip = luaL_checkinteger(L, 3);
else if (fastcmp(field,"tiregrease")) else if (fastcmp(field,"tiregrease"))
plr->tiregrease = luaL_checkinteger(L, 3); plr->tiregrease = luaL_checkinteger(L, 3);
else if (fastcmp(field,"springstars")) else if (fastcmp(field,"springstars"))

View file

@ -38,6 +38,8 @@
#include "lua_script.h" #include "lua_script.h"
#include "lua_hook.h" #include "lua_hook.h"
#include "fastcmp.h"
// //
// CHEAT SEQUENCE PACKAGE // CHEAT SEQUENCE PACKAGE
// //
@ -111,7 +113,7 @@ static UINT8 cheatf_devmode(void)
for (i = 0; i < MAXUNLOCKABLES; i++) for (i = 0; i < MAXUNLOCKABLES; i++)
unlockables[i].unlocked = true; unlockables[i].unlocked = true;
devparm = true; devparm = true;
cv_debug |= 0x8000; cht_debug |= 0x8000;
// Refresh secrets menu existing. // Refresh secrets menu existing.
M_ClearMenus(true); M_ClearMenus(true);
@ -251,37 +253,18 @@ boolean cht_Responder(event_t *ev)
// command that can be typed at the console! // command that can be typed at the console!
void Command_CheatNoClip_f(void) void Command_CheatNoClip_f(void)
{ {
player_t *plyr;
REQUIRE_CHEATS; REQUIRE_CHEATS;
REQUIRE_INLEVEL; REQUIRE_INLEVEL;
REQUIRE_SINGLEPLAYER; // TODO: make netplay compatible
plyr = &players[consoleplayer]; D_Cheat(consoleplayer, CHEAT_NOCLIP);
if (!plyr->mo || P_MobjWasRemoved(plyr->mo))
return;
plyr->cheats ^= PC_NOCLIP;
CONS_Printf(M_GetText("No Clipping %s\n"), plyr->cheats & PC_NOCLIP ? M_GetText("On") : M_GetText("Off"));
if (plyr->cheats & PC_NOCLIP)
plyr->mo->flags |= MF_NOCLIP;
else
plyr->mo->flags &= ~MF_NOCLIP;
} }
void Command_CheatGod_f(void) void Command_CheatGod_f(void)
{ {
player_t *plyr;
REQUIRE_CHEATS; REQUIRE_CHEATS;
REQUIRE_INLEVEL; REQUIRE_INLEVEL;
REQUIRE_SINGLEPLAYER; // TODO: make multiplayer compatible
plyr = &players[consoleplayer]; D_Cheat(consoleplayer, CHEAT_GOD);
plyr->cheats ^= PC_GODMODE;
CONS_Printf(M_GetText("Cheese Mode %s\n"), plyr->cheats & PC_GODMODE ? M_GetText("On") : M_GetText("Off"));
} }
void Command_Scale_f(void) void Command_Scale_f(void)
@ -291,7 +274,6 @@ void Command_Scale_f(void)
REQUIRE_CHEATS; REQUIRE_CHEATS;
REQUIRE_INLEVEL; REQUIRE_INLEVEL;
REQUIRE_SINGLEPLAYER; // TODO: make multiplayer compatible
if (scale < FRACUNIT/100 || scale > 100*FRACUNIT) //COM_Argv(1) will return a null string if they did not give a paramater, so... if (scale < FRACUNIT/100 || scale > 100*FRACUNIT) //COM_Argv(1) will return a null string if they did not give a paramater, so...
{ {
@ -299,29 +281,21 @@ void Command_Scale_f(void)
return; return;
} }
if (!players[consoleplayer].mo) D_Cheat(consoleplayer, CHEAT_SCALE, scale);
return;
players[consoleplayer].mo->destscale = scale;
CONS_Printf(M_GetText("Scale set to %s\n"), COM_Argv(1));
} }
void Command_Gravflip_f(void) void Command_Gravflip_f(void)
{ {
REQUIRE_CHEATS; REQUIRE_CHEATS;
REQUIRE_INLEVEL; REQUIRE_INLEVEL;
REQUIRE_SINGLEPLAYER; // TODO: make multiplayer compatible
if (players[consoleplayer].mo) D_Cheat(consoleplayer, CHEAT_FLIP);
players[consoleplayer].mo->flags2 ^= MF2_OBJECTFLIP;
} }
void Command_Hurtme_f(void) void Command_Hurtme_f(void)
{ {
REQUIRE_CHEATS; REQUIRE_CHEATS;
REQUIRE_INLEVEL; REQUIRE_INLEVEL;
REQUIRE_SINGLEPLAYER; // TODO: make multiplayer compatible
if (COM_Argc() < 2) if (COM_Argc() < 2)
{ {
@ -329,69 +303,26 @@ void Command_Hurtme_f(void)
return; return;
} }
P_DamageMobj(players[consoleplayer].mo, NULL, NULL, atoi(COM_Argv(1)), DMG_NORMAL); D_Cheat(consoleplayer, CHEAT_HURT, atoi(COM_Argv(1)));
} }
void Command_RTeleport_f(void) void Command_RTeleport_f(void)
{ {
fixed_t intx, inty, intz; float x = atof(COM_Argv(1));
size_t i; float y = atof(COM_Argv(2));
player_t *p = &players[consoleplayer]; float z = atof(COM_Argv(3));
subsector_t *ss;
REQUIRE_CHEATS; REQUIRE_CHEATS;
REQUIRE_INLEVEL; REQUIRE_INLEVEL;
REQUIRE_SINGLEPLAYER; // TODO: make multiplayer compatible
if (COM_Argc() < 3 || COM_Argc() > 7) if (COM_Argc() != 4)
{ {
CONS_Printf(M_GetText("rteleport -x <value> -y <value> -z <value>: relative teleport to a location\n")); CONS_Printf(M_GetText("rteleport <x> <y> <z>: relative teleport to a location\n"));
return; return;
} }
if (!p->mo) D_Cheat(consoleplayer, CHEAT_RELATIVE_TELEPORT,
return; FLOAT_TO_FIXED(x), FLOAT_TO_FIXED(y), FLOAT_TO_FIXED(z));
i = COM_CheckParm("-x");
if (i)
intx = atoi(COM_Argv(i + 1));
else
intx = 0;
i = COM_CheckParm("-y");
if (i)
inty = atoi(COM_Argv(i + 1));
else
inty = 0;
ss = R_PointInSubsectorOrNull(p->mo->x + intx*FRACUNIT, p->mo->y + inty*FRACUNIT);
if (!ss || ss->sector->ceilingheight - ss->sector->floorheight < p->mo->height)
{
CONS_Alert(CONS_NOTICE, M_GetText("Not a valid location.\n"));
return;
}
i = COM_CheckParm("-z");
if (i)
{
intz = atoi(COM_Argv(i + 1));
intz <<= FRACBITS;
intz += p->mo->z;
if (intz < ss->sector->floorheight)
intz = ss->sector->floorheight;
if (intz > ss->sector->ceilingheight - p->mo->height)
intz = ss->sector->ceilingheight - p->mo->height;
}
else
intz = p->mo->z;
CONS_Printf(M_GetText("Teleporting by %d, %d, %d...\n"), intx, inty, FixedInt((intz-p->mo->z)));
P_MapStart();
if (!P_SetOrigin(p->mo, p->mo->x+intx*FRACUNIT, p->mo->y+inty*FRACUNIT, intz))
CONS_Alert(CONS_WARNING, M_GetText("Unable to teleport to that spot!\n"));
else
S_StartSound(p->mo, sfx_mixup);
P_MapEnd();
} }
void Command_Teleport_f(void) void Command_Teleport_f(void)
@ -619,7 +550,6 @@ void Command_Skynum_f(void)
{ {
REQUIRE_CHEATS; REQUIRE_CHEATS;
REQUIRE_INLEVEL; REQUIRE_INLEVEL;
REQUIRE_SINGLEPLAYER; // TODO: make multiplayer compatible
if (COM_Argc() != 2) if (COM_Argc() != 2)
{ {
@ -651,18 +581,6 @@ void Command_Weather_f(void)
P_SwitchWeather(atoi(COM_Argv(1))); P_SwitchWeather(atoi(COM_Argv(1)));
} }
void Command_Toggletwod_f(void)
{
player_t *p = &players[consoleplayer];
REQUIRE_CHEATS;
REQUIRE_INLEVEL;
REQUIRE_SINGLEPLAYER; // TODO: make multiplayer compatible
if (p->mo)
p->mo->flags2 ^= MF2_TWOD;
}
#ifdef _DEBUG #ifdef _DEBUG
// You never thought you needed this, did you? >=D // You never thought you needed this, did you? >=D
// Yes, this has the specific purpose of completely screwing you up // Yes, this has the specific purpose of completely screwing you up
@ -715,15 +633,15 @@ void Command_Dumplua_f(void)
void Command_Savecheckpoint_f(void) void Command_Savecheckpoint_f(void)
{ {
mobj_t *thing = players[consoleplayer].mo;
REQUIRE_CHEATS; REQUIRE_CHEATS;
REQUIRE_INLEVEL; REQUIRE_INLEVEL;
REQUIRE_SINGLEPLAYER; // TODO: make multiplayer compatible
players[consoleplayer].respawn.pointx = players[consoleplayer].mo->x; if (!P_MobjWasRemoved(thing))
players[consoleplayer].respawn.pointy = players[consoleplayer].mo->y; {
players[consoleplayer].respawn.pointz = players[consoleplayer].mo->floorz; D_Cheat(consoleplayer, CHEAT_SAVECHECKPOINT, thing->x, thing->y, thing->z);
}
CONS_Printf(M_GetText("Temporary checkpoint created at %d, %d, %d\n"), players[consoleplayer].respawn.pointx, players[consoleplayer].respawn.pointy, players[consoleplayer].respawn.pointz);
} }
// Like M_GetAllEmeralds() but for console devmode junkies. // Like M_GetAllEmeralds() but for console devmode junkies.
@ -749,24 +667,81 @@ void Command_Resetemeralds_f(void)
} }
*/ */
//
// Devmode
//
UINT32 cht_debug;
struct debugFlagNames_s const debug_flag_names[] =
{
{"None", DBG_NONE},
{"Basic", DBG_BASIC},
{"Detailed", DBG_DETAILED},
{"Player", DBG_PLAYER},
{"Render", DBG_RENDER},
{"Renderer", DBG_RENDER}, // alt name
{"Polyobj", DBG_POLYOBJ},
{"GameLogic", DBG_GAMELOGIC},
{"Game", DBG_GAMELOGIC}, // alt name
{"Netplay", DBG_NETPLAY},
{"Memory", DBG_MEMORY},
{"Setup", DBG_SETUP},
{"Lua", DBG_LUA},
{"RNG", DBG_RNG},
{"Randomizer", DBG_RNG}, // alt name
{NULL, 0}
};
void Command_Devmode_f(void) void Command_Devmode_f(void)
{ {
size_t argc = 0;
REQUIRE_CHEATS; REQUIRE_CHEATS;
REQUIRE_SINGLEPLAYER; // TODO: make multiplayer compatible
if (COM_Argc() > 1) argc = COM_Argc();
if (argc > 1)
{ {
const char *arg = COM_Argv(1); UINT32 flags = 0;
size_t i;
if (arg[0] && arg[0] == '0' && for (i = 1; i < argc; i++)
arg[1] && arg[1] == 'x') // Use hexadecimal! {
cv_debug = axtoi(arg+2); const char *arg = COM_Argv(i);
else size_t j;
cv_debug = atoi(arg);
// Try it as a string
for (j = 0; debug_flag_names[j].str; j++)
{
if (stricmp(arg, debug_flag_names[j].str) == 0)
{
break;
}
}
if (debug_flag_names[j].str)
{
flags |= debug_flag_names[j].flag;
continue;
}
// Try it as a number
if (arg[0] && arg[0] == '0' &&
arg[1] && arg[1] == 'x') // Use hexadecimal!
{
flags |= axtoi(arg+2);
}
else
{
flags |= atoi(arg);
}
}
D_Cheat(consoleplayer, CHEAT_DEVMODE, flags);
} }
else else
{ {
CONS_Printf(M_GetText("devmode <flags>: enable debugging tools and info, prepend with 0x to use hexadecimal\n")); CONS_Printf(M_GetText("devmode <flags>: Enable debugging info. Prepend with 0x to use hexadecimal\n"));
return; return;
} }
} }
@ -776,13 +751,7 @@ void Command_Setrings_f(void)
REQUIRE_CHEATS; REQUIRE_CHEATS;
REQUIRE_INLEVEL; REQUIRE_INLEVEL;
if (COM_Argc() > 1) D_Cheat(consoleplayer, CHEAT_RINGS, atoi(COM_Argv(1)));
{
// P_GivePlayerRings does value clamping
players[consoleplayer].rings = 0;
P_GivePlayerRings(&players[consoleplayer], atoi(COM_Argv(1)));
players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings
}
} }
void Command_Setlives_f(void) void Command_Setlives_f(void)
@ -790,20 +759,7 @@ void Command_Setlives_f(void)
REQUIRE_CHEATS; REQUIRE_CHEATS;
REQUIRE_INLEVEL; REQUIRE_INLEVEL;
if (COM_Argc() > 1) D_Cheat(consoleplayer, CHEAT_LIVES, atoi(COM_Argv(1)));
{
SINT8 lives = atoi(COM_Argv(1));
if (lives == -1)
{
players[consoleplayer].lives = INFLIVES; // infinity!
}
else
{
// P_GivePlayerLives does value clamping
players[consoleplayer].lives = 0;
P_GivePlayerLives(&players[consoleplayer], atoi(COM_Argv(1)));
}
}
} }
// //

View file

@ -19,6 +19,24 @@
#include "p_mobj.h" #include "p_mobj.h"
#include "command.h" #include "command.h"
typedef enum {
CHEAT_NOCLIP,
CHEAT_GOD,
CHEAT_SAVECHECKPOINT,
CHEAT_RINGS,
CHEAT_LIVES,
CHEAT_SCALE,
CHEAT_FLIP,
CHEAT_HURT,
CHEAT_RELATIVE_TELEPORT,
CHEAT_DEVMODE,
NUMBER_OF_CHEATS
} cheat_t;
//
// Cheat sequences
//
boolean cht_Responder(event_t *ev); boolean cht_Responder(event_t *ev);
void cht_Init(void); void cht_Init(void);
@ -56,7 +74,6 @@ void Command_Teleport_f(void);
void Command_RTeleport_f(void); void Command_RTeleport_f(void);
void Command_Skynum_f(void); void Command_Skynum_f(void);
void Command_Weather_f(void); void Command_Weather_f(void);
void Command_Toggletwod_f(void);
#ifdef _DEBUG #ifdef _DEBUG
void Command_CauseCfail_f(void); void Command_CauseCfail_f(void);
#endif #endif

View file

@ -1,4 +1,5 @@
hyudoro.c hyudoro.c
gardentop.c
shrink.c shrink.c
item-debris.c item-debris.c
spb.c spb.c

614
src/objects/gardentop.c Normal file
View file

@ -0,0 +1,614 @@
#include "../doomdef.h"
#include "../doomstat.h"
#include "../info.h"
#include "../k_kart.h"
#include "../k_objects.h"
#include "../m_random.h"
#include "../p_local.h"
#include "../r_local.h"
#include "../s_sound.h"
// TODO: separate from this file
static fixed_t K_FlipZOffset(mobj_t *us, mobj_t *them)
{
fixed_t z = 0;
if (them->eflags & MFE_VERTICALFLIP)
z += them->height;
if (us->eflags & MFE_VERTICALFLIP)
z -= us->height;
return z;
}
#define SPARKCOLOR SKINCOLOR_ROBIN
enum {
TOP_ANCHORED,
TOP_LOOSE,
};
#define topsfx_floating sfx_s3k7d
#define topsfx_grinding sfx_s3k79
#define topsfx_lift sfx_s3ka0
#define rider_top(o) ((o)->hnext)
#define top_mode(o) ((o)->extravalue1)
#define top_float(o) ((o)->lastlook)
#define top_sound(o) ((o)->extravalue2)
#define top_soundtic(o) ((o)->movecount)
/* TOP_ANCHORED */
#define top_rider(o) ((o)->tracer)
/* TOP_LOOSE */
#define top_waveangle(o) ((o)->movedir)
/* wavepause will take mobjinfo reactiontime automatically */
#define top_wavepause(o) ((o)->reactiontime)
#define spark_top(o) ((o)->target)
#define spark_angle(o) ((o)->movedir)
static inline player_t *
get_rider_player (mobj_t *rider)
{
return rider ? rider->player : NULL;
}
static inline player_t *
get_top_rider_player (mobj_t *top)
{
return get_rider_player(top_rider(top));
}
static inline boolean
is_top_grind_input (mobj_t *top)
{
player_t *player = get_top_rider_player(top);
return player && K_IsHoldingDownTop(player);
}
static inline boolean
is_top_grinding (mobj_t *top)
{
if (top_float(top) > 0)
return false;
if (!P_IsObjectOnGround(top))
return false;
return true;
}
static inline fixed_t
grind_spark_base_scale (player_t *player)
{
return FRACUNIT/2 +
player->topdriftheld * FRACUNIT
/ GARDENTOP_MAXGRINDTIME;
}
static inline INT32
get_player_steer_tilt
( player_t * player,
INT32 stages)
{
return player->steering
* stages
// 1 degree for a full turn
/ KART_FULLTURN
* ANG1
// stages is for fractions of a full turn, divide to
// get a fraction of a degree
/ stages
// angle is inverted in reverse gravity
* P_MobjFlip(player->mo);
}
static inline fixed_t
goofy_shake (fixed_t n)
{
return P_RandomRange(PR_DECORATION, -1, 1) * n;
}
static inline void
init_top
( mobj_t * top,
INT32 mode)
{
top_mode(top) = mode;
top_float(top) = 0;
top_sound(top) = sfx_None;
top_waveangle(top) = 0;
}
static void
spawn_spark
( mobj_t * top,
angle_t angle)
{
mobj_t *spark = P_SpawnMobjFromMobj(
top, 0, 0, 0, MT_GARDENTOPSPARK);
P_SetTarget(&spark_top(spark), top);
spark_angle(spark) = angle;
spark->color = SPARKCOLOR;
spark->spriteyscale = 3*FRACUNIT/4;
}
static void
spawn_spark_circle
( mobj_t * top,
UINT8 n)
{
const angle_t a = ANGLE_MAX / n;
UINT8 i;
for (i = 0; i < n; ++i)
{
spawn_spark(top, i * a);
}
}
static void
spawn_grind_spark (mobj_t *top)
{
mobj_t *rider = top_rider(top);
mobj_t *spark;
player_t *player = NULL;
fixed_t x = 0;
fixed_t y = 0;
angle_t angle = top->angle;
if (rider)
{
const fixed_t speed = -20 * top->scale;
angle = K_MomentumAngle(rider);
x = P_ReturnThrustX(rider, angle, speed);
y = P_ReturnThrustY(rider, angle, speed);
player = get_rider_player(rider);
}
spark = P_SpawnMobjFromMobj(
top, x, y, 0, MT_DRIFTSPARK);
spark->momx = x;
spark->momy = y;
P_SetMobjState(spark, S_DRIFTSPARK_A1);
spark->angle = angle;
spark->color = SPARKCOLOR;
if (player)
{
spark->destscale = FixedMul(spark->destscale,
grind_spark_base_scale(player));
P_SetScale(spark, spark->destscale);
}
}
static void
loop_sfx
( mobj_t * top,
sfxenum_t sfx)
{
switch (sfx)
{
case topsfx_floating:
if (S_SoundPlaying(top, sfx))
{
return;
}
break;
case topsfx_grinding:
if ((sfxenum_t)top_sound(top) != sfx)
{
top_soundtic(top) = leveltime;
}
/* FIXME: could this sound just be looped
normally? :face_holding_back_tears: */
if ((leveltime - top_soundtic(top)) % 28 > 0)
{
return;
}
break;
default:
break;
}
S_StartSound(top, sfx);
}
static void
modulate (mobj_t *top)
{
const fixed_t max_hover = top->height / 4;
const fixed_t hover_step = max_hover / 4;
sfxenum_t ambience = sfx_None;
if (is_top_grind_input(top))
{
if (top_float(top) == max_hover)
{
P_SetMobjState(top, S_GARDENTOP_SINKING1);
}
if (top_float(top) > 0)
{
top_float(top) = max(0,
top_float(top) - hover_step);
}
else if (P_IsObjectOnGround(top))
{
spawn_grind_spark(top);
ambience = topsfx_grinding;
}
}
else
{
if (top_float(top) == 0)
{
P_SetMobjState(top, S_GARDENTOP_FLOATING);
S_StopSoundByID(top, topsfx_grinding);
S_StartSound(top, topsfx_lift);
}
if (top_float(top) < max_hover)
{
top_float(top) = min(max_hover,
top_float(top) + hover_step);
}
else
{
ambience = topsfx_floating;
}
}
top->sprzoff = top_float(top) * P_MobjFlip(top);
if (ambience)
{
loop_sfx(top, ambience);
}
top_sound(top) = ambience;
}
static void
tilt (mobj_t *top)
{
player_t *player = get_top_rider_player(top);
INT32 tilt = top->rollangle;
if (is_top_grind_input(top))
{
const angle_t tiltmax = ANGLE_22h;
tilt += get_player_steer_tilt(player, 4);
if (abs(tilt) > tiltmax)
{
tilt = intsign(tilt) * tiltmax;
}
}
else
{
const angle_t decay = ANG1 * 2;
if (abs(tilt) > decay)
{
tilt -= intsign(tilt) * decay;
}
else
{
tilt = 0;
}
}
top->rollangle = tilt;
/* Vibrate left and right if you're about to lose it. */
if (player && player->topinfirst)
{
top->spritexoffset = P_LerpFlip(32*FRACUNIT, 1);
}
else
{
top->spritexoffset = 0;
}
/* Go ABSOLUTELY NUTS if the player is tumbling... */
if (player && player->tumbleBounces > 0)
{
const fixed_t yofs = 48 * FRACUNIT;
const fixed_t ofs3d = 24 * top->scale;
/* spriteyoffset scales, e.g. with K_Squish */
top->spriteyoffset = FixedDiv(
goofy_shake(yofs), top->spriteyscale);
top->sprxoff = goofy_shake(ofs3d);
top->spryoff = goofy_shake(ofs3d);
}
else
{
top->spriteyoffset = 0;
top->sprxoff = 0;
top->spryoff = 0;
}
}
static void
anchor_top (mobj_t *top)
{
mobj_t *rider = top_rider(top);
player_t *player = get_rider_player(rider);
if (player && player->curshield != KSHIELD_TOP)
{
P_RemoveMobj(top);
return;
}
tilt(top);
P_MoveOrigin(top, rider->x, rider->y,
rider->z + K_FlipZOffset(top, rider));
K_GenericExtraFlagsNoZAdjust(top, rider);
/* Copying the Z momentum lets the Top squash and stretch
as it falls with the player. Don't copy the X/Y
momentum because then it would always get slightly
ahead of the player. */
top->momx = 0;
top->momy = 0;
top->momz = rider->momz;
/* The Z momentum can put the Top slightly ahead of the
player in that axis too. It looks cool if the Top
falls below you but not if it bounces up. */
if (top->momz * P_MobjFlip(top) > 0)
{
top->momz = 0;
}
/* match rider's slope tilt */
top->pitch = rider->pitch;
top->roll = rider->roll;
}
static void
loose_think (mobj_t *top)
{
const fixed_t thrustamount = top->movefactor;
const angle_t momangle = K_MomentumAngle(top);
angle_t ang = top->angle;
mobj_t *ghost = P_SpawnGhostMobj(top);
ghost->colorized = true; // already has color!
if (AngleDelta(ang, momangle) > ANGLE_90)
{
top->angle = momangle;
}
if (top_wavepause(top))
{
top_wavepause(top)--;
}
else
{
/* oscillate between +90 and -90 degrees */
ang += AbsAngle(top_waveangle(top)) - ANGLE_90;
}
P_InstaThrust(top, top->angle, thrustamount);
P_Thrust(top, ang, thrustamount);
//top_waveangle(top) = (angle_t)top_waveangle(top) + ANG10;
top_waveangle(top) += ANG10;
/* intangibility grace period */
if (top->threshold > 0)
{
top->threshold--;
}
}
static void
anchor_spark (mobj_t *spark)
{
mobj_t *top = spark_top(spark);
mobj_t *rider = top_rider(top);
player_t *player = get_rider_player(rider);
const angle_t angle = top->angle + spark_angle(spark);
const fixed_t x = P_ReturnThrustX(top, angle, spark->scale);
const fixed_t y = P_ReturnThrustY(top, angle, spark->scale);
/* FIXME: THIS FUNCTION FUCKING SUCKS */
K_FlipFromObject(spark, top);
P_MoveOrigin(spark, top->x + x, top->y + y,
top->z + K_FlipZOffset(spark, top));
spark->angle = angle;
if (player)
{
const fixed_t topspeed =
K_GetKartSpeed(player, false, false);
const fixed_t speed = FixedHypot(
rider->momx, rider->momy);
P_SetScale(spark, FixedMul(top->scale, FRACUNIT/2 +
FixedDiv(speed / 2, topspeed)));
}
}
void
Obj_GardenTopDeploy (mobj_t *rider)
{
player_t *player = rider->player;
mobj_t *top = P_SpawnMobjFromMobj(
rider, 0, 0, 0, MT_GARDENTOP);
init_top(top, TOP_ANCHORED);
top->flags |= MF_NOCLIPHEIGHT;
/* only the player's shadow needs to be rendered */
top->shadowscale = 0;
P_SetTarget(&top_rider(top), rider);
P_SetTarget(&rider_top(rider), top);
if (player)
{
player->curshield = KSHIELD_TOP;
rider->radius = K_DefaultPlayerRadius(player);
}
spawn_spark_circle(top, 6);
}
mobj_t *
Obj_GardenTopThrow (player_t *player)
{
mobj_t *top = K_GetGardenTop(player);
if (top)
{
const fixed_t oldfloat = top_float(top);
const fixed_t height = top->height;
K_UpdateHnextList(player, true);
/* Sucks that another one needs to be spawned but
this way, the throwing function can be used. */
top = K_ThrowKartItem(
player, true, MT_GARDENTOP, 1, 0, 0);
init_top(top, TOP_LOOSE);
top_float(top) = oldfloat;
top_waveangle(top) = 0;
/* prevents it from hitting us on its way out */
top->threshold = 20;
/* ensure it's tangible */
top->flags &= ~(MF_NOCLIPTHING);
/* Put player PHYSICALLY on top. While riding the
Top, player collision was used and the player
technically remained on the ground. Now they
should fall off. */
P_SetOrigin(player->mo, player->mo->x, player->mo->y,
player->mo->z + height * P_MobjFlip(player->mo));
if (player->itemamount > 0)
player->itemamount--;
if (player->itemamount <= 0)
player->itemtype = KITEM_NONE;
player->curshield = KSHIELD_NONE;
player->mo->radius = K_DefaultPlayerRadius(player);
}
return top;
}
mobj_t *
Obj_GardenTopDestroy (player_t *player)
{
mobj_t *top = Obj_GardenTopThrow(player);
if (top)
{
/* kill kill kill die die die */
P_KillMobj(top, NULL, NULL, DMG_NORMAL);
}
return top;
}
void
Obj_GardenTopThink (mobj_t *top)
{
modulate(top);
switch (top_mode(top))
{
case TOP_ANCHORED:
if (top_rider(top))
{
anchor_top(top);
}
break;
case TOP_LOOSE:
loose_think(top);
break;
}
}
void
Obj_GardenTopSparkThink (mobj_t *spark)
{
mobj_t *top = spark_top(spark);
if (!top)
{
P_RemoveMobj(spark);
return;
}
anchor_spark(spark);
if (is_top_grinding(top))
{
spark->renderflags ^= RF_DONTDRAW;
}
else
{
spark->renderflags |= RF_DONTDRAW;
}
}
boolean
Obj_GardenTopPlayerIsGrinding (player_t *player)
{
mobj_t *top = K_GetGardenTop(player);
return top ? is_top_grinding(top) : false;
}

View file

@ -143,6 +143,7 @@ void Obj_OrbinautThink(mobj_t *th)
boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2) boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
{ {
boolean damageitem = false; boolean damageitem = false;
boolean tumbleitem = false;
boolean sprung = false; boolean sprung = false;
if ((orbinaut_selfdelay(t1) > 0 && t2->hitlag > 0) if ((orbinaut_selfdelay(t1) > 0 && t2->hitlag > 0)
@ -173,6 +174,11 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
return true; return true;
} }
if (t1->type == MT_GARDENTOP)
{
tumbleitem = true;
}
if (t2->player) if (t2->player)
{ {
if ((t2->player->flashing > 0 && t2->hitlag == 0) if ((t2->player->flashing > 0 && t2->hitlag == 0)
@ -190,7 +196,8 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
else else
{ {
// Player Damage // Player Damage
P_DamageMobj(t2, t1, t1->target, 1, DMG_WIPEOUT|DMG_WOMBO); P_DamageMobj(t2, t1, t1->target, 1, DMG_WOMBO |
(tumbleitem ? DMG_TUMBLE : DMG_WIPEOUT));
K_KartBouncing(t2, t1); K_KartBouncing(t2, t1);
S_StartSound(t2, sfx_s3k7b); S_StartSound(t2, sfx_s3k7b);
} }
@ -233,6 +240,11 @@ boolean Obj_OrbinautJawzCollide(mobj_t *t1, mobj_t *t2)
damageitem = true; damageitem = true;
} }
if (t1->type == MT_GARDENTOP)
{
damageitem = false;
}
if (damageitem) if (damageitem)
{ {
// This Item Damage // This Item Damage

View file

@ -8216,7 +8216,7 @@ void A_OrbitNights(mobj_t* actor)
if (!actor->target) if (!actor->target)
{ {
if (cv_debug && !(actor->target && actor->target->player)) if (cht_debug && !(actor->target && actor->target->player))
CONS_Printf("ERROR: Powerup has no target!\n"); CONS_Printf("ERROR: Powerup has no target!\n");
return; return;
} }
@ -8286,7 +8286,7 @@ void A_SetObjectState(mobj_t *actor)
if ((!locvar2 && !actor->target) || (locvar2 && !actor->tracer)) if ((!locvar2 && !actor->target) || (locvar2 && !actor->tracer))
{ {
if (cv_debug) if (cht_debug)
CONS_Printf("A_SetObjectState: No target to change state!\n"); CONS_Printf("A_SetObjectState: No target to change state!\n");
return; return;
} }
@ -8377,7 +8377,7 @@ void A_KnockBack(mobj_t *actor)
if (!target) if (!target)
{ {
if(cv_debug) if(cht_debug)
CONS_Printf("A_KnockBack: No target!\n"); CONS_Printf("A_KnockBack: No target!\n");
return; return;
} }
@ -8441,7 +8441,7 @@ void A_RingDrain(mobj_t *actor)
if (!actor->target || !actor->target->player) if (!actor->target || !actor->target->player)
{ {
if(cv_debug) if(cht_debug)
CONS_Printf("A_RingDrain: No player targeted!\n"); CONS_Printf("A_RingDrain: No player targeted!\n");
return; return;
} }
@ -8645,7 +8645,7 @@ void A_Custom3DRotate(mobj_t *actor)
if (hspeed==0 && vspeed==0) if (hspeed==0 && vspeed==0)
{ {
if (cv_debug) if (cht_debug)
CONS_Printf("Error: A_Custom3DRotate: Object has no speed.\n"); CONS_Printf("Error: A_Custom3DRotate: Object has no speed.\n");
return; return;
} }
@ -9080,7 +9080,7 @@ void A_SetCustomValue(mobj_t *actor)
if (LUA_CallAction(A_SETCUSTOMVALUE, actor)) if (LUA_CallAction(A_SETCUSTOMVALUE, actor))
return; return;
if (cv_debug) if (cht_debug)
CONS_Printf("Init custom value is %d\n", actor->cusval); CONS_Printf("Init custom value is %d\n", actor->cusval);
if (locvar1 == 0 && locvar2 == 4) if (locvar1 == 0 && locvar2 == 4)
@ -9100,7 +9100,7 @@ void A_SetCustomValue(mobj_t *actor)
else // replace else // replace
actor->cusval = locvar1; actor->cusval = locvar1;
if(cv_debug) if(cht_debug)
CONS_Printf("New custom value is %d\n", actor->cusval); CONS_Printf("New custom value is %d\n", actor->cusval);
} }
@ -9467,7 +9467,7 @@ void A_SetScale(mobj_t *actor)
if (locvar1 <= 0) if (locvar1 <= 0)
{ {
if(cv_debug) if(cht_debug)
CONS_Printf("A_SetScale: Valid scale not specified!\n"); CONS_Printf("A_SetScale: Valid scale not specified!\n");
return; return;
} }
@ -9481,7 +9481,7 @@ void A_SetScale(mobj_t *actor)
if (!target) if (!target)
{ {
if(cv_debug) if(cht_debug)
CONS_Printf("A_SetScale: No target!\n"); CONS_Printf("A_SetScale: No target!\n");
return; return;
} }
@ -9522,7 +9522,7 @@ void A_RemoteDamage(mobj_t *actor)
if (!target) if (!target)
{ {
if(cv_debug) if(cht_debug)
CONS_Printf("A_RemoteDamage: No target!\n"); CONS_Printf("A_RemoteDamage: No target!\n");
return; return;
} }
@ -13149,7 +13149,7 @@ void A_ItemPop(mobj_t *actor)
if (!(actor->target && actor->target->player)) if (!(actor->target && actor->target->player))
{ {
if (cv_debug && !(actor->target && actor->target->player)) if (cht_debug && !(actor->target && actor->target->player))
CONS_Printf("ERROR: Powerup has no target!\n"); CONS_Printf("ERROR: Powerup has no target!\n");
return; return;
} }

View file

@ -1879,7 +1879,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (player) // Player is the target if (player) // Player is the target
{ {
if (player->cheats & PC_GODMODE) if (player->pflags & PF_GODMODE)
return false; return false;
if (!force) if (!force)
@ -2019,6 +2019,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
player->emeralds = 0; player->emeralds = 0;
K_CheckEmeralds(source->player); K_CheckEmeralds(source->player);
} }
/* Drop "shield" immediately on contact. */
if (source->player->curshield == KSHIELD_TOP)
{
Obj_GardenTopDestroy(source->player);
}
} }
else else
{ {

View file

@ -195,6 +195,8 @@ void P_PlayerAfterThink(player_t *player);
void P_DoPlayerExit(player_t *player); void P_DoPlayerExit(player_t *player);
void P_DoTimeOver(player_t *player); void P_DoTimeOver(player_t *player);
void P_ResetPlayerCheats(void);
void P_InstaThrust(mobj_t *mo, angle_t angle, fixed_t move); void P_InstaThrust(mobj_t *mo, angle_t angle, fixed_t move);
fixed_t P_ReturnThrustX(mobj_t *mo, angle_t angle, fixed_t move); fixed_t P_ReturnThrustX(mobj_t *mo, angle_t angle, fixed_t move);
fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move); fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move);
@ -315,7 +317,7 @@ fixed_t P_CameraCeilingZ(camera_t *mobj, sector_t *sector, sector_t *boundsec, f
boolean P_InsideANonSolidFFloor(mobj_t *mobj, ffloor_t *rover); boolean P_InsideANonSolidFFloor(mobj_t *mobj, ffloor_t *rover);
boolean P_CheckDeathPitCollide(mobj_t *mo); boolean P_CheckDeathPitCollide(mobj_t *mo);
boolean P_CheckSolidLava(ffloor_t *rover); boolean P_CheckSolidLava(mobj_t *mobj, ffloor_t *rover);
void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype); void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype);
mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zofs, mobjtype_t type); mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zofs, mobjtype_t type);
@ -337,8 +339,8 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
void P_Attract(mobj_t *source, mobj_t *enemy, boolean nightsgrab); void P_Attract(mobj_t *source, mobj_t *enemy, boolean nightsgrab);
mobj_t *P_GetClosestAxis(mobj_t *source); mobj_t *P_GetClosestAxis(mobj_t *source);
boolean P_CanRunOnWater(player_t *player, ffloor_t *rover); boolean P_CanRunOnWater(mobj_t *mobj, ffloor_t *rover);
boolean P_CheckSolidFFloorSurface(player_t *player, ffloor_t *rover); boolean P_CheckSolidFFloorSurface(mobj_t *mobj, ffloor_t *rover);
void P_MaceRotate(mobj_t *center, INT32 baserot, INT32 baseprevrot); void P_MaceRotate(mobj_t *center, INT32 baserot, INT32 baseprevrot);

View file

@ -866,6 +866,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
&& (tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ && (tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ
|| tmthing->type == MT_BANANA || tmthing->type == MT_EGGMANITEM || tmthing->type == MT_BALLHOG || tmthing->type == MT_BANANA || tmthing->type == MT_EGGMANITEM || tmthing->type == MT_BALLHOG
|| tmthing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || tmthing->type == MT_SINK || tmthing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || tmthing->type == MT_SINK
|| tmthing->type == MT_GARDENTOP
|| (tmthing->type == MT_PLAYER && thing->target != tmthing))) || (tmthing->type == MT_PLAYER && thing->target != tmthing)))
{ {
// see if it went over / under // see if it went over / under
@ -881,6 +882,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
&& (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ && (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ
|| thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG || thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG
|| thing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || thing->type == MT_SINK || thing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || thing->type == MT_SINK
|| thing->type == MT_GARDENTOP
|| (thing->type == MT_PLAYER && tmthing->target != thing))) || (thing->type == MT_PLAYER && tmthing->target != thing)))
{ {
// see if it went over / under // see if it went over / under
@ -901,6 +903,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
&& (tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ && (tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ
|| tmthing->type == MT_BANANA || tmthing->type == MT_EGGMANITEM || tmthing->type == MT_BALLHOG || tmthing->type == MT_BANANA || tmthing->type == MT_EGGMANITEM || tmthing->type == MT_BALLHOG
|| tmthing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || tmthing->type == MT_SINK || tmthing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || tmthing->type == MT_SINK
|| tmthing->type == MT_GARDENTOP
|| (tmthing->type == MT_PLAYER))) || (tmthing->type == MT_PLAYER)))
{ {
// see if it went over / under // see if it went over / under
@ -915,6 +918,7 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
&& (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ && (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ
|| thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG || thing->type == MT_BANANA || thing->type == MT_EGGMANITEM || thing->type == MT_BALLHOG
|| thing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || thing->type == MT_SINK || thing->type == MT_SSMINE || tmthing->type == MT_LANDMINE || thing->type == MT_SINK
|| thing->type == MT_GARDENTOP
|| (thing->type == MT_PLAYER))) || (thing->type == MT_PLAYER)))
{ {
// see if it went over / under // see if it went over / under
@ -932,7 +936,8 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
return BMIT_CONTINUE; return BMIT_CONTINUE;
if (tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ if (tmthing->type == MT_ORBINAUT || tmthing->type == MT_JAWZ
|| tmthing->type == MT_ORBINAUT_SHIELD || tmthing->type == MT_JAWZ_SHIELD) || tmthing->type == MT_ORBINAUT_SHIELD || tmthing->type == MT_JAWZ_SHIELD
|| tmthing->type == MT_GARDENTOP)
{ {
// see if it went over / under // see if it went over / under
if (tmthing->z > thing->z + thing->height) if (tmthing->z > thing->z + thing->height)
@ -943,7 +948,8 @@ static BlockItReturn_t PIT_CheckThing(mobj_t *thing)
return Obj_OrbinautJawzCollide(tmthing, thing) ? BMIT_CONTINUE : BMIT_ABORT; return Obj_OrbinautJawzCollide(tmthing, thing) ? BMIT_CONTINUE : BMIT_ABORT;
} }
else if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ else if (thing->type == MT_ORBINAUT || thing->type == MT_JAWZ
|| thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD) || thing->type == MT_ORBINAUT_SHIELD || thing->type == MT_JAWZ_SHIELD
|| thing->type == MT_GARDENTOP)
{ {
// see if it went over / under // see if it went over / under
if (tmthing->z > thing->z + thing->height) if (tmthing->z > thing->z + thing->height)
@ -1940,7 +1946,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
continue; continue;
} }
if (thing->player && P_CheckSolidFFloorSurface(thing->player, rover)) if (P_CheckSolidFFloorSurface(thing, rover))
; ;
else if (thing->type == MT_SKIM && (rover->flags & FF_SWIMMABLE)) else if (thing->type == MT_SKIM && (rover->flags & FF_SWIMMABLE))
; ;
@ -2354,11 +2360,7 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam)
fixed_t tryx = thiscam->x; fixed_t tryx = thiscam->x;
fixed_t tryy = thiscam->y; fixed_t tryy = thiscam->y;
#ifndef NOCLIPCAM
if ((players[displayplayers[i]].cheats & PC_NOCLIP) || (leveltime < introtime)) // Noclipping player camera noclips too!!
#else
if (!(players[displayplayers[i]].pflags & PF_NOCONTEST)) // Time Over should not clip through walls if (!(players[displayplayers[i]].pflags & PF_NOCONTEST)) // Time Over should not clip through walls
#endif
{ {
floatok = true; floatok = true;
thiscam->floorz = thiscam->z; thiscam->floorz = thiscam->z;
@ -2524,9 +2526,7 @@ static boolean P_WaterRunning(mobj_t *thing)
static boolean P_WaterStepUp(mobj_t *thing) static boolean P_WaterStepUp(mobj_t *thing)
{ {
player_t *player = thing->player; return (thing->waterskip > 0 || P_WaterRunning(thing));
return (player && player->waterskip) ||
P_WaterRunning(thing);
} }
fixed_t P_BaseStepUp(void) fixed_t P_BaseStepUp(void)
@ -2844,6 +2844,11 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
thing->terrain = NULL; thing->terrain = NULL;
} }
if (thing->player && K_IsRidingFloatingTop(thing->player))
{
stairjank = false;
}
/* FIXME: slope step down (even up) has some false /* FIXME: slope step down (even up) has some false
positives, so just ignore them entirely. */ positives, so just ignore them entirely. */
if (stairjank && !oldslope && !thing->standingslope && if (stairjank && !oldslope && !thing->standingslope &&

View file

@ -777,7 +777,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
if (!(rover->flags & FF_EXISTS)) if (!(rover->flags & FF_EXISTS))
continue; continue;
if (mobj->player && P_CheckSolidFFloorSurface(mobj->player, rover)) if (P_CheckSolidFFloorSurface(mobj, rover))
; ;
else if (!((rover->flags & FF_BLOCKPLAYER && mobj->player) else if (!((rover->flags & FF_BLOCKPLAYER && mobj->player)
|| (rover->flags & FF_BLOCKOTHERS && !mobj->player))) || (rover->flags & FF_BLOCKOTHERS && !mobj->player)))
@ -821,7 +821,7 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
if (!(rover->flags & FF_EXISTS)) if (!(rover->flags & FF_EXISTS))
continue; continue;
if (mobj->player && P_CheckSolidFFloorSurface(mobj->player, rover)) if (P_CheckSolidFFloorSurface(mobj, rover))
; ;
else if (!((rover->flags & FF_BLOCKPLAYER && mobj->player) else if (!((rover->flags & FF_BLOCKPLAYER && mobj->player)
|| (rover->flags & FF_BLOCKOTHERS && !mobj->player))) || (rover->flags & FF_BLOCKOTHERS && !mobj->player)))

View file

@ -1105,6 +1105,11 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
} }
} }
if (mo->waterskip > 0)
{
gravityadd = (4*gravityadd)/3;
}
if (mo->player) if (mo->player)
{ {
if (mo->flags2 & MF2_OBJECTFLIP) if (mo->flags2 & MF2_OBJECTFLIP)
@ -1118,11 +1123,6 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
P_PlayerFlip(mo); P_PlayerFlip(mo);
} }
if (mo->player->waterskip)
{
gravityadd = (4*gravityadd)/3;
}
if (mo->player->trickpanel >= 2) if (mo->player->trickpanel >= 2)
{ {
gravityadd = (5*gravityadd)/2; gravityadd = (5*gravityadd)/2;
@ -1133,7 +1133,11 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
gravityadd = FixedMul(TUMBLEGRAVITY, gravityadd); gravityadd = FixedMul(TUMBLEGRAVITY, gravityadd);
} }
if (mo->player->fastfall != 0) if (K_IsHoldingDownTop(mo->player))
{
gravityadd = (5*gravityadd)/2;
}
else if (mo->player->fastfall != 0)
{ {
// Fast falling // Fast falling
gravityadd *= 4; gravityadd *= 4;
@ -1726,8 +1730,7 @@ void P_XYMovement(mobj_t *mo)
//{ SRB2kart - Orbinaut, Ballhog //{ SRB2kart - Orbinaut, Ballhog
// Bump sparks // Bump sparks
if (mo->type == MT_ORBINAUT || mo->type == MT_BALLHOG if (mo->type == MT_ORBINAUT || mo->type == MT_BALLHOG)
|| mo->type == MT_DUELBOMB)
{ {
mobj_t *fx; mobj_t *fx;
fx = P_SpawnMobj(mo->x, mo->y, mo->z, MT_BUMP); fx = P_SpawnMobj(mo->x, mo->y, mo->z, MT_BUMP);
@ -1741,6 +1744,7 @@ void P_XYMovement(mobj_t *mo)
switch (mo->type) switch (mo->type)
{ {
case MT_ORBINAUT: // Orbinaut speed decreasing case MT_ORBINAUT: // Orbinaut speed decreasing
case MT_GARDENTOP:
if (mo->health > 1) if (mo->health > 1)
{ {
S_StartSound(mo, mo->info->attacksound); S_StartSound(mo, mo->info->attacksound);
@ -1945,7 +1949,7 @@ void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motype)
topheight = P_GetFOFTopZ(mo, sector, rover, mo->x, mo->y, NULL); topheight = P_GetFOFTopZ(mo, sector, rover, mo->x, mo->y, NULL);
bottomheight = P_GetFOFBottomZ(mo, sector, rover, mo->x, mo->y, NULL); bottomheight = P_GetFOFBottomZ(mo, sector, rover, mo->x, mo->y, NULL);
if (mo->player && P_CheckSolidFFloorSurface(mo->player, rover)) // only the player should stand on lava or run on water if (P_CheckSolidFFloorSurface(mo, rover)) // only the player should stand on lava or run on water
; ;
else if (motype != 0 && rover->flags & FF_SWIMMABLE) // "scenery" only else if (motype != 0 && rover->flags & FF_SWIMMABLE) // "scenery" only
continue; continue;
@ -2089,7 +2093,7 @@ boolean P_CheckDeathPitCollide(mobj_t *mo)
I_Assert(mo != NULL); I_Assert(mo != NULL);
I_Assert(!P_MobjWasRemoved(mo)); I_Assert(!P_MobjWasRemoved(mo));
if (mo->player && mo->player->cheats & PC_GODMODE) if (mo->player && mo->player->pflags & PF_GODMODE)
return false; return false;
if (((mo->z <= mo->subsector->sector->floorheight if (((mo->z <= mo->subsector->sector->floorheight
@ -2104,11 +2108,18 @@ boolean P_CheckDeathPitCollide(mobj_t *mo)
return false; return false;
} }
boolean P_CheckSolidLava(ffloor_t *rover) boolean P_CheckSolidLava(mobj_t *mobj, ffloor_t *rover)
{ {
if (mobj->player == NULL)
{
return false;
}
if (rover->flags & FF_SWIMMABLE && GETSECSPECIAL(rover->master->frontsector->special, 1) == 3 if (rover->flags & FF_SWIMMABLE && GETSECSPECIAL(rover->master->frontsector->special, 1) == 3
&& !(rover->master->flags & ML_BLOCKPLAYERS)) && !(rover->master->flags & ML_BLOCKPLAYERS))
return true; {
return true;
}
return false; return false;
} }
@ -2186,18 +2197,6 @@ boolean P_ZMovement(mobj_t *mo)
case MT_BIGTUMBLEWEED: case MT_BIGTUMBLEWEED:
case MT_LITTLETUMBLEWEED: case MT_LITTLETUMBLEWEED:
case MT_SHELL: case MT_SHELL:
// SRB2kart stuff that should die in pits
// Shouldn't stop moving along the Z if there's no speed though!
case MT_EGGMANITEM:
case MT_BANANA:
case MT_ORBINAUT:
case MT_JAWZ:
case MT_BALLHOG:
case MT_SSMINE:
case MT_LANDMINE:
case MT_DROPTARGET:
case MT_BUBBLESHIELDTRAP:
case MT_DUELBOMB:
// Remove stuff from death pits. // Remove stuff from death pits.
if (P_CheckDeathPitCollide(mo)) if (P_CheckDeathPitCollide(mo))
{ {
@ -2279,6 +2278,17 @@ boolean P_ZMovement(mobj_t *mo)
} }
break; break;
default: default:
// SRB2kart stuff that should die in pits
// Shouldn't stop moving along the Z if there's no speed though!
if (P_CanDeleteKartItem(mo->type))
{
// Remove stuff from death pits.
if (P_CheckDeathPitCollide(mo))
{
P_RemoveMobj(mo);
return false;
}
}
break; break;
} }
@ -3087,31 +3097,115 @@ boolean P_SceneryZMovement(mobj_t *mo)
return true; return true;
} }
//
// P_CanRunOnWater // P_CanRunOnWater
// //
// Returns true if player can waterrun on the 3D floor // Returns true if player can water run on a 3D floor
// //
boolean P_CanRunOnWater(player_t *player, ffloor_t *rover) boolean P_CanRunOnWater(mobj_t *mobj, ffloor_t *rover)
{ {
boolean flip = player->mo->eflags & MFE_VERTICALFLIP; const boolean flip = (mobj->eflags & MFE_VERTICALFLIP);
fixed_t surfaceheight = flip ? player->mo->waterbottom : player->mo->watertop; player_t *player = mobj->player;
fixed_t playerbottom = flip ? (player->mo->z + player->mo->height) : player->mo->z;
fixed_t clip = flip ? (surfaceheight - playerbottom) : (playerbottom - surfaceheight);
fixed_t span = player->mo->watertop - player->mo->waterbottom;
return fixed_t surfaceheight = INT32_MAX;
clip > -(player->mo->height / 2) && fixed_t surfDiff = INT32_MAX;
span > player->mo->height &&
player->speed / 5 > abs(player->mo->momz) && fixed_t floorheight = INT32_MAX;
player->speed > K_GetKartSpeed(player, false, false) && fixed_t floorDiff = INT32_MAX;
K_WaterRun(player) &&
(rover->flags & FF_SWIMMABLE); fixed_t mobjbottom = INT32_MAX;
fixed_t maxStep = INT32_MAX;
boolean doifit = false;
pslope_t *waterSlope = NULL;
angle_t ourZAng = 0;
angle_t waterZAng = 0;
if (rover == NULL)
{
// No rover.
return false;
}
if (!(rover->flags & FF_SWIMMABLE))
{
// It's not even a water FOF.
return false;
}
if (player != NULL
&& player->carry != CR_NONE) // Special carry state.
{
// No good player state.
return false;
}
if (P_IsObjectOnGround(mobj) == false)
{
// Don't allow jumping onto water to start a water run.
// (Already water running still counts as being on the ground.)
return false;
}
if (K_WaterRun(mobj) == false)
{
// Basic conditions for enabling water run.
return false;
}
if (mobj->standingslope != NULL)
{
ourZAng = mobj->standingslope->zangle;
}
waterSlope = (flip ? *rover->b_slope : *rover->t_slope);
if (waterSlope != NULL)
{
waterZAng = waterSlope->zangle;
}
if (ourZAng != waterZAng)
{
// The surface slopes are different.
return false;
}
surfaceheight = flip ? P_GetFFloorBottomZAt(rover, mobj->x, mobj->y) : P_GetFFloorTopZAt(rover, mobj->x, mobj->y);
mobjbottom = flip ? (mobj->z + mobj->height) : mobj->z;
doifit = flip ? (surfaceheight - mobj->floorz >= mobj->height) : (mobj->ceilingz - surfaceheight >= mobj->height);
if (!doifit)
{
// Object can't fit in this space.
return false;
}
maxStep = P_GetThingStepUp(mobj);
surfDiff = flip ? (surfaceheight - mobjbottom) : (mobjbottom - surfaceheight);
if (surfDiff <= maxStep && surfDiff >= 0)
{
// We start water run IF we can step-down!
floorheight = flip ? P_GetSectorCeilingZAt(mobj->subsector->sector, mobj->x, mobj->y) : P_GetSectorFloorZAt(mobj->subsector->sector, mobj->x, mobj->y);
floorDiff = flip ? (floorheight - mobjbottom) : (mobjbottom - floorheight);
if (floorDiff <= maxStep && floorDiff >= 0)
{
// ... but NOT if real floor is in range.
// FIXME: Count solid FOFs in this check
return false;
}
return true;
}
return false;
} }
boolean P_CheckSolidFFloorSurface(player_t *player, ffloor_t *rover) boolean P_CheckSolidFFloorSurface(mobj_t *mobj, ffloor_t *rover)
{ {
return P_CheckSolidLava(rover) || return P_CheckSolidLava(mobj, rover) ||
P_CanRunOnWater(player, rover); P_CanRunOnWater(mobj, rover);
} }
// //
@ -3133,6 +3227,8 @@ void P_MobjCheckWater(mobj_t *mobj)
boolean wasgroundpounding = false; boolean wasgroundpounding = false;
fixed_t top2 = P_GetSectorCeilingZAt(sector, mobj->x, mobj->y); fixed_t top2 = P_GetSectorCeilingZAt(sector, mobj->x, mobj->y);
fixed_t bot2 = P_GetSectorFloorZAt(sector, mobj->x, mobj->y); fixed_t bot2 = P_GetSectorFloorZAt(sector, mobj->x, mobj->y);
pslope_t *topslope = NULL;
pslope_t *bottomslope = NULL;
// Default if no water exists. // Default if no water exists.
mobj->watertop = mobj->waterbottom = mobj->z - 1000*FRACUNIT; mobj->watertop = mobj->waterbottom = mobj->z - 1000*FRACUNIT;
@ -3175,6 +3271,9 @@ void P_MobjCheckWater(mobj_t *mobj)
mobj->watertop = topheight; mobj->watertop = topheight;
mobj->waterbottom = bottomheight; mobj->waterbottom = bottomheight;
topslope = *rover->t_slope;
bottomslope = *rover->b_slope;
// Just touching the water? // Just touching the water?
if (((mobj->eflags & MFE_VERTICALFLIP) && thingtop - height < bottomheight) if (((mobj->eflags & MFE_VERTICALFLIP) && thingtop - height < bottomheight)
|| (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z + height > topheight)) || (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z + height > topheight))
@ -3212,13 +3311,28 @@ void P_MobjCheckWater(mobj_t *mobj)
mobj->watertop = mobj->z; mobj->watertop = mobj->z;
mobj->waterbottom = mobj->z - height; mobj->waterbottom = mobj->z - height;
} }
topslope = bottomslope = NULL;
} }
} }
// Spectators and dead players don't get to do any of the things after this. if (P_IsObjectOnGround(mobj) == true)
if (p && (p->spectator || p->playerstate != PST_LIVE))
{ {
return; mobj->waterskip = 0;
}
if (p != NULL)
{
// Spectators and dead players don't get to do any of the things after this.
if (p->spectator || p->playerstate != PST_LIVE)
{
return;
}
}
if (mobj->flags & MF_APPLYTERRAIN)
{
K_SpawnWaterRunParticles(mobj);
} }
// The rest of this code only executes on a water state change. // The rest of this code only executes on a water state change.
@ -3227,20 +3341,21 @@ void P_MobjCheckWater(mobj_t *mobj)
return; return;
} }
if (p && !p->waterskip && if (p != NULL
p->curshield != KSHIELD_BUBBLE && wasinwater) && p->curshield != KSHIELD_BUBBLE
&& mobj->waterskip == 0
&& wasinwater)
{ {
// Play the gasp sound
S_StartSound(mobj, sfx_s3k38); S_StartSound(mobj, sfx_s3k38);
} }
if ((p) // Players if (mobj->flags & MF_APPLYTERRAIN)
|| (mobj->flags & MF_PUSHABLE) // Pushables
|| ((mobj->info->flags & MF_PUSHABLE) && mobj->fuse) // Previously pushable, might be moving still
)
{ {
fixed_t waterZ = INT32_MAX; fixed_t waterZ = INT32_MAX;
fixed_t solidZ = INT32_MAX; fixed_t solidZ = INT32_MAX;
fixed_t diff = INT32_MAX; fixed_t diff = INT32_MAX;
INT32 waterDelta = 0;
fixed_t thingZ = INT32_MAX; fixed_t thingZ = INT32_MAX;
boolean splashValid = false; boolean splashValid = false;
@ -3249,11 +3364,19 @@ void P_MobjCheckWater(mobj_t *mobj)
{ {
waterZ = mobj->waterbottom; waterZ = mobj->waterbottom;
solidZ = mobj->ceilingz; solidZ = mobj->ceilingz;
if (bottomslope)
{
waterDelta = bottomslope->zdelta;
}
} }
else else
{ {
waterZ = mobj->watertop; waterZ = mobj->watertop;
solidZ = mobj->floorz; solidZ = mobj->floorz;
if (topslope)
{
waterDelta = topslope->zdelta;
}
} }
diff = waterZ - solidZ; diff = waterZ - solidZ;
@ -3322,25 +3445,22 @@ void P_MobjCheckWater(mobj_t *mobj)
splish->destscale = mobj->scale; splish->destscale = mobj->scale;
P_SetScale(splish, mobj->scale); P_SetScale(splish, mobj->scale);
// skipping stone!
if (K_WaterSkip(mobj) == true
&& abs(waterDelta) < FRACUNIT/21) // Only on flat water
{
const fixed_t hop = 5 * mapobjectscale;
mobj->momx = (4*mobj->momx)/5;
mobj->momy = (4*mobj->momy)/5;
mobj->momz = hop * P_MobjFlip(mobj);
mobj->waterskip++;
}
} }
// skipping stone!
if (p && p->waterskip < 2
&& ((p->speed/3 > abs(mobj->momz)) // Going more forward than horizontal, so you can skip across the water.
|| (p->speed > 20*mapobjectscale && p->waterskip)) // Already skipped once, so you can skip once more!
&& (splashValid == true))
{
const fixed_t hop = 5 * mobj->scale;
mobj->momx = (4*mobj->momx)/5;
mobj->momy = (4*mobj->momy)/5;
mobj->momz = hop * P_MobjFlip(mobj);
p->waterskip++;
}
} }
else if (P_MobjFlip(mobj) * mobj->momz > 0) else
{ {
if (splashValid == true && !(mobj->eflags & MFE_UNDERWATER)) // underwater check to prevent splashes on opposite side if (splashValid == true && !(mobj->eflags & MFE_UNDERWATER)) // underwater check to prevent splashes on opposite side
{ {
@ -3628,7 +3748,7 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
player->karthud[khud_timeovercam] = (2*TICRATE)+1; player->karthud[khud_timeovercam] = (2*TICRATE)+1;
} }
if (!resetcalled && !(player->cheats & PC_NOCLIP || leveltime < introtime) && !P_CheckSight(&dummy, player->mo)) // TODO: "P_CheckCameraSight" instead. if (!resetcalled && !(player->mo->flags & MF_NOCLIP || leveltime < introtime) && !P_CheckSight(&dummy, player->mo)) // TODO: "P_CheckCameraSight" instead.
{ {
P_ResetCamera(player, thiscam); P_ResetCamera(player, thiscam);
} }
@ -4472,7 +4592,7 @@ mobj_t *P_GetClosestAxis(mobj_t *source)
} }
if (closestaxis == NULL) if (closestaxis == NULL)
CONS_Debug(DBG_NIGHTS, "ERROR: No axis points found!\n"); CONS_Alert(CONS_ERROR, "No axis points found!\n");
return closestaxis; return closestaxis;
} }
@ -4544,7 +4664,7 @@ void P_SpawnHoopOfSomething(fixed_t x, fixed_t y, fixed_t z, fixed_t radius, INT
if (!axis) if (!axis)
{ {
CONS_Debug(DBG_NIGHTS, "You forgot to put axis points in the map!\n"); CONS_Alert(CONS_WARNING, "You forgot to put axis points in the map!\n");
return; return;
} }
@ -5000,20 +5120,59 @@ cont:
} }
// Kartitem stuff. // Kartitem stuff.
// This item is never attached to a player -- it can DIE
// unconditionally in death sectors.
boolean P_IsKartFieldItem(INT32 type)
{
switch (type)
{
case MT_BANANA:
case MT_EGGMANITEM:
case MT_ORBINAUT:
case MT_JAWZ:
case MT_SSMINE:
case MT_LANDMINE:
case MT_BALLHOG:
case MT_BUBBLESHIELDTRAP:
case MT_POGOSPRING:
case MT_SINK:
case MT_DROPTARGET:
case MT_DUELBOMB:
return true;
default:
return false;
}
}
boolean P_IsKartItem(INT32 type) boolean P_IsKartItem(INT32 type)
{ {
if (type == MT_EGGMANITEM || type == MT_EGGMANITEM_SHIELD || switch (type)
type == MT_BANANA || type == MT_BANANA_SHIELD || {
type == MT_DROPTARGET || type == MT_DROPTARGET_SHIELD || case MT_EGGMANITEM_SHIELD:
type == MT_ORBINAUT || type == MT_ORBINAUT_SHIELD || case MT_BANANA_SHIELD:
type == MT_JAWZ || type == MT_JAWZ_SHIELD || case MT_DROPTARGET_SHIELD:
type == MT_SSMINE || type == MT_SSMINE_SHIELD || case MT_ORBINAUT_SHIELD:
type == MT_SINK || type == MT_SINK_SHIELD || case MT_JAWZ_SHIELD:
type == MT_SPB || type == MT_BALLHOG || type == MT_BUBBLESHIELDTRAP || case MT_SSMINE_SHIELD:
type == MT_LANDMINE) case MT_SINK_SHIELD:
return true; case MT_SPB:
else case MT_HYUDORO:
return false; return true;
default:
return P_IsKartFieldItem(type);
}
}
// This item can die in death sectors. There may be some
// special conditions for items that don't switch types...
// TODO: just make a general function for things that should
// die like this?
boolean P_CanDeleteKartItem(INT32 type)
{
return P_IsKartFieldItem(type);
} }
// Called when a kart item "thinks" // Called when a kart item "thinks"
@ -6335,6 +6494,7 @@ static boolean P_MobjDeadThink(mobj_t *mobj)
return false; return false;
} }
/* FALLTHRU */ /* FALLTHRU */
case MT_GARDENTOP:
case MT_ORBINAUT_SHIELD: case MT_ORBINAUT_SHIELD:
case MT_BANANA_SHIELD: case MT_BANANA_SHIELD:
case MT_EGGMANITEM_SHIELD: case MT_EGGMANITEM_SHIELD:
@ -6725,11 +6885,13 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
case MT_ORBINAUT: case MT_ORBINAUT:
{ {
Obj_OrbinautThink(mobj); Obj_OrbinautThink(mobj);
P_MobjCheckWater(mobj);
break; break;
} }
case MT_JAWZ: case MT_JAWZ:
{ {
Obj_JawzThink(mobj); Obj_JawzThink(mobj);
P_MobjCheckWater(mobj);
break; break;
} }
case MT_EGGMANITEM: case MT_EGGMANITEM:
@ -6793,6 +6955,8 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
if (mobj->threshold > 0) if (mobj->threshold > 0)
mobj->threshold--; mobj->threshold--;
P_MobjCheckWater(mobj);
} }
break; break;
case MT_SINK: case MT_SINK:
@ -7049,7 +7213,11 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
} }
} }
break; break;
case MT_TRIPWIREBOOST: case MT_TRIPWIREBOOST: {
mobj_t *top;
fixed_t newHeight;
fixed_t newScale;
if (!mobj->target || !mobj->target->health if (!mobj->target || !mobj->target->health
|| !mobj->target->player || !mobj->target->player->tripwireLeniency) || !mobj->target->player || !mobj->target->player->tripwireLeniency)
{ {
@ -7057,10 +7225,21 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
return false; return false;
} }
newHeight = mobj->target->height;
newScale = mobj->target->scale;
top = K_GetGardenTop(mobj->target->player);
if (top)
{
newHeight += 5 * top->height / 4;
newScale = FixedMul(newScale, FixedDiv(newHeight / 2, mobj->target->height));
}
mobj->angle = K_MomentumAngle(mobj->target); mobj->angle = K_MomentumAngle(mobj->target);
P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (mobj->target->height >> 1)); P_MoveOrigin(mobj, mobj->target->x, mobj->target->y, mobj->target->z + (newHeight / 2));
mobj->destscale = mobj->target->scale; mobj->destscale = newScale;
P_SetScale(mobj, mobj->target->scale); P_SetScale(mobj, newScale);
if (mobj->extravalue1) if (mobj->extravalue1)
{ {
@ -7132,6 +7311,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
} }
} }
break; break;
}
case MT_BOOSTFLAME: case MT_BOOSTFLAME:
if (!mobj->target || !mobj->target->health) if (!mobj->target || !mobj->target->health)
{ {
@ -7703,6 +7883,16 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
} }
break; break;
} }
case MT_GARDENTOP:
{
Obj_GardenTopThink(mobj);
break;
}
case MT_GARDENTOPSPARK:
{
Obj_GardenTopSparkThink(mobj);
break;
}
case MT_HYUDORO: case MT_HYUDORO:
{ {
Obj_HyudoroThink(mobj); Obj_HyudoroThink(mobj);
@ -9318,11 +9508,7 @@ void P_MobjThinker(mobj_t *mobj)
return; return;
// Destroy items sector special // Destroy items sector special
if (mobj->type == MT_BANANA || mobj->type == MT_EGGMANITEM if (P_CanDeleteKartItem(mobj->type))
|| mobj->type == MT_ORBINAUT || mobj->type == MT_BALLHOG
|| mobj->type == MT_JAWZ
|| mobj->type == MT_SSMINE || mobj->type == MT_BUBBLESHIELDTRAP
|| mobj->type == MT_LANDMINE)
{ {
if (mobj->health > 0 && P_MobjTouchingSectorSpecial(mobj, 4, 7, true)) if (mobj->health > 0 && P_MobjTouchingSectorSpecial(mobj, 4, 7, true))
{ {
@ -9749,6 +9935,7 @@ static void P_DefaultMobjShadowScale(mobj_t *thing)
case MT_BUBBLESHIELD: case MT_BUBBLESHIELD:
case MT_BUBBLESHIELDTRAP: case MT_BUBBLESHIELDTRAP:
case MT_FLAMESHIELD: case MT_FLAMESHIELD:
case MT_GARDENTOP:
thing->shadowscale = FRACUNIT; thing->shadowscale = FRACUNIT;
break; break;
case MT_RING: case MT_RING:
@ -9891,6 +10078,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
mobj->colorized = false; mobj->colorized = false;
mobj->hitlag = 0; mobj->hitlag = 0;
mobj->waterskip = 0;
// Set shadowscale here, before spawn hook so that Lua can change it // Set shadowscale here, before spawn hook so that Lua can change it
P_DefaultMobjShadowScale(mobj); P_DefaultMobjShadowScale(mobj);

View file

@ -169,7 +169,7 @@ typedef enum
typedef enum typedef enum
{ {
MF2_AXIS = 1, // It's a NiGHTS axis! (For faster checking) MF2_AXIS = 1, // It's a NiGHTS axis! (For faster checking)
MF2_TWOD = 1<<1, // Moves like it's in a 2D level // free: 1<<1
MF2_DONTRESPAWN = 1<<2, // Don't respawn this object! MF2_DONTRESPAWN = 1<<2, // Don't respawn this object!
// free: 1<<3 // free: 1<<3
MF2_AUTOMATIC = 1<<4, // Thrown ring has automatic properties MF2_AUTOMATIC = 1<<4, // Thrown ring has automatic properties
@ -409,6 +409,7 @@ typedef struct mobj_s
struct mobj_s *terrainOverlay; // Overlay sprite object for terrain struct mobj_s *terrainOverlay; // Overlay sprite object for terrain
INT32 hitlag; // Sal-style hit lag, straight from Captain Fetch's jowls INT32 hitlag; // Sal-style hit lag, straight from Captain Fetch's jowls
UINT8 waterskip; // Water skipping counter
INT32 dispoffset; INT32 dispoffset;
@ -499,7 +500,9 @@ void P_RunCachedActions(void);
void P_AddCachedAction(mobj_t *mobj, INT32 statenum); void P_AddCachedAction(mobj_t *mobj, INT32 statenum);
// kartitem stuff: Returns true if the specified 'type' is one of the kart item constants we want in the kitemcap list // kartitem stuff: Returns true if the specified 'type' is one of the kart item constants we want in the kitemcap list
boolean P_IsKartFieldItem(INT32 type);
boolean P_IsKartItem(INT32 type); boolean P_IsKartItem(INT32 type);
boolean P_CanDeleteKartItem(INT32 type);
void P_AddKartItem(mobj_t *thing); // needs to be called in k_kart.c void P_AddKartItem(mobj_t *thing); // needs to be called in k_kart.c
void P_RunKartItems(void); void P_RunKartItems(void);

View file

@ -280,7 +280,6 @@ static void P_NetArchivePlayers(void)
WRITEINT32(save_p, players[i].underwatertilt); WRITEINT32(save_p, players[i].underwatertilt);
WRITEFIXED(save_p, players[i].offroad); WRITEFIXED(save_p, players[i].offroad);
WRITEUINT8(save_p, players[i].waterskip);
WRITEUINT16(save_p, players[i].tiregrease); WRITEUINT16(save_p, players[i].tiregrease);
WRITEUINT16(save_p, players[i].springstars); WRITEUINT16(save_p, players[i].springstars);
@ -379,6 +378,8 @@ static void P_NetArchivePlayers(void)
WRITEUINT8(save_p, players[i].kickstartaccel); WRITEUINT8(save_p, players[i].kickstartaccel);
WRITEUINT8(save_p, players[i].stairjank); WRITEUINT8(save_p, players[i].stairjank);
WRITEUINT8(save_p, players[i].topdriftheld);
WRITEUINT8(save_p, players[i].topinfirst);
WRITEUINT8(save_p, players[i].shrinkLaserDelay); WRITEUINT8(save_p, players[i].shrinkLaserDelay);
@ -394,6 +395,7 @@ static void P_NetArchivePlayers(void)
WRITEUINT32(save_p, players[i].respawn.distanceleft); WRITEUINT32(save_p, players[i].respawn.distanceleft);
WRITEUINT32(save_p, players[i].respawn.dropdash); WRITEUINT32(save_p, players[i].respawn.dropdash);
WRITEUINT8(save_p, players[i].respawn.truedeath); WRITEUINT8(save_p, players[i].respawn.truedeath);
WRITEUINT8(save_p, players[i].respawn.manual);
// botvars_t // botvars_t
WRITEUINT8(save_p, players[i].botvars.difficulty); WRITEUINT8(save_p, players[i].botvars.difficulty);
@ -577,7 +579,6 @@ static void P_NetUnArchivePlayers(void)
players[i].underwatertilt = READINT32(save_p); players[i].underwatertilt = READINT32(save_p);
players[i].offroad = READFIXED(save_p); players[i].offroad = READFIXED(save_p);
players[i].waterskip = READUINT8(save_p);
players[i].tiregrease = READUINT16(save_p); players[i].tiregrease = READUINT16(save_p);
players[i].springstars = READUINT16(save_p); players[i].springstars = READUINT16(save_p);
@ -676,6 +677,8 @@ static void P_NetUnArchivePlayers(void)
players[i].kickstartaccel = READUINT8(save_p); players[i].kickstartaccel = READUINT8(save_p);
players[i].stairjank = READUINT8(save_p); players[i].stairjank = READUINT8(save_p);
players[i].topdriftheld = READUINT8(save_p);
players[i].topinfirst = READUINT8(save_p);
players[i].shrinkLaserDelay = READUINT8(save_p); players[i].shrinkLaserDelay = READUINT8(save_p);
@ -691,6 +694,7 @@ static void P_NetUnArchivePlayers(void)
players[i].respawn.distanceleft = READUINT32(save_p); players[i].respawn.distanceleft = READUINT32(save_p);
players[i].respawn.dropdash = READUINT32(save_p); players[i].respawn.dropdash = READUINT32(save_p);
players[i].respawn.truedeath = READUINT8(save_p); players[i].respawn.truedeath = READUINT8(save_p);
players[i].respawn.manual = READUINT8(save_p);
// botvars_t // botvars_t
players[i].botvars.difficulty = READUINT8(save_p); players[i].botvars.difficulty = READUINT8(save_p);
@ -1638,6 +1642,7 @@ typedef enum
MD2_ITNEXT = 1<<27, MD2_ITNEXT = 1<<27,
MD2_LASTMOMZ = 1<<28, MD2_LASTMOMZ = 1<<28,
MD2_TERRAIN = 1<<29, MD2_TERRAIN = 1<<29,
MD2_WATERSKIP = 1<<30,
} mobj_diff2_t; } mobj_diff2_t;
typedef enum typedef enum
@ -1872,6 +1877,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
} }
if (mobj->hitlag) if (mobj->hitlag)
diff2 |= MD2_HITLAG; diff2 |= MD2_HITLAG;
if (mobj->waterskip)
diff2 |= MD2_WATERSKIP;
if (mobj->dispoffset) if (mobj->dispoffset)
diff2 |= MD2_DISPOFFSET; diff2 |= MD2_DISPOFFSET;
if (mobj == waypointcap) if (mobj == waypointcap)
@ -2082,6 +2089,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
{ {
WRITEINT32(save_p, mobj->hitlag); WRITEINT32(save_p, mobj->hitlag);
} }
if (diff2 & MD2_WATERSKIP)
{
WRITEUINT8(save_p, mobj->waterskip);
}
if (diff2 & MD2_DISPOFFSET) if (diff2 & MD2_DISPOFFSET)
{ {
WRITEINT32(save_p, mobj->dispoffset); WRITEINT32(save_p, mobj->dispoffset);
@ -3191,6 +3202,10 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
{ {
mobj->hitlag = READINT32(save_p); mobj->hitlag = READINT32(save_p);
} }
if (diff2 & MD2_WATERSKIP)
{
mobj->waterskip = READUINT8(save_p);
}
if (diff2 & MD2_DISPOFFSET) if (diff2 & MD2_DISPOFFSET)
{ {
mobj->dispoffset = READINT32(save_p); mobj->dispoffset = READINT32(save_p);
@ -4600,6 +4615,8 @@ static void P_NetArchiveMisc(boolean resending)
WRITEINT16(save_p, task->timer); WRITEINT16(save_p, task->timer);
WRITESTRING(save_p, task->command); WRITESTRING(save_p, task->command);
} }
WRITEUINT32(save_p, cht_debug);
} }
static inline boolean P_NetUnArchiveMisc(boolean reloading) static inline boolean P_NetUnArchiveMisc(boolean reloading)
@ -4764,6 +4781,8 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading)
Schedule_Add(basetime, timer, command); Schedule_Add(basetime, timer, command);
} }
cht_debug = READUINT32(save_p);
return true; return true;
} }

View file

@ -2836,16 +2836,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
EV_DoCrush(line, crushBothOnce); EV_DoCrush(line, crushBothOnce);
break; break;
case 432: // Enable 2D Mode (Disable if noclimb)
if (mo && mo->player)
{
if (line->flags & ML_NOCLIMB)
mo->flags2 &= ~MF2_TWOD;
else
mo->flags2 |= MF2_TWOD;
}
break;
case 433: // Flip gravity (Flop gravity if noclimb) Works on pushables, too! case 433: // Flip gravity (Flop gravity if noclimb) Works on pushables, too!
if (line->flags & ML_NOCLIMB) if (line->flags & ML_NOCLIMB)
mo->flags2 &= ~MF2_OBJECTFLIP; mo->flags2 &= ~MF2_OBJECTFLIP;
@ -4426,7 +4416,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
switch (special) switch (special)
{ {
case 1: // Damage (Generic) case 1: // Damage (Generic)
if (roversector || P_MobjReadyToTrigger(player->mo, sector)) if (!K_IsRidingFloatingTop(player) && (roversector || P_MobjReadyToTrigger(player->mo, sector)))
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_NORMAL); P_DamageMobj(player->mo, NULL, NULL, 1, DMG_NORMAL);
break; break;
case 2: // Damage (Water) // SRB2kart - These three damage types are now offroad sectors case 2: // Damage (Water) // SRB2kart - These three damage types are now offroad sectors
@ -4434,7 +4424,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
case 4: // Damage (Electrical) case 4: // Damage (Electrical)
break; break;
case 5: // Spikes case 5: // Spikes
if (roversector || P_MobjReadyToTrigger(player->mo, sector)) if (!K_IsRidingFloatingTop(player) && (roversector || P_MobjReadyToTrigger(player->mo, sector)))
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_NORMAL); P_DamageMobj(player->mo, NULL, NULL, 1, DMG_NORMAL);
break; break;
case 6: // Death Pit (Camera Mod) case 6: // Death Pit (Camera Mod)

View file

@ -35,7 +35,7 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle,
INT32 starpostnum, tic_t starposttime, angle_t starpostangle, INT32 starpostnum, tic_t starposttime, angle_t starpostangle,
fixed_t starpostscale, angle_t drawangle, INT32 flags2) fixed_t starpostscale, angle_t drawangle, INT32 flags2)
{ {
const INT32 takeflags2 = MF2_TWOD|MF2_OBJECTFLIP; const INT32 takeflags2 = MF2_OBJECTFLIP;
UINT8 i; UINT8 i;
(void)starposttime; (void)starposttime;

View file

@ -44,6 +44,14 @@ INT32 P_AltFlip(INT32 n, tic_t tics)
return leveltime % (2 * tics) < tics ? n : -(n); return leveltime % (2 * tics) < tics ? n : -(n);
} }
// Please read p_tick.h
INT32 P_LerpFlip(INT32 n, tic_t tics)
{
const tic_t w = 2 * tics;
return P_AltFlip(((leveltime % w) - tics) * n, w);
}
// //
// THINKERS // THINKERS
// All thinkers should be allocated by Z_Calloc // All thinkers should be allocated by Z_Calloc

View file

@ -35,4 +35,12 @@ mobj_t *P_SetTarget(mobj_t **mo, mobj_t *target); // killough 11/98
INT32 P_AltFlip(INT32 value, tic_t tics); INT32 P_AltFlip(INT32 value, tic_t tics);
#define P_RandomFlip(value) P_AltFlip(value, 1) #define P_RandomFlip(value) P_AltFlip(value, 1)
// Multiply value back and forth between -(tics) and +(tics).
// Example output P_ModulateFlip(2, 2):
// Tic: 0 1 2 3 4 5 6 7 8
// Val: -4 -2 0 2 4 2 0 -2 -4
// A half cycle (one direction) takes 2 * tics.
// A full cycle takes 4 * tics.
INT32 P_LerpFlip(INT32 value, tic_t tics);
#endif #endif

View file

@ -1801,7 +1801,7 @@ static void P_3dMovement(player_t *player)
// Get the old momentum; this will be needed at the end of the function! -SH // Get the old momentum; this will be needed at the end of the function! -SH
oldMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0); oldMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0);
if (player->stairjank > 8 && leveltime & 3) if ((player->stairjank > 8 && leveltime & 3) || K_IsRidingFloatingTop(player))
{ {
movepushangle = K_MomentumAngle(player->mo); movepushangle = K_MomentumAngle(player->mo);
} }
@ -1884,6 +1884,7 @@ static void P_3dMovement(player_t *player)
if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration... if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration...
movepushforward = FixedMul(movepushforward, player->mo->movefactor); movepushforward = FixedMul(movepushforward, player->mo->movefactor);
if (player->curshield != KSHIELD_TOP)
{ {
INT32 a = K_GetUnderwaterTurnAdjust(player); INT32 a = K_GetUnderwaterTurnAdjust(player);
INT32 adj = 0; INT32 adj = 0;
@ -1967,6 +1968,24 @@ static void P_3dMovement(player_t *player)
player->mo->momx += totalthrust.x; player->mo->momx += totalthrust.x;
player->mo->momy += totalthrust.y; player->mo->momy += totalthrust.y;
// Releasing a drift while on the Top translates all your
// momentum (and even then some) into whichever direction
// you're facing
if (onground && player->curshield == KSHIELD_TOP && (K_GetKartButtons(player) & BT_DRIFT) != BT_DRIFT && (player->oldcmd.buttons & BT_DRIFT))
{
const fixed_t gmin = FRACUNIT/4;
const fixed_t gmax = 5*FRACUNIT/2;
const fixed_t grindfactor = (gmax - gmin) / GARDENTOP_MAXGRINDTIME;
const fixed_t grindscale = gmin + (player->topdriftheld * grindfactor);
const fixed_t speed = R_PointToDist2(0, 0, player->mo->momx, player->mo->momy);
P_InstaThrust(player->mo, player->mo->angle, FixedMul(speed, grindscale));
player->topdriftheld = 0;/* reset after release */
}
if (!onground) if (!onground)
{ {
const fixed_t airspeedcap = (50*mapobjectscale); const fixed_t airspeedcap = (50*mapobjectscale);
@ -2292,100 +2311,6 @@ void P_MovePlayer(player_t *player)
//GAMEPLAY STUFF// //GAMEPLAY STUFF//
////////////////// //////////////////
if (((!(player->mo->eflags & MFE_VERTICALFLIP) && player->mo->z + player->mo->height >= player->mo->watertop && player->mo->z <= player->mo->watertop)
|| (player->mo->eflags & MFE_VERTICALFLIP && player->mo->z + player->mo->height >= player->mo->waterbottom && player->mo->z <= player->mo->waterbottom))
&& (player->speed > runspd)
&& player->mo->momz == 0 && player->carry != CR_SLIDING && !player->spectator)
{
fixed_t playerTopSpeed = K_GetKartSpeed(player, false, false);
fixed_t trailScale = FixedMul(FixedDiv(player->speed - runspd, playerTopSpeed - runspd), mapobjectscale);
if (playerTopSpeed > runspd)
trailScale = FixedMul(FixedDiv(player->speed - runspd, playerTopSpeed - runspd), mapobjectscale);
else
trailScale = mapobjectscale; // Scaling is based off difference between runspeed and top speed
if (trailScale > 0)
{
const angle_t forwardangle = K_MomentumAngle(player->mo);
const fixed_t playerVisualRadius = player->mo->radius + (8 * player->mo->scale);
const size_t numFrames = S_WATERTRAIL8 - S_WATERTRAIL1;
const statenum_t curOverlayFrame = S_WATERTRAIL1 + (leveltime % numFrames);
const statenum_t curUnderlayFrame = S_WATERTRAILUNDERLAY1 + (leveltime % numFrames);
fixed_t x1, x2, y1, y2;
mobj_t *water;
x1 = player->mo->x + player->mo->momx + P_ReturnThrustX(player->mo, forwardangle + ANGLE_90, playerVisualRadius);
y1 = player->mo->y + player->mo->momy + P_ReturnThrustY(player->mo, forwardangle + ANGLE_90, playerVisualRadius);
x1 = x1 + P_ReturnThrustX(player->mo, forwardangle, playerVisualRadius);
y1 = y1 + P_ReturnThrustY(player->mo, forwardangle, playerVisualRadius);
x2 = player->mo->x + player->mo->momx + P_ReturnThrustX(player->mo, forwardangle - ANGLE_90, playerVisualRadius);
y2 = player->mo->y + player->mo->momy + P_ReturnThrustY(player->mo, forwardangle - ANGLE_90, playerVisualRadius);
x2 = x2 + P_ReturnThrustX(player->mo, forwardangle, playerVisualRadius);
y2 = y2 + P_ReturnThrustY(player->mo, forwardangle, playerVisualRadius);
// Left
// underlay
water = P_SpawnMobj(x1, y1,
((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAILUNDERLAY].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAILUNDERLAY);
P_InitAngle(water, forwardangle - ANGLE_180 - ANGLE_22h);
water->destscale = trailScale;
water->momx = player->mo->momx;
water->momy = player->mo->momy;
water->momz = player->mo->momz;
P_SetScale(water, trailScale);
P_SetMobjState(water, curUnderlayFrame);
// overlay
water = P_SpawnMobj(x1, y1,
((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAIL].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAIL);
P_InitAngle(water, forwardangle - ANGLE_180 - ANGLE_22h);
water->destscale = trailScale;
water->momx = player->mo->momx;
water->momy = player->mo->momy;
water->momz = player->mo->momz;
P_SetScale(water, trailScale);
P_SetMobjState(water, curOverlayFrame);
// Right
// Underlay
water = P_SpawnMobj(x2, y2,
((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAILUNDERLAY].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAILUNDERLAY);
P_InitAngle(water, forwardangle - ANGLE_180 + ANGLE_22h);
water->destscale = trailScale;
water->momx = player->mo->momx;
water->momy = player->mo->momy;
water->momz = player->mo->momz;
P_SetScale(water, trailScale);
P_SetMobjState(water, curUnderlayFrame);
// Overlay
water = P_SpawnMobj(x2, y2,
((player->mo->eflags & MFE_VERTICALFLIP) ? player->mo->waterbottom - FixedMul(mobjinfo[MT_WATERTRAIL].height, player->mo->scale) : player->mo->watertop), MT_WATERTRAIL);
P_InitAngle(water, forwardangle - ANGLE_180 + ANGLE_22h);
water->destscale = trailScale;
water->momx = player->mo->momx;
water->momy = player->mo->momy;
water->momz = player->mo->momz;
P_SetScale(water, trailScale);
P_SetMobjState(water, curOverlayFrame);
if (!S_SoundPlaying(player->mo, sfx_s3kdbs))
{
const INT32 volume = (min(trailScale, FRACUNIT) * 255) / FRACUNIT;
S_StartSoundAtVolume(player->mo, sfx_s3kdbs, volume);
}
}
}
// Little water sound while touching water - just a nicety.
if ((player->mo->eflags & MFE_TOUCHWATER) && !(player->mo->eflags & MFE_UNDERWATER) && !player->spectator)
{
if (P_RandomChance(PR_BUBBLE, FRACUNIT/2) && leveltime % TICRATE == 0)
S_StartSound(player->mo, sfx_floush);
}
//////////////////////////// ////////////////////////////
//SPINNING AND SPINDASHING// //SPINNING AND SPINDASHING//
//////////////////////////// ////////////////////////////
@ -3068,10 +2993,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
mobj_t *mo; mobj_t *mo;
fixed_t f1, f2; fixed_t f1, f2;
fixed_t speed; fixed_t speed;
#ifndef NOCLIPCAM
boolean cameranoclip;
subsector_t *newsubsec;
#endif
fixed_t playerScale = FixedDiv(player->mo->scale, mapobjectscale); fixed_t playerScale = FixedDiv(player->mo->scale, mapobjectscale);
fixed_t scaleDiff = playerScale - FRACUNIT; fixed_t scaleDiff = playerScale - FRACUNIT;
@ -3129,12 +3050,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
return true; return true;
} }
#ifndef NOCLIPCAM
cameranoclip = ((player->cheats & PC_NOCLIP)
|| (mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)) // Noclipping player camera noclips too!!
|| (leveltime < introtime)); // Kart intro cam
#endif
if ((player->pflags & PF_NOCONTEST) && (gametyperules & GTR_CIRCUIT)) // 1 for momentum keep, 2 for turnaround if ((player->pflags & PF_NOCONTEST) && (gametyperules & GTR_CIRCUIT)) // 1 for momentum keep, 2 for turnaround
timeover = (player->karthud[khud_timeovercam] > 2*TICRATE ? 2 : 1); timeover = (player->karthud[khud_timeovercam] > 2*TICRATE ? 2 : 1);
else else
@ -3263,6 +3178,13 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
} }
} }
/* The Top is Big Large so zoom out */
if (player->curshield == KSHIELD_TOP)
{
camdist += 40 * mapobjectscale;
camheight += 40 * mapobjectscale;
}
if (!resetcalled && (leveltime >= introtime && timeover != 2) if (!resetcalled && (leveltime >= introtime && timeover != 2)
&& (t_cam_rotate[num] != -42)) && (t_cam_rotate[num] != -42))
{ {
@ -3377,204 +3299,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
z = mo->z + pviewheight + distz; z = mo->z + pviewheight + distz;
} }
#ifndef NOCLIPCAM // Disable all z-clipping for noclip cam
// move camera down to move under lower ceilings
newsubsec = R_PointInSubsectorOrNull(((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1), ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1));
if (!newsubsec)
newsubsec = thiscam->subsector;
if (newsubsec)
{
fixed_t myfloorz, myceilingz;
fixed_t midz = thiscam->z + (thiscam->z - mo->z)/2;
fixed_t midx = ((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1);
fixed_t midy = ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1);
// Cameras use the heightsec's heights rather then the actual sector heights.
// If you can see through it, why not move the camera through it too?
if (newsubsec->sector->camsec >= 0)
{
myfloorz = sectors[newsubsec->sector->camsec].floorheight;
myceilingz = sectors[newsubsec->sector->camsec].ceilingheight;
}
else if (newsubsec->sector->heightsec >= 0)
{
myfloorz = sectors[newsubsec->sector->heightsec].floorheight;
myceilingz = sectors[newsubsec->sector->heightsec].ceilingheight;
}
else
{
myfloorz = P_CameraGetFloorZ(thiscam, newsubsec->sector, midx, midy, NULL);
myceilingz = P_CameraGetCeilingZ(thiscam, newsubsec->sector, midx, midy, NULL);
}
// Check list of fake floors and see if floorz/ceilingz need to be altered.
if (newsubsec->sector->ffloors)
{
ffloor_t *rover;
fixed_t delta1, delta2;
INT32 thingtop = midz + thiscam->height;
for (rover = newsubsec->sector->ffloors; rover; rover = rover->next)
{
fixed_t topheight, bottomheight;
if (!(rover->flags & FF_BLOCKOTHERS) || !(rover->flags & FF_EXISTS) || !(rover->flags & FF_RENDERALL) || GETSECSPECIAL(rover->master->frontsector->special, 4) == 12)
continue;
topheight = P_CameraGetFOFTopZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
bottomheight = P_CameraGetFOFBottomZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
delta1 = midz - (bottomheight
+ ((topheight - bottomheight)/2));
delta2 = thingtop - (bottomheight
+ ((topheight - bottomheight)/2));
if (topheight > myfloorz && abs(delta1) < abs(delta2))
myfloorz = topheight;
if (bottomheight < myceilingz && abs(delta1) >= abs(delta2))
myceilingz = bottomheight;
}
}
// Check polyobjects and see if floorz/ceilingz need to be altered
{
INT32 xl, xh, yl, yh, bx, by;
validcount++;
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
BMBOUNDFIX(xl, xh, yl, yh);
for (by = yl; by <= yh; by++)
for (bx = xl; bx <= xh; bx++)
{
INT32 offset;
polymaplink_t *plink; // haleyjd 02/22/06
if (bx < 0 || by < 0 || bx >= bmapwidth || by >= bmapheight)
continue;
offset = by*bmapwidth + bx;
// haleyjd 02/22/06: consider polyobject lines
plink = polyblocklinks[offset];
while (plink)
{
polyobj_t *po = plink->po;
if (po->validcount != validcount) // if polyobj hasn't been checked
{
sector_t *polysec;
fixed_t delta1, delta2, thingtop;
fixed_t polytop, polybottom;
po->validcount = validcount;
if (!P_PointInsidePolyobj(po, x, y) || !(po->flags & POF_SOLID))
{
plink = (polymaplink_t *)(plink->link.next);
continue;
}
// We're inside it! Yess...
polysec = po->lines[0]->backsector;
if (GETSECSPECIAL(polysec->special, 4) == 12)
{ // Camera noclip polyobj.
plink = (polymaplink_t *)(plink->link.next);
continue;
}
if (po->flags & POF_CLIPPLANES)
{
polytop = polysec->ceilingheight;
polybottom = polysec->floorheight;
}
else
{
polytop = INT32_MAX;
polybottom = INT32_MIN;
}
thingtop = midz + thiscam->height;
delta1 = midz - (polybottom + ((polytop - polybottom)/2));
delta2 = thingtop - (polybottom + ((polytop - polybottom)/2));
if (polytop > myfloorz && abs(delta1) < abs(delta2))
myfloorz = polytop;
if (polybottom < myceilingz && abs(delta1) >= abs(delta2))
myceilingz = polybottom;
}
plink = (polymaplink_t *)(plink->link.next);
}
}
}
// crushed camera
if (myceilingz <= myfloorz + thiscam->height && !resetcalled && !cameranoclip)
{
P_ResetCamera(player, thiscam);
return true;
}
// camera fit?
if (myceilingz != myfloorz
&& myceilingz - thiscam->height < z)
{
/* // no fit
if (!resetcalled && !cameranoclip)
{
P_ResetCamera(player, thiscam);
return true;
}
*/
z = myceilingz - thiscam->height-FixedMul(11*FRACUNIT, mo->scale);
// is the camera fit is there own sector
}
// Make the camera a tad smarter with 3d floors
if (newsubsec->sector->ffloors && !cameranoclip)
{
ffloor_t *rover;
for (rover = newsubsec->sector->ffloors; rover; rover = rover->next)
{
fixed_t topheight, bottomheight;
if ((rover->flags & FF_BLOCKOTHERS) && (rover->flags & FF_RENDERALL) && (rover->flags & FF_EXISTS) && GETSECSPECIAL(rover->master->frontsector->special, 4) == 12)
{
topheight = P_CameraGetFOFTopZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
bottomheight = P_CameraGetFOFBottomZ(thiscam, newsubsec->sector, rover, midx, midy, NULL);
if (bottomheight - thiscam->height < z
&& midz < bottomheight)
z = bottomheight - thiscam->height-FixedMul(11*FRACUNIT, mo->scale);
else if (topheight + thiscam->height > z
&& midz > topheight)
z = topheight;
if ((mo->z >= topheight && midz < bottomheight)
|| ((mo->z < bottomheight && mo->z+mo->height < topheight) && midz >= topheight))
{
// Can't see
if (!resetcalled)
P_ResetCamera(player, thiscam);
return true;
}
}
}
}
}
if (thiscam->z < thiscam->floorz && !cameranoclip)
thiscam->z = thiscam->floorz;
#endif // NOCLIPCAM
// point viewed by the camera // point viewed by the camera
// this point is just 64 unit forward the player // this point is just 64 unit forward the player
dist = 64*cameraScale; dist = 64*cameraScale;
@ -4660,3 +4384,28 @@ boolean P_PlayerFullbright(player_t *player)
{ {
return (player->invincibilitytimer > 0); return (player->invincibilitytimer > 0);
} }
void P_ResetPlayerCheats(void)
{
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
{
player_t *player = &players[i];
mobj_t *thing = player->mo;
if (!playeringame[i])
continue;
player->pflags &= ~(PF_GODMODE);
player->respawn.manual = false;
if (P_MobjWasRemoved(thing))
continue;
thing->flags &= ~(MF_NOCLIP);
thing->destscale = mapobjectscale;
P_SetScale(thing, thing->destscale);
}
}

View file

@ -915,39 +915,9 @@ void R_ApplyViewMorph(int s)
end = width * height; end = width * height;
#if 0 for (p = 0; p < end; p++)
if (cv_debug & DBG_VIEWMORPH)
{ {
UINT8 border = 32; tmpscr[p] = srcscr[viewmorph[s].scrmap[p]];
UINT8 grid = 160;
INT32 ws = vid.width / 4;
INT32 hs = vid.width * (vid.height / 4);
memcpy(tmpscr, srcscr, vid.width*vid.height);
for (p = 0; p < vid.width; p++)
{
tmpscr[viewmorph.scrmap[p]] = border;
tmpscr[viewmorph.scrmap[p + hs]] = grid;
tmpscr[viewmorph.scrmap[p + hs*2]] = grid;
tmpscr[viewmorph.scrmap[p + hs*3]] = grid;
tmpscr[viewmorph.scrmap[end - 1 - p]] = border;
}
for (p = vid.width; p < end; p += vid.width)
{
tmpscr[viewmorph.scrmap[p]] = border;
tmpscr[viewmorph.scrmap[p + ws]] = grid;
tmpscr[viewmorph.scrmap[p + ws*2]] = grid;
tmpscr[viewmorph.scrmap[p + ws*3]] = grid;
tmpscr[viewmorph.scrmap[end - 1 - p]] = border;
}
}
else
#endif
{
for (p = 0; p < end; p++)
{
tmpscr[p] = srcscr[viewmorph[s].scrmap[p]];
}
} }
VID_BlitLinearScreen(tmpscr, srcscr, VID_BlitLinearScreen(tmpscr, srcscr,

View file

@ -42,6 +42,8 @@ static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer)
angle_t rollAngle = 0; angle_t rollAngle = 0;
mobj_t *top = K_GetGardenTop(player);
if (player->mo->eflags & MFE_UNDERWATER) if (player->mo->eflags & MFE_UNDERWATER)
{ {
rollAngle -= player->underwatertilt; rollAngle -= player->underwatertilt;
@ -61,6 +63,14 @@ static angle_t R_PlayerSpriteRotation(player_t *player, player_t *viewPlayer)
(17 / player->stairjank)); (17 / player->stairjank));
} }
if (top)
{
/* FIXME: why does it not look right at more acute
angles without this? There's a related hack to
spritexoffset in K_KartPlayerThink. */
rollAngle += 3 * (INT32)top->rollangle / 2;
}
return rollAngle; return rollAngle;
} }

View file

@ -364,7 +364,7 @@ static void ST_drawDebugInfo(void)
if (!stplyr->mo) if (!stplyr->mo)
return; return;
if (cv_debug & DBG_BASIC) if (cht_debug & DBG_BASIC)
{ {
const fixed_t d = AngleFixed(stplyr->mo->angle); const fixed_t d = AngleFixed(stplyr->mo->angle);
V_DrawRightAlignedString(320, 168, V_MONOSPACE, va("X: %6d", stplyr->mo->x>>FRACBITS)); V_DrawRightAlignedString(320, 168, V_MONOSPACE, va("X: %6d", stplyr->mo->x>>FRACBITS));
@ -375,7 +375,7 @@ static void ST_drawDebugInfo(void)
height = 152; height = 152;
} }
if (cv_debug & DBG_DETAILED) if (cht_debug & DBG_DETAILED)
{ {
//V_DrawRightAlignedString(320, height - 104, V_MONOSPACE, va("SHIELD: %5x", stplyr->powers[pw_shield])); //V_DrawRightAlignedString(320, height - 104, V_MONOSPACE, va("SHIELD: %5x", stplyr->powers[pw_shield]));
V_DrawRightAlignedString(320, height - 96, V_MONOSPACE, va("SCALE: %5d%%", (stplyr->mo->scale*100)/FRACUNIT)); V_DrawRightAlignedString(320, height - 96, V_MONOSPACE, va("SCALE: %5d%%", (stplyr->mo->scale*100)/FRACUNIT));
@ -404,7 +404,7 @@ static void ST_drawDebugInfo(void)
height -= 120; height -= 120;
} }
if (cv_debug & DBG_RANDOMIZER) // randomizer testing if (cht_debug & DBG_RNG) // randomizer testing
{ {
// TODO: this only accounts for the undefined class, // TODO: this only accounts for the undefined class,
// which should be phased out as much as possible anyway. // which should be phased out as much as possible anyway.
@ -421,7 +421,7 @@ static void ST_drawDebugInfo(void)
height -= 32; height -= 32;
} }
if (cv_debug & DBG_MEMORY) if (cht_debug & DBG_MEMORY)
V_DrawRightAlignedString(320, height, V_MONOSPACE, va("Heap used: %7sKB", sizeu1(Z_TagsUsage(0, INT32_MAX)>>10))); V_DrawRightAlignedString(320, height, V_MONOSPACE, va("Heap used: %7sKB", sizeu1(Z_TagsUsage(0, INT32_MAX)>>10)));
} }

View file

@ -36,6 +36,7 @@
// SRB2Kart // SRB2Kart
#include "k_hud.h" #include "k_hud.h"
#include "i_time.h"
// Each screen is [vid.width*vid.height]; // Each screen is [vid.width*vid.height];
UINT8 *screens[5]; UINT8 *screens[5];
@ -1636,6 +1637,14 @@ UINT8 *V_GetStringColormap(INT32 colorflags)
#endif #endif
} }
INT32 V_DanceYOffset(INT32 counter)
{
const INT32 duration = 16;
const INT32 step = (I_GetTime() + counter) % duration;
return abs(step - (duration / 2)) - (duration / 4);
}
// Writes a single character (draw WHITE if bit 7 set) // Writes a single character (draw WHITE if bit 7 set)
// //
void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed) void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed)
@ -2044,9 +2053,13 @@ void V_DrawStringScaled(
boolean uppercase; boolean uppercase;
boolean notcolored; boolean notcolored;
boolean dance;
boolean nodanceoverride;
INT32 dancecounter;
fixed_t cx, cy; fixed_t cx, cy;
fixed_t cxoff; fixed_t cxoff, cyoff;
fixed_t cw; fixed_t cw;
INT32 spacing; INT32 spacing;
@ -2057,6 +2070,14 @@ void V_DrawStringScaled(
uppercase = !( flags & V_ALLOWLOWERCASE ); uppercase = !( flags & V_ALLOWLOWERCASE );
flags &= ~(V_FLIP);/* These two (V_ALLOWLOWERCASE) share a bit. */ flags &= ~(V_FLIP);/* These two (V_ALLOWLOWERCASE) share a bit. */
dance = (flags & V_STRINGDANCE) != 0;
nodanceoverride = !dance;
dancecounter = 0;
/* Some of these flags get overloaded in this function so
don't pass them on. */
flags &= ~(V_PARAMMASK);
if (colormap == NULL) if (colormap == NULL)
{ {
colormap = V_GetStringColormap(( flags & V_CHARCOLORMASK )); colormap = V_GetStringColormap(( flags & V_CHARCOLORMASK ));
@ -2247,8 +2268,9 @@ void V_DrawStringScaled(
cx = x; cx = x;
cy = y; cy = y;
cyoff = 0;
for (; ( c = *s ); ++s) for (; ( c = *s ); ++s, ++dancecounter)
{ {
switch (c) switch (c)
{ {
@ -2267,18 +2289,29 @@ void V_DrawStringScaled(
( ( c & 0x7f )<< V_CHARCOLORSHIFT )& ( ( c & 0x7f )<< V_CHARCOLORSHIFT )&
V_CHARCOLORMASK); V_CHARCOLORMASK);
} }
if (nodanceoverride)
{
dance = false;
}
}
else if (c == V_STRINGDANCE)
{
dance = true;
} }
else if (cx < right) else if (cx < right)
{ {
if (uppercase) if (uppercase)
c = toupper(c); c = toupper(c);
if (dance)
cyoff = V_DanceYOffset(dancecounter) * FRACUNIT;
c -= font->start; c -= font->start;
if (c >= 0 && c < font->size && font->font[c]) if (c >= 0 && c < font->size && font->font[c])
{ {
cw = SHORT (font->font[c]->width) * dupx; cw = SHORT (font->font[c]->width) * dupx;
cxoff = (*dim_fn)(scale, chw, hchw, dupx, &cw); cxoff = (*dim_fn)(scale, chw, hchw, dupx, &cw);
V_DrawFixedPatch(cx + cxoff, cy, scale, V_DrawFixedPatch(cx + cxoff, cy + cyoff, scale,
flags, font->font[c], colormap); flags, font->font[c], colormap);
cx += cw; cx += cw;
} }

View file

@ -84,6 +84,9 @@ void V_CubeApply(RGBA_t *input);
// Bottom 8 bits are used for parameter (screen or character) // Bottom 8 bits are used for parameter (screen or character)
#define V_PARAMMASK 0x000000FF #define V_PARAMMASK 0x000000FF
// strings/characters only
#define V_STRINGDANCE 0x00000002
// flags hacked in scrn (not supported by all functions (see src)) // flags hacked in scrn (not supported by all functions (see src))
// patch scaling uses bits 9 and 10 // patch scaling uses bits 9 and 10
#define V_SCALEPATCHSHIFT 8 #define V_SCALEPATCHSHIFT 8
@ -220,6 +223,8 @@ void V_DrawPromptBack(INT32 boxheight, INT32 color);
#define V__IntegerStringWidth( scale,option,font,string ) \ #define V__IntegerStringWidth( scale,option,font,string ) \
(V_StringScaledWidth(scale,FRACUNIT,FRACUNIT,option,font,string) / FRACUNIT) (V_StringScaledWidth(scale,FRACUNIT,FRACUNIT,option,font,string) / FRACUNIT)
INT32 V_DanceYOffset(INT32 counter);
// draw a single character // draw a single character
void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed); void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed);
// draw a single character, but for the chat // draw a single character, but for the chat