Merge branch 'master' of http://git.magicalgirl.moe/STJr/SRB2Internal.git into more_about_menus

# Conflicts:
#	src/m_menu.c
This commit is contained in:
toasterbabe 2017-09-25 20:35:04 +01:00
commit 295b100df4
45 changed files with 2003 additions and 1003 deletions

View File

@ -271,6 +271,12 @@ void B_RespawnBot(INT32 playernum)
player->powers[pw_spacetime] = sonic->player->powers[pw_spacetime]; player->powers[pw_spacetime] = sonic->player->powers[pw_spacetime];
player->powers[pw_gravityboots] = sonic->player->powers[pw_gravityboots]; player->powers[pw_gravityboots] = sonic->player->powers[pw_gravityboots];
player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol]; player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol];
player->acceleration = sonic->player->acceleration;
player->accelstart = sonic->player->accelstart;
player->thrustfactor = sonic->player->thrustfactor;
player->normalspeed = sonic->player->normalspeed;
player->pflags |= PF_AUTOBRAKE;
player->pflags &= ~PF_DIRECTIONCHAR;
P_TeleportMove(tails, x, y, z); P_TeleportMove(tails, x, y, z);
if (player->charability == CA_FLY) if (player->charability == CA_FLY)

View File

@ -2253,7 +2253,8 @@ static void Command_connect(void)
CONS_Printf(M_GetText( CONS_Printf(M_GetText(
"Connect <serveraddress> (port): connect to a server\n" "Connect <serveraddress> (port): connect to a server\n"
"Connect ANY: connect to the first lan server found\n" "Connect ANY: connect to the first lan server found\n"
"Connect SELF: connect to your own server.\n")); //"Connect SELF: connect to your own server.\n"
));
return; return;
} }
@ -2267,7 +2268,7 @@ static void Command_connect(void)
// we don't request a restart unless the filelist differs // we don't request a restart unless the filelist differs
server = false; server = false;
/*
if (!stricmp(COM_Argv(1), "self")) if (!stricmp(COM_Argv(1), "self"))
{ {
servernode = 0; servernode = 0;
@ -2276,6 +2277,7 @@ static void Command_connect(void)
//SV_SpawnServer(); //SV_SpawnServer();
} }
else else
*/
{ {
// used in menu to connect to a server in the list // used in menu to connect to a server in the list
if (netgame && !stricmp(COM_Argv(1), "node")) if (netgame && !stricmp(COM_Argv(1), "node"))
@ -2299,10 +2301,13 @@ static void Command_connect(void)
if (!stricmp(COM_Argv(1), "any")) if (!stricmp(COM_Argv(1), "any"))
servernode = BROADCASTADDR; servernode = BROADCASTADDR;
else if (I_NetMakeNodewPort && COM_Argc() >= 3)
servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2));
else if (I_NetMakeNodewPort) else if (I_NetMakeNodewPort)
servernode = I_NetMakeNode(COM_Argv(1)); {
if (COM_Argc() >= 3) // address AND port
servernode = I_NetMakeNodewPort(COM_Argv(1), COM_Argv(2));
else // address only, or address:port
servernode = I_NetMakeNode(COM_Argv(1));
}
else else
{ {
CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n")); CONS_Alert(CONS_ERROR, M_GetText("There is no server identification with this network driver\n"));
@ -3097,11 +3102,15 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
secondarydisplayplayer = newplayernum; secondarydisplayplayer = newplayernum;
DEBFILE("spawning me\n"); DEBFILE("spawning me\n");
// Apply player flags as soon as possible! // Apply player flags as soon as possible!
players[newplayernum].pflags &= ~(PF_FLIPCAM|PF_ANALOGMODE); players[newplayernum].pflags &= ~(PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE);
if (cv_flipcam.value) if (cv_flipcam.value)
players[newplayernum].pflags |= PF_FLIPCAM; players[newplayernum].pflags |= PF_FLIPCAM;
if (cv_analog.value) if (cv_analog.value)
players[newplayernum].pflags |= PF_ANALOGMODE; players[newplayernum].pflags |= PF_ANALOGMODE;
if (cv_directionchar.value)
players[newplayernum].pflags |= PF_DIRECTIONCHAR;
if (cv_autobrake.value)
players[newplayernum].pflags |= PF_AUTOBRAKE;
} }
else else
{ {
@ -3110,11 +3119,15 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
if (botingame) if (botingame)
players[newplayernum].bot = 1; players[newplayernum].bot = 1;
// Same goes for player 2 when relevant // Same goes for player 2 when relevant
players[newplayernum].pflags &= ~(PF_FLIPCAM|PF_ANALOGMODE); players[newplayernum].pflags &= ~(PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE);
if (cv_flipcam2.value) if (cv_flipcam2.value)
players[newplayernum].pflags |= PF_FLIPCAM; players[newplayernum].pflags |= PF_FLIPCAM;
if (cv_analog2.value) if (cv_analog2.value)
players[newplayernum].pflags |= PF_ANALOGMODE; players[newplayernum].pflags |= PF_ANALOGMODE;
if (cv_directionchar2.value)
players[newplayernum].pflags |= PF_DIRECTIONCHAR;
if (cv_autobrake2.value)
players[newplayernum].pflags |= PF_AUTOBRAKE;
} }
D_SendPlayerConfig(); D_SendPlayerConfig();
addedtogame = true; addedtogame = true;

View File

@ -301,7 +301,7 @@ static void D_Display(void)
if (rendermode != render_none) if (rendermode != render_none)
{ {
// Fade to black first // Fade to black first
if (gamestate != GS_LEVEL // fades to black on its own timing, always if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)) // fades to black on its own timing, always
&& wipedefs[wipedefindex] != UINT8_MAX) && wipedefs[wipedefindex] != UINT8_MAX)
{ {
F_WipeStartScreen(); F_WipeStartScreen();
@ -317,6 +317,12 @@ static void D_Display(void)
// do buffered drawing // do buffered drawing
switch (gamestate) switch (gamestate)
{ {
case GS_TITLESCREEN:
if (!titlemapinaction) {
F_TitleScreenDrawer();
break;
}
// Intentional fall-through
case GS_LEVEL: case GS_LEVEL:
if (!gametic) if (!gametic)
break; break;
@ -366,10 +372,6 @@ static void D_Display(void)
HU_Drawer(); HU_Drawer();
break; break;
case GS_TITLESCREEN:
F_TitleScreenDrawer();
break;
case GS_WAITINGPLAYERS: case GS_WAITINGPLAYERS:
// The clientconnect drawer is independent... // The clientconnect drawer is independent...
case GS_DEDICATEDSERVER: case GS_DEDICATEDSERVER:
@ -379,9 +381,10 @@ static void D_Display(void)
// clean up border stuff // clean up border stuff
// see if the border needs to be initially drawn // see if the border needs to be initially drawn
if (gamestate == GS_LEVEL) if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))
{ {
// draw the view directly // draw the view directly
if (!automapactive && !dedicated && cv_renderview.value) if (!automapactive && !dedicated && cv_renderview.value)
{ {
if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD) if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
@ -439,9 +442,13 @@ static void D_Display(void)
lastdraw = false; lastdraw = false;
} }
ST_Drawer(); if (gamestate == GS_LEVEL)
{
HU_Drawer(); ST_Drawer();
HU_Drawer();
}
else
F_TitleScreenDrawer();
} }
// change gamma if needed // change gamma if needed
@ -681,6 +688,9 @@ void D_AdvanceDemo(void)
void D_StartTitle(void) void D_StartTitle(void)
{ {
INT32 i; INT32 i;
S_StopMusic();
if (netgame) if (netgame)
{ {
if (gametype == GT_COOP) if (gametype == GT_COOP)
@ -1346,6 +1356,19 @@ void D_SRB2Main(void)
ultimatemode = true; ultimatemode = true;
} }
// rei/miru: bootmap (Idea: starts the game on a predefined map)
if (bootmap && !(M_CheckParm("-warp") && M_IsNextParm()))
{
pstartmap = bootmap;
if (pstartmap < 1 || pstartmap > NUMMAPS)
I_Error("Cannot warp to map %d (out of range)\n", pstartmap);
else
{
autostart = true;
}
}
if (autostart || netgame || M_CheckParm("+connect") || M_CheckParm("-connect")) if (autostart || netgame || M_CheckParm("+connect") || M_CheckParm("-connect"))
{ {
gameaction = ga_nothing; gameaction = ga_nothing;

View File

@ -771,6 +771,12 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_useranalog); CV_RegisterVar(&cv_useranalog);
CV_RegisterVar(&cv_useranalog2); CV_RegisterVar(&cv_useranalog2);
// deez New User eXperiences
CV_RegisterVar(&cv_directionchar);
CV_RegisterVar(&cv_directionchar2);
CV_RegisterVar(&cv_autobrake);
CV_RegisterVar(&cv_autobrake2);
// s_sound.c // s_sound.c
CV_RegisterVar(&cv_soundvolume); CV_RegisterVar(&cv_soundvolume);
CV_RegisterVar(&cv_closedcaptioning); CV_RegisterVar(&cv_closedcaptioning);
@ -1433,6 +1439,10 @@ void SendWeaponPref(void)
buf[0] |= 1; buf[0] |= 1;
if (players[consoleplayer].pflags & PF_ANALOGMODE) if (players[consoleplayer].pflags & PF_ANALOGMODE)
buf[0] |= 2; buf[0] |= 2;
if (players[consoleplayer].pflags & PF_DIRECTIONCHAR)
buf[0] |= 4;
if (players[consoleplayer].pflags & PF_AUTOBRAKE)
buf[0] |= 8;
SendNetXCmd(XD_WEAPONPREF, buf, 1); SendNetXCmd(XD_WEAPONPREF, buf, 1);
} }
@ -1445,6 +1455,10 @@ void SendWeaponPref2(void)
buf[0] |= 1; buf[0] |= 1;
if (players[secondarydisplayplayer].pflags & PF_ANALOGMODE) if (players[secondarydisplayplayer].pflags & PF_ANALOGMODE)
buf[0] |= 2; buf[0] |= 2;
if (players[secondarydisplayplayer].pflags & PF_DIRECTIONCHAR)
buf[0] |= 4;
if (players[secondarydisplayplayer].pflags & PF_AUTOBRAKE)
buf[0] |= 8;
SendNetXCmd2(XD_WEAPONPREF, buf, 1); SendNetXCmd2(XD_WEAPONPREF, buf, 1);
} }
@ -1452,11 +1466,15 @@ static void Got_WeaponPref(UINT8 **cp,INT32 playernum)
{ {
UINT8 prefs = READUINT8(*cp); UINT8 prefs = READUINT8(*cp);
players[playernum].pflags &= ~(PF_FLIPCAM|PF_ANALOGMODE); players[playernum].pflags &= ~(PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE);
if (prefs & 1) if (prefs & 1)
players[playernum].pflags |= PF_FLIPCAM; players[playernum].pflags |= PF_FLIPCAM;
if (prefs & 2) if (prefs & 2)
players[playernum].pflags |= PF_ANALOGMODE; players[playernum].pflags |= PF_ANALOGMODE;
if (prefs & 4)
players[playernum].pflags |= PF_DIRECTIONCHAR;
if (prefs & 8)
players[playernum].pflags |= PF_AUTOBRAKE;
} }
void D_SendPlayerConfig(void) void D_SendPlayerConfig(void)
@ -2578,12 +2596,12 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
{ {
players[playernum].spectator = true; players[playernum].spectator = true;
players[playernum].pflags &= ~PF_TAGIT; players[playernum].pflags &= ~PF_TAGIT;
players[playernum].pflags &= ~PF_TAGGED; players[playernum].pflags &= ~PF_GAMETYPEOVER;
} }
else if (NetPacket.packet.newteam != 3) // .newteam == 1 or 2. else if (NetPacket.packet.newteam != 3) // .newteam == 1 or 2.
{ {
players[playernum].spectator = false; players[playernum].spectator = false;
players[playernum].pflags &= ~PF_TAGGED;//Just in case. players[playernum].pflags &= ~PF_GAMETYPEOVER; //Just in case.
if (NetPacket.packet.newteam == 1) //Make the player IT. if (NetPacket.packet.newteam == 1) //Make the player IT.
players[playernum].pflags |= PF_TAGIT; players[playernum].pflags |= PF_TAGIT;

View File

@ -98,66 +98,58 @@ typedef enum
// //
typedef enum typedef enum
{ {
// Flip camera angle with gravity flip prefrence. // Cvars
PF_FLIPCAM = 1, PF_FLIPCAM = 1, // Flip camera angle with gravity flip prefrence.
PF_ANALOGMODE = 1<<1, // Analog mode?
PF_DIRECTIONCHAR = 1<<2, // Directional character sprites?
PF_AUTOBRAKE = 1<<3, // Autobrake?
// Cheats // Cheats
PF_GODMODE = 1<<1, PF_GODMODE = 1<<4,
PF_NOCLIP = 1<<2, PF_NOCLIP = 1<<5,
PF_INVIS = 1<<3, PF_INVIS = 1<<6,
// True if button down last tic. // True if button down last tic.
PF_ATTACKDOWN = 1<<4, PF_ATTACKDOWN = 1<<7,
PF_USEDOWN = 1<<5, PF_USEDOWN = 1<<8,
PF_JUMPDOWN = 1<<6, PF_JUMPDOWN = 1<<9,
PF_WPNDOWN = 1<<7, PF_WPNDOWN = 1<<10,
// Unmoving states // Unmoving states
PF_STASIS = 1<<8, // Player is not allowed to move PF_STASIS = 1<<11, // Player is not allowed to move
PF_JUMPSTASIS = 1<<9, // and that includes jumping. PF_JUMPSTASIS = 1<<12, // and that includes jumping.
PF_FULLSTASIS = PF_STASIS|PF_JUMPSTASIS, PF_FULLSTASIS = PF_STASIS|PF_JUMPSTASIS,
// Did you get a time-over? // Applying autobrake?
PF_TIMEOVER = 1<<10, PF_APPLYAUTOBRAKE = 1<<13,
// Character action status // Character action status
PF_STARTJUMP = 1<<11, PF_STARTJUMP = 1<<14,
PF_JUMPED = 1<<12, PF_JUMPED = 1<<15,
PF_SPINNING = 1<<13, PF_NOJUMPDAMAGE = 1<<16,
PF_STARTDASH = 1<<14,
PF_THOKKED = 1<<15,
// Are you gliding? PF_SPINNING = 1<<17,
PF_GLIDING = 1<<16, PF_STARTDASH = 1<<18,
PF_THOKKED = 1<<19,
PF_SHIELDABILITY = 1<<20,
PF_GLIDING = 1<<21,
PF_BOUNCING = 1<<22,
// Sliding (usually in water) like Labyrinth/Oil Ocean // Sliding (usually in water) like Labyrinth/Oil Ocean
PF_SLIDING = 1<<17, PF_SLIDING = 1<<23,
// Bouncing // NiGHTS stuff
PF_BOUNCING = 1<<18, PF_TRANSFERTOCLOSEST = 1<<24,
PF_DRILLING = 1<<25,
/*** NIGHTS STUFF ***/ // Gametype-specific stuff
PF_TRANSFERTOCLOSEST = 1<<19, PF_GAMETYPEOVER = 1<<26, // Race time over, or H&S out-of-game
PF_NIGHTSFALL = 1<<20, PF_TAGIT = 1<<27, // The player is it! For Tag Mode
PF_DRILLING = 1<<21,
PF_SKIDDOWN = 1<<22,
/*** TAG STUFF ***/
PF_TAGGED = 1<<23, // Player has been tagged and awaits the next round in hide and seek.
PF_TAGIT = 1<<24, // The player is it! For Tag Mode
/*** misc ***/ /*** misc ***/
PF_FORCESTRAFE = 1<<25, // Turning inputs are translated into strafing inputs PF_FORCESTRAFE = 1<<28, // Turning inputs are translated into strafing inputs
PF_ANALOGMODE = 1<<26, // Analog mode? PF_CANCARRY = 1<<29, // Can carry another player?
// Can carry another player?
PF_CANCARRY = 1<<27,
// Used shield ability
PF_SHIELDABILITY = 1<<28,
// Jump damage?
PF_NOJUMPDAMAGE = 1<<29,
// up to 1<<31 is free // up to 1<<31 is free
} pflags_t; } pflags_t;
@ -234,6 +226,7 @@ typedef enum
CR_PLAYER, CR_PLAYER,
// NiGHTS mode. Not technically a CARRYING, but doesn't stack with any of the others, so might as well go here. // NiGHTS mode. Not technically a CARRYING, but doesn't stack with any of the others, so might as well go here.
CR_NIGHTSMODE, CR_NIGHTSMODE,
CR_NIGHTSFALL,
// Old Brak sucks hard, but this gimmick could be used for something better, so we might as well continue supporting it. // Old Brak sucks hard, but this gimmick could be used for something better, so we might as well continue supporting it.
CR_BRAKGOOP, CR_BRAKGOOP,
// Specific level gimmicks. // Specific level gimmicks.
@ -254,6 +247,7 @@ typedef enum
pw_underwater, // underwater timer pw_underwater, // underwater timer
pw_spacetime, // In space, no one can hear you spin! pw_spacetime, // In space, no one can hear you spin!
pw_extralife, // Extra Life timer pw_extralife, // Extra Life timer
pw_pushing,
pw_super, // Are you super? pw_super, // Are you super?
pw_gravityboots, // gravity boots pw_gravityboots, // gravity boots
@ -326,6 +320,9 @@ typedef struct player_s
// It is updated with cmd->aiming. // It is updated with cmd->aiming.
angle_t aiming; angle_t aiming;
// fun thing for player sprite
angle_t drawangle;
// player's ring count // player's ring count
INT32 rings; INT32 rings;

View File

@ -64,6 +64,7 @@ memset(used_spr,0,sizeof(UINT8) * ((NUMSPRITEFREESLOTS / 8) + 1));\
static mobjtype_t get_mobjtype(const char *word); static mobjtype_t get_mobjtype(const char *word);
static statenum_t get_state(const char *word); static statenum_t get_state(const char *word);
static spritenum_t get_sprite(const char *word); static spritenum_t get_sprite(const char *word);
static playersprite_t get_sprite2(const char *word);
static sfxenum_t get_sfx(const char *word); static sfxenum_t get_sfx(const char *word);
#ifdef MUSICSLOT_COMPATIBILITY #ifdef MUSICSLOT_COMPATIBILITY
static UINT16 get_mus(const char *word, UINT8 dehacked_mode); static UINT16 get_mus(const char *word, UINT8 dehacked_mode);
@ -77,6 +78,8 @@ boolean deh_loaded = false;
static int dbg_line; static int dbg_line;
static boolean gamedataadded = false; static boolean gamedataadded = false;
static boolean titlechanged = false;
static boolean introchanged = false;
ATTRINLINE static FUNCINLINE char myfget_color(MYFILE *f) ATTRINLINE static FUNCINLINE char myfget_color(MYFILE *f)
{ {
@ -769,6 +772,49 @@ static void readspritelight(MYFILE *f, INT32 num)
} }
#endif // HWRENDER #endif // HWRENDER
static void readsprite2(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
char *word, *word2;
char *tmp;
do
{
if (myfgets(s, MAXLINELEN, f))
{
if (s[0] == '\n')
break;
tmp = strchr(s, '#');
if (tmp)
*tmp = '\0';
if (s == tmp)
continue; // Skip comment lines, but don't break.
word = strtok(s, " ");
if (word)
strupr(word);
else
break;
word2 = strtok(NULL, " = ");
if (word2)
strupr(word2);
else
break;
if (word2[strlen(word2)-1] == '\n')
word2[strlen(word2)-1] = '\0';
if (fastcmp(word, "DEFAULT"))
spr2defaults[num] = get_number(word2);
else
deh_warning("Sprite2 %s: unknown word '%s'", spr2names[num], word);
}
} while (!myfeof(f)); // finish when the line is empty
Z_Free(s);
}
static const struct { static const struct {
const char *name; const char *name;
const UINT16 flag; const UINT16 flag;
@ -2667,14 +2713,36 @@ static void readmaincfg(MYFILE *f)
// range check, you morons. // range check, you morons.
if (introtoplay > 128) if (introtoplay > 128)
introtoplay = 128; introtoplay = 128;
introchanged = true;
} }
else if (fastcmp(word, "LOOPTITLE")) else if (fastcmp(word, "LOOPTITLE"))
{ {
looptitle = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); looptitle = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y');
titlechanged = true;
}
else if (fastcmp(word, "TITLEMAP"))
{
// Support using the actual map name,
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
else
value = get_number(word2);
titlemap = (INT16)value;
titlechanged = true;
}
else if (fastcmp(word, "HIDETITLEPICS"))
{
hidetitlepics = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y');
titlechanged = true;
} }
else if (fastcmp(word, "TITLESCROLLSPEED")) else if (fastcmp(word, "TITLESCROLLSPEED"))
{ {
titlescrollspeed = get_number(word2); titlescrollspeed = get_number(word2);
titlechanged = true;
} }
else if (fastcmp(word, "CREDITSCUTSCENE")) else if (fastcmp(word, "CREDITSCUTSCENE"))
{ {
@ -2690,14 +2758,17 @@ static void readmaincfg(MYFILE *f)
else if (fastcmp(word, "NUMDEMOS")) else if (fastcmp(word, "NUMDEMOS"))
{ {
numDemos = (UINT8)get_number(word2); numDemos = (UINT8)get_number(word2);
titlechanged = true;
} }
else if (fastcmp(word, "DEMODELAYTIME")) else if (fastcmp(word, "DEMODELAYTIME"))
{ {
demoDelayTime = get_number(word2); demoDelayTime = get_number(word2);
titlechanged = true;
} }
else if (fastcmp(word, "DEMOIDLETIME")) else if (fastcmp(word, "DEMOIDLETIME"))
{ {
demoIdleTime = get_number(word2); demoIdleTime = get_number(word2);
titlechanged = true;
} }
else if (fastcmp(word, "USE1UPSOUND")) else if (fastcmp(word, "USE1UPSOUND"))
{ {
@ -2731,14 +2802,31 @@ static void readmaincfg(MYFILE *f)
strlcat(savegamename, "%u.ssg", sizeof(savegamename)); strlcat(savegamename, "%u.ssg", sizeof(savegamename));
gamedataadded = true; gamedataadded = true;
titlechanged = true;
} }
else if (fastcmp(word, "RESETDATA")) else if (fastcmp(word, "RESETDATA"))
{ {
P_ResetData(value); P_ResetData(value);
titlechanged = true;
} }
else if (fastcmp(word, "CUSTOMVERSION")) else if (fastcmp(word, "CUSTOMVERSION"))
{ {
strlcpy(customversionstring, word2, sizeof (customversionstring)); strlcpy(customversionstring, word2, sizeof (customversionstring));
//titlechanged = true;
}
else if (fastcmp(word, "BOOTMAP"))
{
// Support using the actual map name,
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = M_MapNumber(word2[0], word2[1]);
else
value = get_number(word2);
bootmap = (INT16)value;
//titlechanged = true;
} }
else else
deh_warning("Maincfg: unknown word '%s'", word); deh_warning("Maincfg: unknown word '%s'", word);
@ -2935,7 +3023,7 @@ static void DEH_LoadDehackedFile(MYFILE *f)
deh_num_warning = 0; deh_num_warning = 0;
gamedataadded = false; gamedataadded = titlechanged = introchanged = false;
// it doesn't test the version of SRB2 and version of dehacked file // it doesn't test the version of SRB2 and version of dehacked file
dbg_line = -1; // start at -1 so the first line is 0. dbg_line = -1; // start at -1 so the first line is 0.
@ -3020,9 +3108,21 @@ static void DEH_LoadDehackedFile(MYFILE *f)
ignorelines(f); ignorelines(f);
} }
} }
else if (fastcmp(word, "SPRITE2"))
{
if (i == 0 && word2[0] != '0') // If word2 isn't a number
i = get_sprite2(word2); // find a sprite by name
if (i < (INT32)free_spr2 && i >= (INT32)SPR2_FIRSTFREESLOT)
readsprite2(f, i);
else
{
deh_warning("Sprite2 number %d out of range (%d - %d)", i, SPR2_FIRSTFREESLOT, free_spr2-1);
ignorelines(f);
}
}
#ifdef HWRENDER
else if (fastcmp(word, "LIGHT")) else if (fastcmp(word, "LIGHT"))
{ {
#ifdef HWRENDER
// TODO: Read lights by name // TODO: Read lights by name
if (i > 0 && i < NUMLIGHTS) if (i > 0 && i < NUMLIGHTS)
readlight(f, i); readlight(f, i);
@ -3031,22 +3131,20 @@ static void DEH_LoadDehackedFile(MYFILE *f)
deh_warning("Light number %d out of range (1 - %d)", i, NUMLIGHTS-1); deh_warning("Light number %d out of range (1 - %d)", i, NUMLIGHTS-1);
ignorelines(f); ignorelines(f);
} }
#endif
} }
else if (fastcmp(word, "SPRITE")) else if (fastcmp(word, "SPRITE"))
{ {
#ifdef HWRENDER
if (i == 0 && word2[0] != '0') // If word2 isn't a number if (i == 0 && word2[0] != '0') // If word2 isn't a number
i = get_sprite(word2); // find a sprite by name i = get_sprite(word2); // find a sprite by name
if (i < NUMSPRITES && i >= 0) if (i < NUMSPRITES && i > 0)
readspritelight(f, i); readspritelight(f, i);
else else
{ {
deh_warning("Sprite number %d out of range (0 - %d)", i, NUMSPRITES-1); deh_warning("Sprite number %d out of range (0 - %d)", i, NUMSPRITES-1);
ignorelines(f); ignorelines(f);
} }
#endif
} }
#endif
else if (fastcmp(word, "LEVEL")) else if (fastcmp(word, "LEVEL"))
{ {
// Support using the actual map name, // Support using the actual map name,
@ -3236,6 +3334,14 @@ static void DEH_LoadDehackedFile(MYFILE *f)
if (gamedataadded) if (gamedataadded)
G_LoadGameData(); G_LoadGameData();
if (gamestate == GS_TITLESCREEN)
{
if (introchanged)
COM_BufAddText("playintro");
else if (titlechanged)
COM_BufAddText("exitgame"); // Command_ExitGame_f() but delayed
}
dbg_line = -1; dbg_line = -1;
if (deh_num_warning) if (deh_num_warning)
{ {
@ -3302,6 +3408,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_PLAY_STND", "S_PLAY_STND",
"S_PLAY_WAIT", "S_PLAY_WAIT",
"S_PLAY_WALK", "S_PLAY_WALK",
"S_PLAY_SKID",
"S_PLAY_RUN", "S_PLAY_RUN",
"S_PLAY_DASH", "S_PLAY_DASH",
"S_PLAY_PAIN", "S_PLAY_PAIN",
@ -6422,8 +6529,12 @@ static const char *const MAPTHINGFLAG_LIST[4] = {
#endif #endif
static const char *const PLAYERFLAG_LIST[] = { static const char *const PLAYERFLAG_LIST[] = {
// Flip camera angle with gravity flip prefrence.
"FLIPCAM", // Cvars
"FLIPCAM", // Flip camera angle with gravity flip prefrence.
"ANALOGMODE", // Analog mode?
"DIRECTIONCHAR", // Directional character sprites?
"AUTOBRAKE", // Autobrake?
// Cheats // Cheats
"GODMODE", "GODMODE",
@ -6441,41 +6552,36 @@ static const char *const PLAYERFLAG_LIST[] = {
"JUMPSTASIS", // and that includes jumping. "JUMPSTASIS", // and that includes jumping.
// (we don't include FULLSTASIS here I guess because it's just those two together...?) // (we don't include FULLSTASIS here I guess because it's just those two together...?)
// Did you get a time-over? // Applying autobrake?
"TIMEOVER", "APPLYAUTOBRAKE",
// Character action status // Character action status
"STARTJUMP", "STARTJUMP",
"JUMPED", "JUMPED",
"NOJUMPDAMAGE",
"SPINNING", "SPINNING",
"STARTDASH", "STARTDASH",
"THOKKED",
// Are you gliding? "THOKKED",
"SHIELDABILITY",
"GLIDING", "GLIDING",
"BOUNCING",
// Sliding (usually in water) like Labyrinth/Oil Ocean // Sliding (usually in water) like Labyrinth/Oil Ocean
"SLIDING", "SLIDING",
// Bouncing // NiGHTS stuff
"BOUNCING",
/*** NIGHTS STUFF ***/
"TRANSFERTOCLOSEST", "TRANSFERTOCLOSEST",
"NIGHTSFALL",
"DRILLING", "DRILLING",
"SKIDDOWN",
/*** TAG STUFF ***/ // Gametype-specific stuff
"TAGGED", // Player has been tagged and awaits the next round in hide and seek. "GAMETYPEOVER", // Race time over, or H&S out-of-game
"TAGIT", // The player is it! For Tag Mode "TAGIT", // The player is it! For Tag Mode
/*** misc ***/ /*** misc ***/
"FORCESTRAFE", // Translate turn inputs into strafe inputs "FORCESTRAFE", // Translate turn inputs into strafe inputs
"ANALOGMODE", // Analog mode?
"CANCARRY", // Can carry? "CANCARRY", // Can carry?
"SHIELDABILITY", // Thokked with shield ability
"NOJUMPDAMAGE", // No jump damage
NULL // stop loop here. NULL // stop loop here.
}; };
@ -6641,6 +6747,7 @@ static const char *const POWERS_LIST[] = {
"UNDERWATER", // underwater timer "UNDERWATER", // underwater timer
"SPACETIME", // In space, no one can hear you spin! "SPACETIME", // In space, no one can hear you spin!
"EXTRALIFE", // Extra Life timer "EXTRALIFE", // Extra Life timer
"PUSHING",
"SUPER", // Are you super? "SUPER", // Are you super?
"GRAVITYBOOTS", // gravity boots "GRAVITYBOOTS", // gravity boots
@ -6891,6 +6998,7 @@ struct {
{"CR_GENERIC",CR_GENERIC}, {"CR_GENERIC",CR_GENERIC},
{"CR_PLAYER",CR_PLAYER}, {"CR_PLAYER",CR_PLAYER},
{"CR_NIGHTSMODE",CR_NIGHTSMODE}, {"CR_NIGHTSMODE",CR_NIGHTSMODE},
{"CR_NIGHTSFALL",CR_NIGHTSFALL},
{"CR_BRAKGOOP",CR_BRAKGOOP}, {"CR_BRAKGOOP",CR_BRAKGOOP},
{"CR_ZOOMTUBE",CR_ZOOMTUBE}, {"CR_ZOOMTUBE",CR_ZOOMTUBE},
{"CR_ROPEHANG",CR_ROPEHANG}, {"CR_ROPEHANG",CR_ROPEHANG},
@ -7290,6 +7398,20 @@ static spritenum_t get_sprite(const char *word)
return SPR_NULL; return SPR_NULL;
} }
static playersprite_t get_sprite2(const char *word)
{ // Returns the value of SPR2_ enumerations
playersprite_t i;
if (*word >= '0' && *word <= '9')
return atoi(word);
if (fastncmp("SPR2_",word,5))
word += 5; // take off the SPR2_
for (i = 0; i < NUMPLAYERSPRITES; i++)
if (!spr2names[i][4] && memcmp(word,spr2names[i],4)==0)
return i;
deh_warning("Couldn't find sprite named 'SPR2_%s'",word);
return SPR2_STND;
}
static sfxenum_t get_sfx(const char *word) static sfxenum_t get_sfx(const char *word)
{ // Returns the value of SFX_ enumerations { // Returns the value of SFX_ enumerations
sfxenum_t i; sfxenum_t i;
@ -7735,7 +7857,7 @@ static inline int lib_freeslot(lua_State *L)
else if (fastcmp(type, "SPR2")) else if (fastcmp(type, "SPR2"))
{ {
// Search if we already have an SPR2 by that name... // Search if we already have an SPR2 by that name...
enum playersprite i; playersprite_t i;
for (i = SPR2_FIRSTFREESLOT; i < free_spr2; i++) for (i = SPR2_FIRSTFREESLOT; i < free_spr2; i++)
if (memcmp(spr2names[i],word,4) == 0) if (memcmp(spr2names[i],word,4) == 0)
break; break;
@ -7914,7 +8036,7 @@ static inline int lib_getenum(lua_State *L)
if (mathlib) return luaL_error(L, "sprite '%s' could not be found.\n", word); if (mathlib) return luaL_error(L, "sprite '%s' could not be found.\n", word);
return 0; return 0;
} }
else if (fastncmp("SPR2_",word,4)) { else if (fastncmp("SPR2_",word,5)) {
p = word+5; p = word+5;
for (i = 0; i < (fixed_t)free_spr2; i++) for (i = 0; i < (fixed_t)free_spr2; i++)
if (!spr2names[i][4]) if (!spr2names[i][4])
@ -8119,6 +8241,12 @@ static inline int lib_getenum(lua_State *L)
} else if (fastcmp(word,"paused")) { } else if (fastcmp(word,"paused")) {
lua_pushboolean(L, paused); lua_pushboolean(L, paused);
return 1; return 1;
} else if (fastcmp(word,"titlemap")) {
lua_pushinteger(L, titlemap);
return 1;
} else if (fastcmp(word,"titlemapinaction")) {
lua_pushboolean(L, (titlemapinaction != TITLEMAP_OFF));
return 1;
} else if (fastcmp(word,"gametype")) { } else if (fastcmp(word,"gametype")) {
lua_pushinteger(L, gametype); lua_pushinteger(L, gametype);
return 1; return 1;

View File

@ -45,6 +45,10 @@ extern INT32 cursaveslot;
extern INT16 lastmaploaded; extern INT16 lastmaploaded;
extern boolean gamecomplete; extern boolean gamecomplete;
#define maxgameovers 13
extern UINT8 numgameovers;
extern SINT8 startinglivesbalance[maxgameovers+1];
#define PRECIP_NONE 0 #define PRECIP_NONE 0
#define PRECIP_STORM 1 #define PRECIP_STORM 1
#define PRECIP_SNOW 2 #define PRECIP_SNOW 2
@ -125,6 +129,10 @@ extern INT16 spstage_start;
extern INT16 sstage_start; extern INT16 sstage_start;
extern INT16 sstage_end; extern INT16 sstage_end;
extern INT16 titlemap;
extern boolean hidetitlepics;
extern INT16 bootmap; //bootmap for loading a map on startup
extern boolean looptitle; extern boolean looptitle;
extern boolean useNightsSS; extern boolean useNightsSS;

View File

@ -31,11 +31,18 @@
#include "m_random.h" #include "m_random.h"
#include "y_inter.h" #include "y_inter.h"
#include "m_cond.h" #include "m_cond.h"
#include "p_local.h"
#include "p_setup.h"
#ifdef HAVE_BLUA
#include "lua_hud.h"
#endif
// Stage of animation: // Stage of animation:
// 0 = text, 1 = art screen // 0 = text, 1 = art screen
static INT32 finalecount; static INT32 finalecount;
INT32 titlescrollspeed = 80; INT32 titlescrollspeed = 80;
UINT8 titlemapinaction = TITLEMAP_OFF;
static INT32 timetonext; // Delay between screen changes static INT32 timetonext; // Delay between screen changes
static INT32 continuetime; // Short delay when continuing static INT32 continuetime; // Short delay when continuing
@ -217,17 +224,19 @@ static void F_SkyScroll(INT32 scrollspeed)
{ {
INT32 scrolled, x, mx, fakedwidth; INT32 scrolled, x, mx, fakedwidth;
patch_t *pat; patch_t *pat;
INT16 patwidth;
pat = W_CachePatchName("TITLESKY", PU_CACHE); pat = W_CachePatchName("TITLESKY", PU_CACHE);
animtimer = ((finalecount*scrollspeed)/16) % SHORT(pat->width); patwidth = SHORT(pat->width);
animtimer = ((finalecount*scrollspeed)/16 + patwidth) % patwidth;
fakedwidth = vid.width / vid.dupx; fakedwidth = vid.width / vid.dupx;
if (rendermode == render_soft) if (rendermode == render_soft)
{ // if only hardware rendering could be this elegant and complete { // if only hardware rendering could be this elegant and complete
scrolled = (SHORT(pat->width) - animtimer) - 1; scrolled = (patwidth - animtimer) - 1;
for (x = 0, mx = scrolled; x < fakedwidth; x++, mx = (mx+1)%SHORT(pat->width)) for (x = 0, mx = scrolled; x < fakedwidth; x++, mx = (mx+1)%patwidth)
F_DrawPatchCol(x, pat, mx); F_DrawPatchCol(x, pat, mx);
} }
#ifdef HWRENDER #ifdef HWRENDER
@ -235,8 +244,8 @@ static void F_SkyScroll(INT32 scrollspeed)
{ // if only software rendering could be this simple and retarded { // if only software rendering could be this simple and retarded
scrolled = animtimer; scrolled = animtimer;
if (scrolled > 0) if (scrolled > 0)
V_DrawScaledPatch(scrolled - SHORT(pat->width), 0, 0, pat); V_DrawScaledPatch(scrolled - patwidth, 0, 0, pat);
for (x = 0; x < fakedwidth; x += SHORT(pat->width)) for (x = 0; x < fakedwidth; x += patwidth)
V_DrawScaledPatch(x + scrolled, 0, 0, pat); V_DrawScaledPatch(x + scrolled, 0, 0, pat);
} }
#endif #endif
@ -278,6 +287,8 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset
void F_StartIntro(void) void F_StartIntro(void)
{ {
S_StopMusic();
if (introtoplay) if (introtoplay)
{ {
if (!cutscenes[introtoplay - 1]) if (!cutscenes[introtoplay - 1])
@ -998,7 +1009,7 @@ static const char *credits[] = {
"", "",
"\1Sprite Artists", "\1Sprite Artists",
"Odi \"Iceman404\" Atunzu", "Odi \"Iceman404\" Atunzu",
"Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D: "Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D:
"Jim \"MotorRoach\" DeMello", "Jim \"MotorRoach\" DeMello",
"Desmond \"Blade\" DesJardins", "Desmond \"Blade\" DesJardins",
"Sherman \"CoatRack\" DesJardins", "Sherman \"CoatRack\" DesJardins",
@ -1114,7 +1125,7 @@ void F_StartCredits(void)
M_ClearMenus(true); M_ClearMenus(true);
// Save the second we enter the credits // Save the second we enter the credits
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot >= 0) if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0)
G_SaveGame((UINT32)cursaveslot); G_SaveGame((UINT32)cursaveslot);
if (creditscutscene) if (creditscutscene)
@ -1252,7 +1263,7 @@ static boolean drawemblem = false, drawchaosemblem = false;
void F_StartGameEvaluation(void) void F_StartGameEvaluation(void)
{ {
// Credits option in secrets menu // Credits option in secrets menu
if (cursaveslot == -2) if (cursaveslot == -1)
{ {
F_StartGameEnd(); F_StartGameEnd();
return; return;
@ -1266,7 +1277,7 @@ void F_StartGameEvaluation(void)
// Save the second we enter the evaluation // Save the second we enter the evaluation
// We need to do this again! Remember, it's possible a mod designed skipped // We need to do this again! Remember, it's possible a mod designed skipped
// the credits sequence! // the credits sequence!
if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot >= 0) if ((!modifiedgame || savemoddata) && !(netgame || multiplayer) && cursaveslot > 0)
G_SaveGame((UINT32)cursaveslot); G_SaveGame((UINT32)cursaveslot);
gameaction = ga_nothing; gameaction = ga_nothing;
@ -1415,17 +1426,72 @@ void F_GameEndTicker(void)
// ============== // ==============
void F_StartTitleScreen(void) void F_StartTitleScreen(void)
{ {
S_ChangeMusicInternal("_title", looptitle);
if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS) if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS)
finalecount = 0; finalecount = 0;
else else
wipegamestate = GS_TITLESCREEN; wipegamestate = GS_TITLESCREEN;
if (titlemap)
{
mapthing_t *startpos;
gamestate_t prevwipegamestate = wipegamestate;
titlemapinaction = TITLEMAP_LOADING;
gamemap = titlemap;
if (!mapheaderinfo[gamemap-1])
P_AllocMapHeader(gamemap-1);
maptol = mapheaderinfo[gamemap-1]->typeoflevel;
globalweather = mapheaderinfo[gamemap-1]->weather;
G_DoLoadLevel(true);
if (!titlemap)
return;
players[displayplayer].playerstate = PST_DEAD; // Don't spawn the player in dummy (I'm still a filthy cheater)
// Set Default Position
if (playerstarts[0])
startpos = playerstarts[0];
else if (deathmatchstarts[0])
startpos = deathmatchstarts[0];
else
startpos = NULL;
if (startpos)
{
camera.x = startpos->x << FRACBITS;
camera.y = startpos->y << FRACBITS;
camera.subsector = R_PointInSubsector(camera.x, camera.y);
camera.z = camera.subsector->sector->floorheight + ((startpos->options >> ZSHIFT) << FRACBITS);
camera.angle = (startpos->angle % 360)*ANG1;
camera.aiming = 0;
}
else
{
camera.x = camera.y = camera.z = camera.angle = camera.aiming = 0;
camera.subsector = NULL; // toast is filthy too
}
camera.chase = true;
camera.height = 0;
wipegamestate = prevwipegamestate;
}
else
{
titlemapinaction = TITLEMAP_OFF;
gamemap = 1; // g_game.c
CON_ClearHUD();
}
G_SetGamestate(GS_TITLESCREEN); G_SetGamestate(GS_TITLESCREEN);
CON_ClearHUD();
// IWAD dependent stuff. // IWAD dependent stuff.
S_ChangeMusicInternal("_title", looptitle);
animtimer = 0; animtimer = 0;
demoDelayLeft = demoDelayTime; demoDelayLeft = demoDelayTime;
@ -1455,12 +1521,21 @@ void F_TitleScreenDrawer(void)
return; // We likely came here from retrying. Don't do a damn thing. return; // We likely came here from retrying. Don't do a damn thing.
// Draw that sky! // Draw that sky!
F_SkyScroll(titlescrollspeed); if (!titlemapinaction)
F_SkyScroll(titlescrollspeed);
// Don't draw outside of the title screewn, or if the patch isn't there. // Don't draw outside of the title screewn, or if the patch isn't there.
if (!ttwing || (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS)) if (!ttwing || (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS))
return; return;
// rei|miru: use title pics?
if (hidetitlepics)
#ifdef HAVE_BLUA
goto luahook;
#else
return;
#endif
V_DrawScaledPatch(30, 14, 0, ttwing); V_DrawScaledPatch(30, 14, 0, ttwing);
if (finalecount < 57) if (finalecount < 57)
@ -1497,6 +1572,11 @@ void F_TitleScreenDrawer(void)
} }
V_DrawScaledPatch(48, 142, 0,ttbanner); V_DrawScaledPatch(48, 142, 0,ttbanner);
#ifdef HAVE_BLUA
luahook:
LUAh_TitleHUD();
#endif
} }
// (no longer) De-Demo'd Title Screen // (no longer) De-Demo'd Title Screen
@ -1509,6 +1589,46 @@ void F_TitleScreenTicker(boolean run)
if (gameaction != ga_nothing || gamestate != GS_TITLESCREEN) if (gameaction != ga_nothing || gamestate != GS_TITLESCREEN)
return; return;
// Execute the titlemap camera settings
if (titlemapinaction)
{
thinker_t *th;
mobj_t *mo2;
mobj_t *cameraref = NULL;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
{
if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker
continue;
mo2 = (mobj_t *)th;
if (!mo2)
continue;
if (mo2->type != MT_ALTVIEWMAN)
continue;
cameraref = mo2;
break;
}
if (cameraref)
{
camera.x = cameraref->x;
camera.y = cameraref->y;
camera.z = cameraref->z;
camera.angle = cameraref->angle;
camera.aiming = cameraref->cusval;
camera.subsector = cameraref->subsector;
}
else
{
// Default behavior: Do a lil' camera spin if a title map is loaded;
camera.angle += titlescrollspeed*ANG1/64;
}
}
// no demos to play? or, are they disabled? // no demos to play? or, are they disabled?
if (!cv_rollingdemos.value || !numDemos) if (!cv_rollingdemos.value || !numDemos)
return; return;

View File

@ -62,6 +62,15 @@ void F_ContinueDrawer(void);
extern INT32 titlescrollspeed; extern INT32 titlescrollspeed;
typedef enum
{
TITLEMAP_OFF = 0,
TITLEMAP_LOADING,
TITLEMAP_RUNNING
} titlemap_enum;
extern UINT8 titlemapinaction;
// //
// WIPE // WIPE
// //

View File

@ -76,11 +76,14 @@ INT16 gamemap = 1;
INT16 maptol; INT16 maptol;
UINT8 globalweather = 0; UINT8 globalweather = 0;
INT32 curWeather = PRECIP_NONE; INT32 curWeather = PRECIP_NONE;
INT32 cursaveslot = -1; // Auto-save 1p savegame slot INT32 cursaveslot = 0; // Auto-save 1p savegame slot
//INT16 lastmapsaved = 0; // Last map we auto-saved at //INT16 lastmapsaved = 0; // Last map we auto-saved at
INT16 lastmaploaded = 0; // Last map the game loaded INT16 lastmaploaded = 0; // Last map the game loaded
boolean gamecomplete = false; boolean gamecomplete = false;
UINT8 numgameovers = 0; // for startinglives balance
SINT8 startinglivesbalance[maxgameovers+1] = {3, 5, 7, 9, 12, 15, 20, 25, 30, 40, 50, 75, 99, 0x7F};
UINT16 mainwads = 0; UINT16 mainwads = 0;
boolean modifiedgame; // Set if homebrew PWAD stuff has been added. boolean modifiedgame; // Set if homebrew PWAD stuff has been added.
boolean savemoddata = false; boolean savemoddata = false;
@ -121,6 +124,10 @@ INT16 spstage_start;
INT16 sstage_start; INT16 sstage_start;
INT16 sstage_end; INT16 sstage_end;
INT16 titlemap = 0;
boolean hidetitlepics = false;
INT16 bootmap; //bootmap for loading a map on startup
boolean looptitle = false; boolean looptitle = false;
boolean useNightsSS = false; boolean useNightsSS = false;
@ -286,6 +293,10 @@ static void UserAnalog_OnChange(void);
static void UserAnalog2_OnChange(void); static void UserAnalog2_OnChange(void);
static void Analog_OnChange(void); static void Analog_OnChange(void);
static void Analog2_OnChange(void); static void Analog2_OnChange(void);
static void DirectionChar_OnChange(void);
static void DirectionChar2_OnChange(void);
static void AutoBrake_OnChange(void);
static void AutoBrake2_OnChange(void);
void SendWeaponPref(void); void SendWeaponPref(void);
void SendWeaponPref2(void); void SendWeaponPref2(void);
@ -368,6 +379,14 @@ consvar_t cv_useranalog = {"useranalog", "Off", CV_SAVE|CV_CALL, CV_OnOff, UserA
consvar_t cv_useranalog2 = {"useranalog2", "Off", CV_SAVE|CV_CALL, CV_OnOff, UserAnalog2_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_useranalog2 = {"useranalog2", "Off", CV_SAVE|CV_CALL, CV_OnOff, UserAnalog2_OnChange, 0, NULL, NULL, 0, 0, NULL};
#endif #endif
static CV_PossibleValue_t directionchar_cons_t[] = {{0, "Camera"}, {1, "Movement"}, {0, NULL}};
// deez New User eXperiences
consvar_t cv_directionchar = {"directionchar", "Movement", CV_SAVE|CV_CALL, directionchar_cons_t, DirectionChar_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_directionchar2 = {"directionchar2", "Movement", CV_SAVE|CV_CALL, directionchar_cons_t, DirectionChar2_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_autobrake = {"autobrake", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_autobrake2 = {"autobrake2", "On", CV_SAVE|CV_CALL, CV_OnOff, AutoBrake2_OnChange, 0, NULL, NULL, 0, 0, NULL};
typedef enum typedef enum
{ {
AXISNONE = 0, AXISNONE = 0,
@ -1615,6 +1634,46 @@ static void Analog2_OnChange(void)
SendWeaponPref2(); SendWeaponPref2();
} }
static void DirectionChar_OnChange(void)
{
if (cv_directionchar.value)
players[consoleplayer].pflags |= PF_DIRECTIONCHAR;
else
players[consoleplayer].pflags &= ~PF_DIRECTIONCHAR;
SendWeaponPref();
}
static void DirectionChar2_OnChange(void)
{
if (cv_directionchar2.value)
players[secondarydisplayplayer].pflags |= PF_DIRECTIONCHAR;
else
players[secondarydisplayplayer].pflags &= ~PF_DIRECTIONCHAR;
SendWeaponPref2();
}
static void AutoBrake_OnChange(void)
{
if (cv_autobrake.value)
players[consoleplayer].pflags |= PF_AUTOBRAKE;
else
players[consoleplayer].pflags &= ~PF_AUTOBRAKE;
SendWeaponPref();
}
static void AutoBrake2_OnChange(void)
{
if (cv_autobrake2.value)
players[secondarydisplayplayer].pflags |= PF_AUTOBRAKE;
else
players[secondarydisplayplayer].pflags &= ~PF_AUTOBRAKE;
SendWeaponPref2();
}
// //
// G_DoLoadLevel // G_DoLoadLevel
// //
@ -1633,6 +1692,21 @@ void G_DoLoadLevel(boolean resetplayer)
if (gamestate == GS_INTERMISSION) if (gamestate == GS_INTERMISSION)
Y_EndIntermission(); Y_EndIntermission();
// cleanup
if (titlemapinaction == TITLEMAP_LOADING)
{
if (W_CheckNumForName(G_BuildMapName(gamemap)) == LUMPERROR)
{
titlemap = 0; // let's not infinite recursion ok
Command_ExitGame_f();
return;
}
titlemapinaction = TITLEMAP_RUNNING;
}
else
titlemapinaction = TITLEMAP_OFF;
G_SetGamestate(GS_LEVEL); G_SetGamestate(GS_LEVEL);
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
@ -1642,7 +1716,7 @@ void G_DoLoadLevel(boolean resetplayer)
} }
// Setup the level. // Setup the level.
if (!P_SetupLevel(false)) if (!P_SetupLevel(false)) // this never returns false?
{ {
// fail so reset game stuff // fail so reset game stuff
Command_ExitGame_f(); Command_ExitGame_f();
@ -1989,6 +2063,7 @@ void G_Ticker(boolean run)
break; break;
case GS_TITLESCREEN: case GS_TITLESCREEN:
if (titlemapinaction) P_Ticker(run); // then intentionally fall through
case GS_WAITINGPLAYERS: case GS_WAITINGPLAYERS:
F_TitleScreenTicker(run); F_TitleScreenTicker(run);
break; break;
@ -2101,7 +2176,7 @@ void G_PlayerReborn(INT32 player)
jointime = players[player].jointime; jointime = players[player].jointime;
spectator = players[player].spectator; spectator = players[player].spectator;
outofcoop = players[player].outofcoop; outofcoop = players[player].outofcoop;
pflags = (players[player].pflags & (PF_TIMEOVER|PF_FLIPCAM|PF_TAGIT|PF_TAGGED|PF_ANALOGMODE)); pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER));
// As long as we're not in multiplayer, carry over cheatcodes from map to map // As long as we're not in multiplayer, carry over cheatcodes from map to map
if (!(netgame || multiplayer)) if (!(netgame || multiplayer))
@ -3127,8 +3202,11 @@ static void G_DoContinued(void)
tokenlist = 0; tokenlist = 0;
token = 0; token = 0;
if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && (!modifiedgame || savemoddata) && cursaveslot > 0)
G_SaveGameOver((UINT32)cursaveslot, true);
// Reset # of lives // Reset # of lives
pl->lives = (ultimatemode) ? 1 : 3; pl->lives = (ultimatemode) ? 1 : startinglivesbalance[numgameovers];
D_MapChange(gamemap, gametype, ultimatemode, false, 0, false, false); D_MapChange(gamemap, gametype, ultimatemode, false, 0, false, false);
@ -3467,59 +3545,6 @@ void G_SaveGameData(void)
#define VERSIONSIZE 16 #define VERSIONSIZE 16
#ifdef SAVEGAMES_OTHERVERSIONS
static INT16 startonmapnum = 0;
//
// User wants to load a savegame from a different version?
//
static void M_ForceLoadGameResponse(INT32 ch)
{
if (ch != 'y' && ch != KEY_ENTER)
{
//refused
Z_Free(savebuffer);
save_p = savebuffer = NULL;
startonmapnum = 0;
M_SetupNextMenu(&SP_LoadDef);
return;
}
// pick up where we left off.
save_p += VERSIONSIZE;
if (!P_LoadGame(startonmapnum))
{
M_ClearMenus(true); // so ESC backs out to title
M_StartMessage(M_GetText("Savegame file corrupted\n\nPress ESC\n"), NULL, MM_NOTHING);
Command_ExitGame_f();
Z_Free(savebuffer);
save_p = savebuffer = NULL;
startonmapnum = 0;
// no cheating!
memset(&savedata, 0, sizeof(savedata));
return;
}
// done
Z_Free(savebuffer);
save_p = savebuffer = NULL;
startonmapnum = 0;
//set cursaveslot to -1 so nothing gets saved.
cursaveslot = -1;
displayplayer = consoleplayer;
multiplayer = splitscreen = false;
if (setsizeneeded)
R_ExecuteSetViewSize();
M_ClearMenus(true);
CON_ToggleOff();
}
#endif
// //
// G_InitFromSavegame // G_InitFromSavegame
// Can be called by the startup code or the menu task. // Can be called by the startup code or the menu task.
@ -3612,13 +3637,13 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride)
// G_SaveGame // G_SaveGame
// Saves your game. // Saves your game.
// //
void G_SaveGame(UINT32 savegameslot) void G_SaveGame(UINT32 slot)
{ {
boolean saved; boolean saved;
char savename[256] = ""; char savename[256] = "";
const char *backup; const char *backup;
sprintf(savename, savegamename, savegameslot); sprintf(savename, savegamename, slot);
backup = va("%s",savename); backup = va("%s",savename);
// save during evaluation or credits? game's over, folks! // save during evaluation or credits? game's over, folks!
@ -3654,9 +3679,91 @@ void G_SaveGame(UINT32 savegameslot)
if (cv_debug && saved) if (cv_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, savegameslot, savegamename); CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, savegamename);
} }
#define BADSAVE goto cleanup;
#define CHECKPOS if (save_p >= end_p) BADSAVE
void G_SaveGameOver(UINT32 slot, boolean modifylives)
{
boolean saved = false;
size_t length;
char vcheck[VERSIONSIZE];
char savename[255];
const char *backup;
sprintf(savename, savegamename, slot);
backup = va("%s",savename);
length = FIL_ReadFile(savename, &savebuffer);
if (!length)
{
CONS_Printf(M_GetText("Couldn't read file %s\n"), savename);
return;
}
{
char temp[sizeof(timeattackfolder)];
UINT8 *end_p = savebuffer + length;
UINT8 *lives_p;
SINT8 pllives;
save_p = savebuffer;
// Version check
memset(vcheck, 0, sizeof (vcheck));
sprintf(vcheck, "version %d", VERSION);
if (strcmp((const char *)save_p, (const char *)vcheck)) BADSAVE
save_p += VERSIONSIZE;
// P_UnArchiveMisc()
(void)READINT16(save_p);
CHECKPOS
(void)READUINT16(save_p); // emeralds
CHECKPOS
READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to
if (strcmp(temp, timeattackfolder)) BADSAVE
// P_UnArchivePlayer()
CHECKPOS
(void)READUINT16(save_p);
CHECKPOS
WRITEUINT8(save_p, numgameovers);
CHECKPOS
lives_p = save_p;
pllives = READSINT8(save_p); // lives
CHECKPOS
if (modifylives && pllives < startinglivesbalance[numgameovers])
{
pllives = startinglivesbalance[numgameovers];
WRITESINT8(lives_p, pllives);
}
(void)READINT32(save_p); // Score
CHECKPOS
(void)READINT32(save_p); // continues
// File end marker check
CHECKPOS
if (READUINT8(save_p) != 0x1d) BADSAVE;
// done
saved = FIL_WriteFile(backup, savebuffer, length);
}
cleanup:
if (cv_debug && saved)
CONS_Printf(M_GetText("Game saved.\n"));
else if (!saved)
CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, savegamename);
Z_Free(savebuffer);
save_p = savebuffer = NULL;
}
#undef CHECKPOS
#undef BADSAVE
// //
// G_DeferedInitNew // G_DeferedInitNew
// Can be called by the startup code or the menu task, // Can be called by the startup code or the menu task,
@ -3664,7 +3771,7 @@ void G_SaveGame(UINT32 savegameslot)
// //
void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, boolean SSSG, boolean FLS) void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, boolean SSSG, boolean FLS)
{ {
UINT8 color = 0; UINT8 color = skins[pickedchar].prefcolor;
paused = false; paused = false;
if (demoplayback) if (demoplayback)
@ -3676,10 +3783,8 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, b
if (savedata.lives > 0) if (savedata.lives > 0)
{ {
color = savedata.skincolor; if ((botingame = ((botskin = savedata.botskin) != 0)))
botskin = savedata.botskin; botcolor = skins[botskin-1].prefcolor;
botcolor = savedata.botcolor;
botingame = (botskin != 0);
} }
else if (splitscreen != SSSG) else if (splitscreen != SSSG)
{ {
@ -3687,8 +3792,7 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, b
SplitScreen_OnChange(); SplitScreen_OnChange();
} }
if (!color) color = skins[pickedchar].prefcolor;
color = skins[pickedchar].prefcolor;
SetPlayerSkinByNum(consoleplayer, pickedchar); SetPlayerSkinByNum(consoleplayer, pickedchar);
CV_StealthSet(&cv_skin, skins[pickedchar].name); CV_StealthSet(&cv_skin, skins[pickedchar].name);
CV_StealthSetValue(&cv_playercolor, color); CV_StealthSetValue(&cv_playercolor, color);
@ -3720,7 +3824,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
if (resetplayer) if (resetplayer)
{ {
// Clear a bunch of variables // Clear a bunch of variables
tokenlist = token = sstimer = redscore = bluescore = lastmap = 0; numgameovers = tokenlist = token = sstimer = redscore = bluescore = lastmap = 0;
countdown = countdown2 = 0; countdown = countdown2 = 0;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
@ -3735,22 +3839,17 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
players[i].lives = cv_startinglives.value; players[i].lives = cv_startinglives.value;
players[i].continues = 0; players[i].continues = 0;
} }
else if (pultmode)
{
players[i].lives = 1;
players[i].continues = 0;
}
else else
{ {
players[i].lives = 3; players[i].lives = (pultmode) ? 1 : startinglivesbalance[0];
players[i].continues = 1; players[i].continues = (pultmode) ? 0 : 1;
} }
if (!((netgame || multiplayer) && (FLS))) if (!((netgame || multiplayer) && (FLS)))
players[i].score = 0; players[i].score = 0;
// The latter two should clear by themselves, but just in case // The latter two should clear by themselves, but just in case
players[i].pflags &= ~(PF_TAGIT|PF_TAGGED|PF_FULLSTASIS); players[i].pflags &= ~(PF_TAGIT|PF_GAMETYPEOVER|PF_FULLSTASIS);
// Clear cheatcodes too, just in case. // Clear cheatcodes too, just in case.
players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS); players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS);

View File

@ -59,6 +59,8 @@ extern consvar_t cv_invertmouse, cv_alwaysfreelook, cv_mousemove;
extern consvar_t cv_invertmouse2, cv_alwaysfreelook2, cv_mousemove2; extern consvar_t cv_invertmouse2, cv_alwaysfreelook2, cv_mousemove2;
extern consvar_t cv_useranalog, cv_useranalog2; extern consvar_t cv_useranalog, cv_useranalog2;
extern consvar_t cv_analog, cv_analog2; extern consvar_t cv_analog, cv_analog2;
extern consvar_t cv_directionchar, cv_directionchar2;
extern consvar_t cv_autobrake, cv_autobrake2;
extern consvar_t cv_sideaxis,cv_turnaxis,cv_moveaxis,cv_lookaxis,cv_fireaxis,cv_firenaxis; extern consvar_t cv_sideaxis,cv_turnaxis,cv_moveaxis,cv_lookaxis,cv_fireaxis,cv_firenaxis;
extern consvar_t cv_sideaxis2,cv_turnaxis2,cv_moveaxis2,cv_lookaxis2,cv_fireaxis2,cv_firenaxis2; extern consvar_t cv_sideaxis2,cv_turnaxis2,cv_moveaxis2,cv_lookaxis2,cv_fireaxis2,cv_firenaxis2;
extern consvar_t cv_ghost_bestscore, cv_ghost_besttime, cv_ghost_bestrings, cv_ghost_last, cv_ghost_guest; extern consvar_t cv_ghost_bestscore, cv_ghost_besttime, cv_ghost_bestrings, cv_ghost_last, cv_ghost_guest;
@ -116,6 +118,8 @@ void G_SaveGameData(void);
void G_SaveGame(UINT32 slot); void G_SaveGame(UINT32 slot);
void G_SaveGameOver(UINT32 slot, boolean modifylives);
// Only called by startup code. // Only called by startup code.
void G_RecordDemo(const char *name); void G_RecordDemo(const char *name);
void G_RecordMetal(void); void G_RecordMetal(void);

View File

@ -4753,8 +4753,6 @@ static polyplaneinfo_t *polyplaneinfo = NULL;
#ifndef SORTING #ifndef SORTING
size_t numfloors = 0; size_t numfloors = 0;
#else #else
//static floorinfo_t *floorinfo = NULL;
//static size_t numfloors = 0;
//Hurdler: 3D water sutffs //Hurdler: 3D water sutffs
typedef struct gr_drawnode_s typedef struct gr_drawnode_s
{ {
@ -5241,7 +5239,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
// Note: DO NOT do this in software mode version, it actually // Note: DO NOT do this in software mode version, it actually
// makes papersprites look WORSE there (I know, I've tried) // makes papersprites look WORSE there (I know, I've tried)
// Monster Iestyn - 13/05/17 // Monster Iestyn - 13/05/17
ang = dup_viewangle - thing->angle; ang = dup_viewangle - (thing->player ? thing->player->drawangle : thing->angle);
ang_scale = FIXED_TO_FLOAT(FINESINE(ang>>ANGLETOFINESHIFT)); ang_scale = FIXED_TO_FLOAT(FINESINE(ang>>ANGLETOFINESHIFT));
ang_scalez = FIXED_TO_FLOAT(FINECOSINE(ang>>ANGLETOFINESHIFT)); ang_scalez = FIXED_TO_FLOAT(FINECOSINE(ang>>ANGLETOFINESHIFT));
@ -5252,7 +5250,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
} }
} }
else if (sprframe->rotate != SRF_SINGLE) else if (sprframe->rotate != SRF_SINGLE)
ang = R_PointToAngle (thing->x, thing->y) - thing->angle; ang = R_PointToAngle (thing->x, thing->y) - (thing->player ? thing->player->drawangle : thing->angle);
if (sprframe->rotate == SRF_SINGLE) if (sprframe->rotate == SRF_SINGLE)
{ {

View File

@ -46,8 +46,7 @@ void HWR_SetViewSize(void);
void HWR_DrawPatch(GLPatch_t *gpatch, INT32 x, INT32 y, INT32 option); void HWR_DrawPatch(GLPatch_t *gpatch, INT32 x, INT32 y, INT32 option);
void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t scale, INT32 option, const UINT8 *colormap); void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t scale, INT32 option, const UINT8 *colormap);
void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t scale, INT32 option, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h); void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t scale, INT32 option, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h);
void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, INT32 option, fixed_t scale, fixed_t sx, fixed_t sy, fixed_t w, fixed_t h); void HWR_MakePatch(const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap);
void HWR_MakePatch (const patch_t *patch, GLPatch_t *grPatch, GLMipmap_t *grMipmap, boolean makebitmap);
void HWR_CreatePlanePolygons(INT32 bspnum); void HWR_CreatePlanePolygons(INT32 bspnum);
void HWR_CreateStaticLightmaps(INT32 bspnum); void HWR_CreateStaticLightmaps(INT32 bspnum);
void HWR_PrepLevelCache(size_t pnumtextures); void HWR_PrepLevelCache(size_t pnumtextures);
@ -104,13 +103,4 @@ extern float gr_viewwindowx, gr_basewindowcentery;
extern fixed_t *hwbbox; extern fixed_t *hwbbox;
extern FTransform atransform; extern FTransform atransform;
typedef struct
{
wallVert3D floorVerts[4];
FSurfaceInfo Surf;
INT32 texnum;
INT32 blend;
INT32 drawcount;
} floorinfo_t;
#endif #endif

View File

@ -28,6 +28,7 @@
#include "../doomdef.h" #include "../doomdef.h"
#include "../doomstat.h" #include "../doomstat.h"
#include "../fastcmp.h"
#ifdef HWRENDER #ifdef HWRENDER
#include "hw_drv.h" #include "hw_drv.h"
@ -265,6 +266,9 @@ static void md2_freeModel (md2_model_t *model)
free(model->frames); free(model->frames);
} }
if (model->spr2frames)
free(model->spr2frames);
if (model->glCommandBuffer) if (model->glCommandBuffer)
free(model->glCommandBuffer); free(model->glCommandBuffer);
@ -395,6 +399,39 @@ static md2_model_t *md2_readModel(const char *filename)
} }
strcpy(model->frames[i].name, frame->name); strcpy(model->frames[i].name, frame->name);
if (frame->name[0] == 'S')
{
boolean super;
if ((super = (fastncmp("UPER", frame->name+1, 4))) // SUPER
|| fastncmp("PR2_", frame->name+1, 4)) // SPR2_
{
UINT8 spr2;
for (spr2 = 0; spr2 < free_spr2; spr2++)
if (fastncmp(frame->name+5,spr2names[spr2],3)
&& ((frame->name[8] == spr2names[spr2][3])
|| (frame->name[8] == '.' && spr2names[spr2][3] == '_')))
break;
if (spr2 < free_spr2)
{
if (!model->spr2frames)
{
model->spr2frames = calloc(sizeof (size_t), 2*NUMPLAYERSPRITES*2);
if (!model->spr2frames)
{
md2_freeModel (model);
fclose(file);
return 0;
}
}
if (super)
spr2 |= FF_SPR2SUPER;
if (model->spr2frames[spr2*2 + 1]++ == 0) // numspr2frames
model->spr2frames[spr2*2] = i; // starting frame
CONS_Debug(DBG_RENDER, "frame %s, sprite2 %s - starting frame %d, number of frames %d\n", frame->name, spr2names[spr2 & ~FF_SPR2SUPER], model->spr2frames[spr2*2], model->spr2frames[spr2*2 + 1]);
}
}
}
for (j = 0; j < model->header.numVertices; j++) for (j = 0; j < model->header.numVertices; j++)
{ {
model->frames[i].vertices[j].vertex[0] = (float) ((INT32) frame->alias_vertices[j].vertex[0]) * frame->scale[0] + frame->translate[0]; model->frames[i].vertices[j].vertex[0] = (float) ((INT32) frame->alias_vertices[j].vertex[0]) * frame->scale[0] + frame->translate[0];
@ -1078,6 +1115,51 @@ static void HWR_GetBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, con
res? res?
run? run?
*/ */
static UINT8 P_GetModelSprite2(md2_t *md2, skin_t *skin, UINT8 spr2, player_t *player)
{
UINT8 super = 0, i = 0;
if (!md2 || !skin)
return 0;
while (!(md2->model->spr2frames[spr2*2 + 1])
&& spr2 != SPR2_STND
&& ++i != 32) // recursion limiter
{
if (spr2 & FF_SPR2SUPER)
{
super = FF_SPR2SUPER;
spr2 &= ~FF_SPR2SUPER;
continue;
}
switch(spr2)
{
// Normal special cases.
case SPR2_JUMP:
spr2 = ((player
? player->charflags
: skin->flags)
& SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_ROLL;
break;
case SPR2_TIRE:
spr2 = (player && player->charability == CA_SWIM) ? SPR2_SWIM : SPR2_FLY;
break;
// Use the handy list, that's what it's there for!
default:
spr2 = spr2defaults[spr2];
break;
}
spr2 |= super;
}
return spr2;
}
#define NORMALFOG 0x00000000 #define NORMALFOG 0x00000000
#define FADEFOG 0x19000000 #define FADEFOG 0x19000000
void HWR_DrawMD2(gr_vissprite_t *spr) void HWR_DrawMD2(gr_vissprite_t *spr)
@ -1225,31 +1307,70 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
tics = spr->mobj->anim_duration; tics = spr->mobj->anim_duration;
} }
//FIXME: this is not yet correct #define INTERPOLERATION_LIMIT TICRATE/4
frame = (spr->mobj->frame & FF_FRAMEMASK) % md2->model->header.numFrames;
buff = md2->model->glCommandBuffer; if (spr->mobj->skin && spr->mobj->sprite == SPR_PLAY && md2->model->spr2frames)
curr = &md2->model->frames[frame];
if (cv_grmd2.value == 1)
{ {
// frames are handled differently for states with FF_ANIMATE, so get the next frame differently for the interpolation UINT8 spr2 = P_GetModelSprite2(md2, spr->mobj->skin, spr->mobj->sprite2, spr->mobj->player);
if (spr->mobj->frame & FF_ANIMATE) UINT8 mod = md2->model->spr2frames[spr2*2 + 1] ? md2->model->spr2frames[spr2*2 + 1] : md2->model->header.numFrames;
if (mod > ((skin_t *)spr->mobj->skin)->sprites[spr2].numframes)
mod = ((skin_t *)spr->mobj->skin)->sprites[spr2].numframes;
//FIXME: this is not yet correct
frame = (spr->mobj->frame & FF_FRAMEMASK);
if (frame >= mod)
frame = 0;
buff = md2->model->glCommandBuffer;
curr = &md2->model->frames[md2->model->spr2frames[spr2*2] + frame];
if (cv_grmd2.value == 1 && tics <= INTERPOLERATION_LIMIT)
{ {
UINT32 nextframe = (spr->mobj->frame & FF_FRAMEMASK) + 1; if (durs > INTERPOLERATION_LIMIT)
if (nextframe >= (UINT32)spr->mobj->state->var1) durs = INTERPOLERATION_LIMIT;
nextframe = (spr->mobj->state->frame & FF_FRAMEMASK);
nextframe %= md2->model->header.numFrames; if (spr->mobj->frame & FF_ANIMATE
next = &md2->model->frames[nextframe]; || (spr->mobj->state->nextstate != S_NULL
} && states[spr->mobj->state->nextstate].sprite == spr->mobj->sprite
else && (states[spr->mobj->state->nextstate].frame & FF_FRAMEMASK) == spr->mobj->sprite2))
{
if (spr->mobj->state->nextstate != S_NULL && states[spr->mobj->state->nextstate].sprite != SPR_NULL
&& !(spr->mobj->player && spr->mobj->state->nextstate == S_PLAY_WAIT && spr->mobj->state == &states[S_PLAY_STND]))
{ {
const UINT32 nextframe = (states[spr->mobj->state->nextstate].frame & FF_FRAMEMASK) % md2->model->header.numFrames; if (++frame >= mod)
next = &md2->model->frames[nextframe]; frame = 0;
if (frame || !(spr->mobj->state->frame & FF_SPR2ENDSTATE))
next = &md2->model->frames[md2->model->spr2frames[spr2*2] + frame];
} }
} }
} }
else
{
//FIXME: this is not yet correct
frame = (spr->mobj->frame & FF_FRAMEMASK) % md2->model->header.numFrames;
buff = md2->model->glCommandBuffer;
curr = &md2->model->frames[frame];
if (cv_grmd2.value == 1 && tics <= INTERPOLERATION_LIMIT)
{
if (durs > INTERPOLERATION_LIMIT)
durs = INTERPOLERATION_LIMIT;
// frames are handled differently for states with FF_ANIMATE, so get the next frame differently for the interpolation
if (spr->mobj->frame & FF_ANIMATE)
{
UINT32 nextframe = (spr->mobj->frame & FF_FRAMEMASK) + 1;
if (nextframe >= (UINT32)spr->mobj->state->var1)
nextframe = (spr->mobj->state->frame & FF_FRAMEMASK);
nextframe %= md2->model->header.numFrames;
next = &md2->model->frames[nextframe];
}
else
{
if (spr->mobj->state->nextstate != S_NULL
&& states[spr->mobj->state->nextstate].sprite == spr->mobj->sprite)
{
const UINT32 nextframe = (states[spr->mobj->state->nextstate].frame & FF_FRAMEMASK) % md2->model->header.numFrames;
next = &md2->model->frames[nextframe];
}
}
}
}
#undef INTERPOLERATION_LIMIT
//Hurdler: it seems there is still a small problem with mobj angle //Hurdler: it seems there is still a small problem with mobj angle
p.x = FIXED_TO_FLOAT(spr->mobj->x); p.x = FIXED_TO_FLOAT(spr->mobj->x);
@ -1269,7 +1390,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
if (sprframe->rotate) if (sprframe->rotate)
{ {
const fixed_t anglef = AngleFixed(spr->mobj->angle); const fixed_t anglef = AngleFixed((spr->mobj->player ? spr->mobj->player->drawangle : spr->mobj->angle));
p.angley = FIXED_TO_FLOAT(anglef); p.angley = FIXED_TO_FLOAT(anglef);
} }
else else

View File

@ -22,6 +22,7 @@
#define _HW_MD2_H_ #define _HW_MD2_H_
#include "hw_glob.h" #include "hw_glob.h"
#include "../info.h"
// magic number "IDP2" or 844121161 // magic number "IDP2" or 844121161
#define MD2_IDENT (INT32)(('2' << 24) + ('P' << 16) + ('D' << 8) + 'I') #define MD2_IDENT (INT32)(('2' << 24) + ('P' << 16) + ('D' << 8) + 'I')
@ -111,7 +112,8 @@ typedef struct
md2_textureCoordinate_t *texCoords; md2_textureCoordinate_t *texCoords;
md2_triangle_t *triangles; md2_triangle_t *triangles;
md2_frame_t *frames; md2_frame_t *frames;
INT32 *glCommandBuffer; size_t *spr2frames; // size_t spr2frames[2*NUMPLAYERSPRITES][2];
INT32 *glCommandBuffer;
} ATTRPACK md2_model_t; } ATTRPACK md2_model_t;
#if defined(_MSC_VER) #if defined(_MSC_VER)

View File

@ -83,6 +83,7 @@ patch_t *rmatcico;
patch_t *bmatcico; patch_t *bmatcico;
patch_t *tagico; patch_t *tagico;
patch_t *tallminus; patch_t *tallminus;
patch_t *tallinfin;
//------------------------------------------- //-------------------------------------------
// coop hud // coop hud
@ -235,6 +236,7 @@ void HU_LoadGraphics(void)
// minus for negative tallnums // minus for negative tallnums
tallminus = (patch_t *)W_CachePatchName("STTMINUS", PU_HUDGFX); tallminus = (patch_t *)W_CachePatchName("STTMINUS", PU_HUDGFX);
tallinfin = (patch_t *)W_CachePatchName("STTINFIN", PU_HUDGFX);
// cache the crosshairs, don't bother to know which one is being used, // cache the crosshairs, don't bother to know which one is being used,
// just cache all 3, they're so small anyway. // just cache all 3, they're so small anyway.
@ -1250,7 +1252,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
} }
} }
if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3))) //show lives if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != 0x7f)) //show lives
V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|(greycheck ? V_60TRANS : 0), va("%dx", players[tab[i].num].lives)); V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|(greycheck ? V_60TRANS : 0), va("%dx", players[tab[i].num].lives));
else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT) else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
{ {
@ -1388,7 +1390,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
| (greycheck ? V_TRANSLUCENT : 0) | (greycheck ? V_TRANSLUCENT : 0)
| V_ALLOWLOWERCASE, name); | V_ALLOWLOWERCASE, name);
if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3))) //show lives if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != 0x7f)) //show lives
V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE, va("%dx", players[tab[i].num].lives)); V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE, va("%dx", players[tab[i].num].lives));
else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT) else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
V_DrawSmallScaledPatch(x-28, y-4, 0, tagico); V_DrawSmallScaledPatch(x-28, y-4, 0, tagico);

View File

@ -71,6 +71,7 @@ extern patch_t *rmatcico;
extern patch_t *bmatcico; extern patch_t *bmatcico;
extern patch_t *tagico; extern patch_t *tagico;
extern patch_t *tallminus; extern patch_t *tallminus;
extern patch_t *tallinfin;
extern patch_t *tokenicon; extern patch_t *tokenicon;
// set true when entering a chat message // set true when entering a chat message

View File

@ -405,6 +405,7 @@ char spr2names[NUMPLAYERSPRITES][5] =
"STND", "STND",
"WAIT", "WAIT",
"WALK", "WALK",
"SKID",
"RUN_", "RUN_",
"DASH", "DASH",
"PAIN", "PAIN",
@ -481,7 +482,89 @@ char spr2names[NUMPLAYERSPRITES][5] =
"SIGN", "SIGN",
"LIFE" "LIFE"
}; };
enum playersprite free_spr2 = SPR2_FIRSTFREESLOT; playersprite_t free_spr2 = SPR2_FIRSTFREESLOT;
playersprite_t spr2defaults[NUMPLAYERSPRITES] = {
0, // SPR2_STND,
0, // SPR2_WAIT,
0, // SPR2_WALK,
SPR2_WALK, // SPR2_SKID,
SPR2_WALK, // SPR2_RUN ,
SPR2_FRUN, // SPR2_DASH,
0, // SPR2_PAIN,
SPR2_PAIN, // SPR2_STUN,
SPR2_PAIN, // SPR2_DEAD,
SPR2_DEAD, // SPR2_DRWN,
0, // SPR2_ROLL,
SPR2_SPNG, // SPR2_GASP,
0, // SPR2_JUMP, (conditional)
SPR2_FALL, // SPR2_SPNG,
SPR2_WALK, // SPR2_FALL,
0, // SPR2_EDGE,
SPR2_FALL, // SPR2_RIDE,
SPR2_ROLL, // SPR2_SPIN,
SPR2_SPNG, // SPR2_FLY ,
SPR2_FLY , // SPR2_SWIM,
0, // SPR2_TIRE, (conditional)
SPR2_FLY , // SPR2_GLID,
SPR2_CLMB, // SPR2_CLNG,
SPR2_ROLL, // SPR2_CLMB,
SPR2_WALK, // SPR2_FLT ,
SPR2_RUN , // SPR2_FRUN,
SPR2_FALL, // SPR2_BNCE,
SPR2_ROLL, // SPR2_BLND,
0, // SPR2_FIRE,
SPR2_ROLL, // SPR2_TWIN,
SPR2_TWIN, // SPR2_MLEE,
0, // SPR2_MLEL,
0, // SPR2_TRNS,
FF_SPR2SUPER|SPR2_STND, // SPR2_NSTD,
FF_SPR2SUPER|SPR2_FLT , // SPR2_NFLT,
FF_SPR2SUPER|SPR2_STUN, // SPR2_NSTN,
SPR2_NSTN, // SPR2_NPUL,
FF_SPR2SUPER|SPR2_ROLL, // SPR2_NATK,
0, // SPR2_NGT0, (should never be referenced)
SPR2_NGT0, // SPR2_NGT1,
SPR2_NGT1, // SPR2_NGT2,
SPR2_NGT2, // SPR2_NGT3,
SPR2_NGT3, // SPR2_NGT4,
SPR2_NGT4, // SPR2_NGT5,
SPR2_NGT5, // SPR2_NGT6,
SPR2_NGT0, // SPR2_NGT7,
SPR2_NGT7, // SPR2_NGT8,
SPR2_NGT8, // SPR2_NGT9,
SPR2_NGT9, // SPR2_NGTA,
SPR2_NGTA, // SPR2_NGTB,
SPR2_NGTB, // SPR2_NGTC,
SPR2_NGT0, // SPR2_DRL0,
SPR2_NGT1, // SPR2_DRL1,
SPR2_NGT2, // SPR2_DRL2,
SPR2_NGT3, // SPR2_DRL3,
SPR2_NGT4, // SPR2_DRL4,
SPR2_NGT5, // SPR2_DRL5,
SPR2_NGT6, // SPR2_DRL6,
SPR2_NGT7, // SPR2_DRL7,
SPR2_NGT8, // SPR2_DRL8,
SPR2_NGT9, // SPR2_DRL9,
SPR2_NGTA, // SPR2_DRLA,
SPR2_NGTB, // SPR2_DRLB,
SPR2_NGTC, // SPR2_DRLC,
0, // SPR2_SIGN,
0, // SPR2_LIFE
};
// Doesn't work with g++, needs actionf_p1 (don't modify this comment) // Doesn't work with g++, needs actionf_p1 (don't modify this comment)
state_t states[NUMSTATES] = state_t states[NUMSTATES] =
@ -512,6 +595,7 @@ state_t states[NUMSTATES] =
{SPR_PLAY, SPR2_STND|FF_ANIMATE, 105, {NULL}, 0, 7, S_PLAY_WAIT}, // S_PLAY_STND {SPR_PLAY, SPR2_STND|FF_ANIMATE, 105, {NULL}, 0, 7, S_PLAY_WAIT}, // S_PLAY_STND
{SPR_PLAY, SPR2_WAIT|FF_ANIMATE, -1, {NULL}, 0, 16, S_NULL}, // S_PLAY_WAIT {SPR_PLAY, SPR2_WAIT|FF_ANIMATE, -1, {NULL}, 0, 16, S_NULL}, // S_PLAY_WAIT
{SPR_PLAY, SPR2_WALK, 4, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_WALK {SPR_PLAY, SPR2_WALK, 4, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_WALK
{SPR_PLAY, SPR2_SKID, 1, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_SKID
{SPR_PLAY, SPR2_RUN , 2, {NULL}, 0, 0, S_PLAY_RUN}, // S_PLAY_RUN {SPR_PLAY, SPR2_RUN , 2, {NULL}, 0, 0, S_PLAY_RUN}, // S_PLAY_RUN
{SPR_PLAY, SPR2_DASH, 2, {NULL}, 0, 0, S_PLAY_DASH}, // S_PLAY_DASH {SPR_PLAY, SPR2_DASH, 2, {NULL}, 0, 0, S_PLAY_DASH}, // S_PLAY_DASH
{SPR_PLAY, SPR2_PAIN|FF_ANIMATE, 350, {NULL}, 0, 4, S_PLAY_FALL}, // S_PLAY_PAIN {SPR_PLAY, SPR2_PAIN|FF_ANIMATE, 350, {NULL}, 0, 4, S_PLAY_FALL}, // S_PLAY_PAIN

View File

@ -604,11 +604,12 @@ typedef enum sprite
// Make sure to be conscious of FF_FRAMEMASK and the fact sprite2 is stored as a UINT8 whenever you change this table. // Make sure to be conscious of FF_FRAMEMASK and the fact sprite2 is stored as a UINT8 whenever you change this table.
// Currently, FF_FRAMEMASK is 0xff, or 255 - but the second half is used by FF_SPR2SUPER, so the limitation is 0x7f. // Currently, FF_FRAMEMASK is 0xff, or 255 - but the second half is used by FF_SPR2SUPER, so the limitation is 0x7f.
// Since this is zero-based, there can be at most 128 different SPR2_'s without changing that. // Since this is zero-based, there can be at most 128 different SPR2_'s without changing that.
enum playersprite typedef enum playersprite
{ {
SPR2_STND = 0, SPR2_STND = 0,
SPR2_WAIT, SPR2_WAIT,
SPR2_WALK, SPR2_WALK,
SPR2_SKID,
SPR2_RUN , SPR2_RUN ,
SPR2_DASH, SPR2_DASH,
SPR2_PAIN, SPR2_PAIN,
@ -690,7 +691,7 @@ enum playersprite
SPR2_FIRSTFREESLOT, SPR2_FIRSTFREESLOT,
SPR2_LASTFREESLOT = 0x7f, SPR2_LASTFREESLOT = 0x7f,
NUMPLAYERSPRITES NUMPLAYERSPRITES
}; } playersprite_t;
typedef enum state typedef enum state
{ {
@ -713,6 +714,7 @@ typedef enum state
S_PLAY_STND, S_PLAY_STND,
S_PLAY_WAIT, S_PLAY_WAIT,
S_PLAY_WALK, S_PLAY_WALK,
S_PLAY_SKID,
S_PLAY_RUN, S_PLAY_RUN,
S_PLAY_DASH, S_PLAY_DASH,
S_PLAY_PAIN, S_PLAY_PAIN,
@ -3193,8 +3195,9 @@ typedef struct
extern state_t states[NUMSTATES]; extern state_t states[NUMSTATES];
extern char sprnames[NUMSPRITES + 1][5]; extern char sprnames[NUMSPRITES + 1][5];
extern char spr2names[NUMPLAYERSPRITES][5]; extern char spr2names[NUMPLAYERSPRITES][5];
extern playersprite_t spr2defaults[NUMPLAYERSPRITES];
extern state_t *astate; extern state_t *astate;
extern enum playersprite free_spr2; extern playersprite_t free_spr2;
typedef enum mobj_type typedef enum mobj_type
{ {

View File

@ -462,6 +462,8 @@ static int lib_pSpawnLockOn(lua_State *L)
return LUA_ErrInvalid(L, "mobj_t"); return LUA_ErrInvalid(L, "mobj_t");
if (!player) if (!player)
return LUA_ErrInvalid(L, "player_t"); return LUA_ErrInvalid(L, "player_t");
if (state >= NUMSTATES)
return luaL_error(L, "state %d out of range (0 - %d)", state, NUMSTATES-1);
if (P_IsLocalPlayer(player)) // Only display it on your own view. if (P_IsLocalPlayer(player)) // Only display it on your own view.
{ {
mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker

View File

@ -42,3 +42,4 @@ boolean LUA_HudEnabled(enum hud option);
void LUAh_GameHUD(player_t *stplyr); void LUAh_GameHUD(player_t *stplyr);
void LUAh_ScoresHUD(void); void LUAh_ScoresHUD(void);
void LUAh_TitleHUD(void);

View File

@ -88,11 +88,13 @@ static const char *const patch_opt[] = {
enum hudhook { enum hudhook {
hudhook_game = 0, hudhook_game = 0,
hudhook_scores hudhook_scores,
hudhook_title
}; };
static const char *const hudhook_opt[] = { static const char *const hudhook_opt[] = {
"game", "game",
"scores", "scores",
"title",
NULL}; NULL};
// alignment types for v.drawString // alignment types for v.drawString
@ -808,6 +810,9 @@ int LUA_HudLib(lua_State *L)
lua_newtable(L); lua_newtable(L);
lua_rawseti(L, -2, 3); // HUD[2] = scores rendering functions array lua_rawseti(L, -2, 3); // HUD[2] = scores rendering functions array
lua_newtable(L);
lua_rawseti(L, -2, 4); // HUD[3] = title rendering functions array
lua_setfield(L, LUA_REGISTRYINDEX, "HUD"); lua_setfield(L, LUA_REGISTRYINDEX, "HUD");
luaL_newmetatable(L, META_HUDINFO); luaL_newmetatable(L, META_HUDINFO);
@ -920,4 +925,29 @@ void LUAh_ScoresHUD(void)
hud_running = false; hud_running = false;
} }
void LUAh_TitleHUD(void)
{
if (!gL || !(hudAvailable & (1<<hudhook_title)))
return;
hud_running = true;
lua_pop(gL, -1);
lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -1, 4); // HUD[4] = rendering funcs
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
I_Assert(lua_istable(gL, -1));
lua_remove(gL, -3); // pop HUD
lua_pushnil(gL);
while (lua_next(gL, -3) != 0) {
lua_pushvalue(gL, -3); // graphics library (HUD[1])
LUA_Call(gL, 1);
}
lua_pop(gL, -1);
hud_running = false;
}
#endif #endif

View File

@ -127,6 +127,74 @@ static int lib_getSpr2name(lua_State *L)
return 0; return 0;
} }
static int lib_getSpr2default(lua_State *L)
{
UINT32 i;
lua_remove(L, 1); // don't care about spr2defaults[] dummy userdata.
if (lua_isnumber(L, 1))
i = lua_tonumber(L, 1);
else if (lua_isstring(L, 1))
{
const char *name = lua_tostring(L, 1);
for (i = 0; i < free_spr2; i++)
if (fastcmp(name, spr2names[i]))
break;
}
else
return luaL_error(L, "spr2defaults[] invalid index");
if (i >= free_spr2)
return 0;
lua_pushinteger(L, spr2defaults[i]);
return 1;
}
static int lib_setSpr2default(lua_State *L)
{
UINT32 i;
UINT8 j = 0;
lua_remove(L, 1); // don't care about spr2defaults[] dummy userdata.
if (lua_isnumber(L, 1))
i = lua_tonumber(L, 1);
else if (lua_isstring(L, 1))
{
const char *name = lua_tostring(L, 1);
for (i = 0; i < free_spr2; i++)
if (fastcmp(name, spr2names[i]))
break;
if (i == free_spr2)
return luaL_error(L, "spr2defaults[] invalid index");
}
else
return luaL_error(L, "spr2defaults[] invalid index");
if (i < SPR2_FIRSTFREESLOT || i >= free_spr2)
return luaL_error(L, "spr2defaults[] index %d out of range (%d - %d)", i, SPR2_FIRSTFREESLOT, free_spr2-1);
if (lua_isnumber(L, 2))
j = lua_tonumber(L, 2);
else if (lua_isstring(L, 2))
{
const char *name = lua_tostring(L, 2);
for (j = 0; j < free_spr2; j++)
if (fastcmp(name, spr2names[j]))
break;
if (j == free_spr2)
return luaL_error(L, "spr2defaults[] invalid index");
}
if (j >= free_spr2)
j = 0; // return luaL_error(L, "spr2defaults[] set %d out of range (%d - %d)", j, 0, free_spr2-1);
spr2defaults[i] = j;
return 0;
}
static int lib_spr2namelen(lua_State *L) static int lib_spr2namelen(lua_State *L)
{ {
lua_pushinteger(L, free_spr2); lua_pushinteger(L, free_spr2);
@ -984,6 +1052,19 @@ int LUA_InfoLib(lua_State *L)
lua_setmetatable(L, -2); lua_setmetatable(L, -2);
lua_setglobal(L, "spr2names"); lua_setglobal(L, "spr2names");
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getSpr2default);
lua_setfield(L, -2, "__index");
lua_pushcfunction(L, lib_setSpr2default);
lua_setfield(L, -2, "__newindex");
lua_pushcfunction(L, lib_spr2namelen);
lua_setfield(L, -2, "__len");
lua_setmetatable(L, -2);
lua_setglobal(L, "spr2defaults");
lua_newuserdata(L, 0); lua_newuserdata(L, 0);
lua_createtable(L, 0, 2); lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getState); lua_pushcfunction(L, lib_getState);

View File

@ -126,6 +126,8 @@ static int player_get(lua_State *L)
lua_pushfixed(L, plr->bob); lua_pushfixed(L, plr->bob);
else if (fastcmp(field,"aiming")) else if (fastcmp(field,"aiming"))
lua_pushangle(L, plr->aiming); lua_pushangle(L, plr->aiming);
else if (fastcmp(field,"drawangle"))
lua_pushangle(L, plr->drawangle);
else if (fastcmp(field,"rings")) else if (fastcmp(field,"rings"))
lua_pushinteger(L, plr->rings); lua_pushinteger(L, plr->rings);
else if (fastcmp(field,"pity")) else if (fastcmp(field,"pity"))
@ -386,6 +388,8 @@ static int player_set(lua_State *L)
else if (plr == &players[secondarydisplayplayer]) else if (plr == &players[secondarydisplayplayer])
localaiming2 = plr->aiming; localaiming2 = plr->aiming;
} }
else if (fastcmp(field,"drawangle"))
plr->drawangle = luaL_checkangle(L, 3);
else if (fastcmp(field,"rings")) else if (fastcmp(field,"rings"))
plr->rings = (INT32)luaL_checkinteger(L, 3); plr->rings = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"pity")) else if (fastcmp(field,"pity"))

View File

@ -61,7 +61,7 @@ static UINT8 cheatf_ultimate(void)
if (menuactive && (currentMenu != &MainDef && currentMenu != &SP_LoadDef)) if (menuactive && (currentMenu != &MainDef && currentMenu != &SP_LoadDef))
return 0; // Only on the main menu, or the save select! return 0; // Only on the main menu, or the save select!
S_StartSound(0, sfx_itemup); BwehHehHe();
ultimate_selectable = (!ultimate_selectable); ultimate_selectable = (!ultimate_selectable);
// If on the save select, move to what is now Ultimate Mode! // If on the save select, move to what is now Ultimate Mode!
@ -1097,7 +1097,7 @@ void OP_ObjectplaceMovement(player_t *player)
ticcmd_t *cmd = &player->cmd; ticcmd_t *cmd = &player->cmd;
if (!player->climbing && (netgame || !cv_analog.value || (player->pflags & PF_SPINNING))) if (!player->climbing && (netgame || !cv_analog.value || (player->pflags & PF_SPINNING)))
player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */); player->drawangle = player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
ticruned++; ticruned++;
if (!(cmd->angleturn & TICCMD_RECEIVED)) if (!(cmd->angleturn & TICCMD_RECEIVED))

View File

@ -172,7 +172,9 @@ static char joystickInfo[8][25];
static UINT32 serverlistpage; static UINT32 serverlistpage;
#endif #endif
static saveinfo_t savegameinfo[MAXSAVEGAMES]; // Extra info about the save games. static UINT8 numsaves = 0;
static saveinfo_t* savegameinfo = NULL; // Extra info about the save games.
static patch_t *savselp[7];
INT16 startmap; // Mario, NiGHTS, or just a plain old normal game? INT16 startmap; // Mario, NiGHTS, or just a plain old normal game?
@ -222,7 +224,7 @@ static INT32 lsoffs[2];
#define lshli levelselectselect[2] #define lshli levelselectselect[2]
#define lshseperation 101 #define lshseperation 101
#define lsbasevseperation 62 #define lsbasevseperation (62*vid.height)/(BASEVIDHEIGHT*vid.dupy) //62
#define lsheadingheight 16 #define lsheadingheight 16
#define getheadingoffset(row) (levelselect.rows[row].header[0] ? lsheadingheight : 0) #define getheadingoffset(row) (levelselect.rows[row].header[0] ? lsheadingheight : 0)
#define lsvseperation(row) lsbasevseperation + getheadingoffset(row) #define lsvseperation(row) lsbasevseperation + getheadingoffset(row)
@ -1047,7 +1049,9 @@ static menuitem_t OP_P1ControlsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam , 60}, {IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam , 60},
{IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair, 70}, {IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair, 70},
{IT_STRING | IT_CVAR, NULL, "Analog Control", &cv_useranalog, 90}, //{IT_STRING | IT_CVAR, NULL, "Analog Control", &cv_useranalog, 90},
{IT_STRING | IT_CVAR, NULL, "Character angle", &cv_directionchar, 90},
{IT_STRING | IT_CVAR, NULL, "Automatic braking", &cv_autobrake, 100},
}; };
static menuitem_t OP_P2ControlsMenu[] = static menuitem_t OP_P2ControlsMenu[] =
@ -1060,7 +1064,9 @@ static menuitem_t OP_P2ControlsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam2 , 60}, {IT_STRING | IT_CVAR, NULL, "Flip Camera with Gravity" , &cv_flipcam2 , 60},
{IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair2, 70}, {IT_STRING | IT_CVAR, NULL, "Crosshair", &cv_crosshair2, 70},
{IT_STRING | IT_CVAR, NULL, "Analog Control", &cv_useranalog2, 90}, //{IT_STRING | IT_CVAR, NULL, "Analog Control", &cv_useranalog2, 90},
{IT_STRING | IT_CVAR, NULL, "Character angle", &cv_directionchar2, 90},
{IT_STRING | IT_CVAR, NULL, "Automatic braking", &cv_autobrake2, 100},
}; };
static menuitem_t OP_ChangeControlsMenu[] = static menuitem_t OP_ChangeControlsMenu[] =
@ -2545,7 +2551,7 @@ boolean M_Responder(event_t *ev)
{ {
if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && modifiedgame && !savemoddata) if (((currentMenu->menuitems[itemOn].status & IT_CALLTYPE) & IT_CALL_NOTMODIFIED) && modifiedgame && !savemoddata)
{ {
S_StartSound(NULL, sfx_menu1); S_StartSound(NULL, sfx_skid);
M_StartMessage(M_GetText("This cannot be done in a modified game.\n\n(Press a key)\n"), NULL, MM_NOTHING); M_StartMessage(M_GetText("This cannot be done in a modified game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
return true; return true;
} }
@ -4047,10 +4053,10 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
if (actnum) if (actnum)
sprintf(mapname, "%s %d", mapheaderinfo[mapnum]->lvlttl, actnum); sprintf(mapname, "%s %d", mapheaderinfo[mapnum]->lvlttl, actnum);
else else
sprintf(mapname, "%s", mapheaderinfo[mapnum]->lvlttl); strcpy(mapname, mapheaderinfo[mapnum]->lvlttl);
if (strlen(mapname) >= 17) if (strlen(mapname) >= 17)
sprintf(mapname+17-3, "..."); strcpy(mapname+17-3, "...");
strcpy(levelselect.rows[row].mapnames[col], (const char *)mapname); strcpy(levelselect.rows[row].mapnames[col], (const char *)mapname);
} }
@ -4079,6 +4085,24 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick)
mapnum++; mapnum++;
} }
#ifdef SYMMETRICAL_PLATTER
// horizontally space out rows with missing right sides
for (; row >= 0; row--)
{
if (!levelselect.rows[row].maplist[2] // no right side
&& levelselect.rows[row].maplist[0] && levelselect.rows[row].maplist[1]) // all the left filled in
{
levelselect.rows[row].maplist[2] = levelselect.rows[row].maplist[1];
STRBUFCPY(levelselect.rows[row].mapnames[2], levelselect.rows[row].mapnames[1]);
levelselect.rows[row].mapavailable[2] = levelselect.rows[row].mapavailable[1];
levelselect.rows[row].maplist[1] = -1; // diamond
levelselect.rows[row].mapnames[1][0] = '\0';
levelselect.rows[row].mapavailable[1] = false;
}
}
#endif
if (levselp[0][0]) // never going to have some provided but not all, saves individually checking if (levselp[0][0]) // never going to have some provided but not all, saves individually checking
{ {
W_UnlockCachedPatch(levselp[0][0]); W_UnlockCachedPatch(levselp[0][0]);
@ -4276,14 +4300,10 @@ void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlig
y += lsheadingheight - 12; y += lsheadingheight - 12;
V_DrawString(19, y, (headerhighlight ? V_YELLOWMAP : 0)|(allowlowercase ? V_ALLOWLOWERCASE : 0), header); V_DrawString(19, y, (headerhighlight ? V_YELLOWMAP : 0)|(allowlowercase ? V_ALLOWLOWERCASE : 0), header);
y += 9; y += 9;
if ((y >= 0) && (y < 200)) V_DrawFill(19, y, 281, 1, (headerhighlight ? yellowmap[3] : 3));
{ V_DrawFill(300, y, 1, 1, 26);
V_DrawFill(19, y, 281, 1, (headerhighlight ? yellowmap[3] : 3));
V_DrawFill(300, y, 1, 1, 26);
}
y++; y++;
if ((y >= 0) && (y < 200)) V_DrawFill(19, y, 282, 1, 26);
V_DrawFill(19, y, 282, 1, 26);
} }
static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolean highlight) static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolean highlight)
@ -4291,7 +4311,7 @@ static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, bo
patch_t *patch; patch_t *patch;
INT32 map = levelselect.rows[row].maplist[col]; INT32 map = levelselect.rows[row].maplist[col];
if (!map) if (map <= 0)
return; return;
// A 564x100 image of the level as entry MAPxxW // A 564x100 image of the level as entry MAPxxW
@ -4307,22 +4327,9 @@ static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, bo
V_DrawSmallScaledPatch(x, y, 0, patch); V_DrawSmallScaledPatch(x, y, 0, patch);
} }
if ((y+50) < 200) V_DrawFill(x, y+50, 282, 8,
{ ((mapheaderinfo[map-1]->unlockrequired < 0)
INT32 topy = (y+50), h = 8; ? 159 : 63));
if (topy < 0)
{
h += topy;
topy = 0;
}
else if (topy + h >= 200)
h = 200 - y;
if (h > 0)
V_DrawFill(x, topy, 282, h,
((mapheaderinfo[map-1]->unlockrequired < 0)
? 159 : 63));
}
V_DrawString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]); V_DrawString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]);
} }
@ -4332,7 +4339,7 @@ static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolea
patch_t *patch; patch_t *patch;
INT32 map = levelselect.rows[row].maplist[col]; INT32 map = levelselect.rows[row].maplist[col];
if (!map) if (map <= 0)
return; return;
// A 160x100 image of the level as entry MAPxxP // A 160x100 image of the level as entry MAPxxP
@ -4348,22 +4355,9 @@ static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolea
V_DrawSmallScaledPatch(x, y, 0, patch); V_DrawSmallScaledPatch(x, y, 0, patch);
} }
if ((y+50) < 200) V_DrawFill(x, y+50, 80, 8,
{ ((mapheaderinfo[map-1]->unlockrequired < 0)
INT32 topy = (y+50), h = 8; ? 159 : 63));
if (topy < 0)
{
h += topy;
topy = 0;
}
else if (topy + h >= 200)
h = 200 - y;
if (h > 0)
V_DrawFill(x, topy, 80, h,
((mapheaderinfo[map-1]->unlockrequired < 0)
? 159 : 63));
}
if (strlen(levelselect.rows[row].mapnames[col]) > 6) // "AERIAL GARDEN" vs "ACT 18" - "THE ACT" intentionally compressed if (strlen(levelselect.rows[row].mapnames[col]) > 6) // "AERIAL GARDEN" vs "ACT 18" - "THE ACT" intentionally compressed
V_DrawThinString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]); V_DrawThinString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]);
@ -4440,17 +4434,17 @@ static void M_DrawLevelPlatterMenu(void)
V_DrawSmallScaledPatch(lsbasex + cursorx + lsoffs[1], lsbasey, 0, (levselp[sizeselect][((skullAnimCounter/4) ? 1 : 0)])); V_DrawSmallScaledPatch(lsbasex + cursorx + lsoffs[1], lsbasey, 0, (levselp[sizeselect][((skullAnimCounter/4) ? 1 : 0)]));
#if 0 #if 0
if (levelselect.rows[lsrow].maplist[lscol]) if (levelselect.rows[lsrow].maplist[lscol] > 0)
V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_CACHE)); V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_CACHE));
#endif #endif
// handle movement of cursor box // handle movement of cursor box
if (abs(lsoffs[0]) > 1) if (lsoffs[0] > 1 || lsoffs[0] < -1)
lsoffs[0] = 2*lsoffs[0]/3; lsoffs[0] = 2*lsoffs[0]/3;
else else
lsoffs[0] = 0; lsoffs[0] = 0;
if (abs(lsoffs[1]) > 1) if (lsoffs[1] > 1 || lsoffs[1] < -1)
lsoffs[1] = 2*lsoffs[1]/3; lsoffs[1] = 2*lsoffs[1]/3;
else else
lsoffs[1] = 0; lsoffs[1] = 0;
@ -5452,7 +5446,7 @@ static void M_LevelSelectWarp(INT32 choice)
G_LoadGame((UINT32)cursaveslot, startmap); G_LoadGame((UINT32)cursaveslot, startmap);
else else
{ {
cursaveslot = -1; cursaveslot = 0;
M_SetupChoosePlayer(0); M_SetupChoosePlayer(0);
} }
} }
@ -6027,7 +6021,7 @@ static void M_CustomWarp(INT32 choice)
static void M_Credits(INT32 choice) static void M_Credits(INT32 choice)
{ {
(void)choice; (void)choice;
cursaveslot = -2; cursaveslot = -1;
M_ClearMenus(true); M_ClearMenus(true);
F_StartCredits(); F_StartCredits();
} }
@ -6085,149 +6079,285 @@ static void M_LoadGameLevelSelect(INT32 choice)
// LOAD GAME MENU // LOAD GAME MENU
// ============== // ==============
static INT32 saveSlotSelected = 0; static INT32 saveSlotSelected = 1;
static short menumovedir = 0; static INT32 loadgamescroll = 0;
static UINT8 loadgameoffset = 0;
static void M_DrawLoadGameData(void) static void M_DrawLoadGameData(void)
{ {
INT32 ecks; INT32 i, savetodraw, x, y, hsep = 90;
INT32 i; skin_t *charskin = NULL;
ecks = SP_LoadDef.x + 24; if (vid.width != BASEVIDWIDTH*vid.dupx)
M_DrawTextBox(SP_LoadDef.x-12,144, 24, 4); hsep = (hsep*vid.width)/(BASEVIDWIDTH*vid.dupx);
if (saveSlotSelected == NOSAVESLOT) // last slot is play without saving for (i = -2; i <= 2; i++)
{ {
if (ultimate_selectable) savetodraw = (saveSlotSelected + i + numsaves)%numsaves;
x = (BASEVIDWIDTH/2 - 42 + loadgamescroll) + (i*hsep);
y = 33 + 9;
{ {
V_DrawCenteredString(ecks + 68, 144, V_ORANGEMAP, "ULTIMATE MODE"); INT32 diff = x - (BASEVIDWIDTH/2 - 42);
V_DrawCenteredString(ecks + 68, 156, 0, "NO RINGS, NO ONE-UPS,"); if (diff < 0)
V_DrawCenteredString(ecks + 68, 164, 0, "NO CONTINUES, ONE LIFE,"); diff = -diff;
V_DrawCenteredString(ecks + 68, 172, 0, "FINAL DESTINATION."); diff = (42 - diff)/3 - loadgameoffset;
if (diff < 0)
diff = 0;
y -= diff;
} }
else
{ if (savetodraw == 0)
V_DrawCenteredString(ecks + 68, 144, V_ORANGEMAP, "PLAY WITHOUT SAVING");
V_DrawCenteredString(ecks + 68, 156, 0, "THIS GAME WILL NOT BE");
V_DrawCenteredString(ecks + 68, 164, 0, "SAVED, BUT YOU CAN STILL");
V_DrawCenteredString(ecks + 68, 172, 0, "GET EMBLEMS AND SECRETS.");
}
return;
}
if (savegameinfo[saveSlotSelected].lives == -42) // Empty
{
V_DrawCenteredString(ecks + 68, 160, 0, "NO DATA");
return;
}
if (savegameinfo[saveSlotSelected].lives == -666) // savegame is bad
{
V_DrawCenteredString(ecks + 68, 144, V_REDMAP, "CORRUPT SAVE FILE");
V_DrawCenteredString(ecks + 68, 156, 0, "THIS SAVE FILE");
V_DrawCenteredString(ecks + 68, 164, 0, "CAN NOT BE LOADED.");
V_DrawCenteredString(ecks + 68, 172, 0, "DELETE USING BACKSPACE.");
return;
}
// Draw the back sprite, it looks ugly if we don't
V_DrawScaledPatch(SP_LoadDef.x, 144+8, 0, livesback);
if (savegameinfo[saveSlotSelected].skincolor == 0)
V_DrawScaledPatch(SP_LoadDef.x,144+8,0,W_CachePatchName(skins[savegameinfo[saveSlotSelected].skinnum].face, PU_CACHE));
else
{
UINT8 *colormap = R_GetTranslationColormap(savegameinfo[saveSlotSelected].skinnum, savegameinfo[saveSlotSelected].skincolor, 0);
V_DrawMappedPatch(SP_LoadDef.x,144+8,0,W_CachePatchName(skins[savegameinfo[saveSlotSelected].skinnum].face, PU_CACHE), colormap);
}
V_DrawString(ecks + 12, 152, 0, savegameinfo[saveSlotSelected].playername);
#ifdef SAVEGAMES_OTHERVERSIONS
if (savegameinfo[saveSlotSelected].gamemap & 16384)
V_DrawCenteredString(ecks + 68, 144, V_REDMAP, "OUTDATED SAVE FILE!");
#endif
if (savegameinfo[saveSlotSelected].gamemap & 8192)
V_DrawString(ecks + 12, 160, V_GREENMAP, "CLEAR!");
else
V_DrawString(ecks + 12, 160, 0, va("%s", savegameinfo[saveSlotSelected].levelname));
// Use the big face pic for lives, duh. :3
V_DrawScaledPatch(ecks + 12, 175, 0, W_CachePatchName("STLIVEX", PU_HUDGFX));
V_DrawTallNum(ecks + 40, 172, 0, savegameinfo[saveSlotSelected].lives);
// Absolute ridiculousness, condensed into another function.
V_DrawContinueIcon(ecks + 58, 182, 0, savegameinfo[saveSlotSelected].skinnum, savegameinfo[saveSlotSelected].skincolor);
V_DrawScaledPatch(ecks + 68, 175, 0, W_CachePatchName("STLIVEX", PU_HUDGFX));
V_DrawTallNum(ecks + 96, 172, 0, savegameinfo[saveSlotSelected].continues);
for (i = 0; i < 7; ++i)
{
if (savegameinfo[saveSlotSelected].numemeralds & (1 << i))
V_DrawScaledPatch(ecks + 104 + (i * 8), 172, 0, tinyemeraldpics[i]);
}
}
#define LOADBARHEIGHT SP_LoadDef.y + (LINEHEIGHT * (j+1)) + ymod
#define CURSORHEIGHT SP_LoadDef.y + (LINEHEIGHT*3) - 1
static void M_DrawLoad(void)
{
INT32 i, j;
INT32 ymod = 0, offset = 0;
M_DrawMenuTitle();
if (menumovedir != 0) //movement illusion
{
ymod = (-(LINEHEIGHT/4))*menumovedir;
offset = ((menumovedir > 0) ? -1 : 1);
}
V_DrawCenteredString(BASEVIDWIDTH/2, 40, 0, "Press backspace to delete a save.");
for (i = MAXSAVEGAMES + saveSlotSelected - 2 + offset, j = 0;i <= MAXSAVEGAMES + saveSlotSelected + 2 + offset; i++, j++)
{
if ((menumovedir < 0 && j == 4) || (menumovedir > 0 && j == 0))
continue; //this helps give the illusion of movement
M_DrawSaveLoadBorder(SP_LoadDef.x, LOADBARHEIGHT);
if ((i%MAXSAVEGAMES) == NOSAVESLOT) // play without saving
{ {
V_DrawSmallScaledPatch(x, y, 0,
savselp[((ultimate_selectable) ? 2 : 1)]);
x += 2;
y += 1;
V_DrawString(x, y,
((savetodraw == saveSlotSelected) ? V_YELLOWMAP : 0),
"NO FILE");
if (savetodraw == saveSlotSelected)
V_DrawFill(x, y+9, 80, 1, yellowmap[3]);
y += 11;
V_DrawSmallScaledPatch(x, y, V_STATIC, savselp[4]);
y += 41;
if (ultimate_selectable) if (ultimate_selectable)
V_DrawCenteredString(SP_LoadDef.x+92, LOADBARHEIGHT - 1, V_ORANGEMAP, "ULTIMATE MODE"); V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "ULTIMATE.");
else else
V_DrawCenteredString(SP_LoadDef.x+92, LOADBARHEIGHT - 1, V_ORANGEMAP, "PLAY WITHOUT SAVING"); V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "DON'T SAVE!");
continue; continue;
} }
if (savegameinfo[i%MAXSAVEGAMES].lives == -42) savetodraw--;
V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, V_TRANSLUCENT, "NO DATA");
else if (savegameinfo[i%MAXSAVEGAMES].lives == -666)
V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, V_REDMAP, "CORRUPT SAVE FILE");
else if (savegameinfo[i%MAXSAVEGAMES].gamemap & 8192)
V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, V_GREENMAP, "CLEAR!");
else
V_DrawString(SP_LoadDef.x-6, LOADBARHEIGHT - 1, 0, va("%s", savegameinfo[i%MAXSAVEGAMES].levelname));
//Draw the save slot number on the right side if (savegameinfo[savetodraw].lives > 0)
V_DrawRightAlignedString(SP_LoadDef.x+192, LOADBARHEIGHT - 1, 0, va("%d",(i%MAXSAVEGAMES) + 1)); charskin = &skins[savegameinfo[savetodraw].skinnum];
// signpost background
{
UINT8 col;
if (savegameinfo[savetodraw].lives == -666)
{
V_DrawSmallScaledPatch(x+2, y+64, 0, savselp[5]);
}
#ifndef PERFECTSAVE // disabled, don't touch
else if ((savegameinfo[savetodraw].skinnum == 1)
&& (savegameinfo[savetodraw].lives == 99)
&& (savegameinfo[savetodraw].gamemap & 8192)
&& (savegameinfo[savetodraw].numgameovers == 0)
&& (savegameinfo[savetodraw].numemeralds == ((1<<7) - 1))) // perfect save
{
V_DrawFill(x+6, y+64, 72, 50, 134);
V_DrawFill(x+6, y+74, 72, 30, 201);
V_DrawFill(x+6, y+84, 72, 10, 1);
}
#endif
else
{
if (savegameinfo[savetodraw].lives == -42)
col = 26;
else if (savegameinfo[savetodraw].botskin == 3) // & knuckles
col = 105;
else if (savegameinfo[savetodraw].botskin) // tailsbot or custom
col = 134;
else
{
col = (charskin->prefcolor - 1)*2;
col = Color_Index[Color_Opposite[col]-1][Color_Opposite[col+1]];
}
V_DrawFill(x+6, y+64, 72, 50, col);
}
}
V_DrawSmallScaledPatch(x, y, 0, savselp[0]);
x += 2;
y += 1;
V_DrawString(x, y,
((savetodraw == saveSlotSelected-1) ? V_YELLOWMAP : 0),
va("FILE %d", savetodraw+1));
if (savetodraw == saveSlotSelected-1)
V_DrawFill(x, y+9, 80, 1, yellowmap[3]);
y += 11;
// level image area
{
patch_t *patch;
INT32 flags = 0;
if ((savegameinfo[savetodraw].lives == -42)
|| (savegameinfo[savetodraw].lives == -666))
{
patch = savselp[3];
flags = V_STATIC;
}
else if (savegameinfo[savetodraw].gamemap & 8192)
patch = savselp[6];
else
{
lumpnum_t lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName((savegameinfo[savetodraw].gamemap) & 8191)));
if (lumpnum != LUMPERROR)
patch = W_CachePatchNum(lumpnum, PU_CACHE);
else
patch = savselp[5];
}
V_DrawSmallScaledPatch(x, y, flags, patch);
y += 41;
if (savegameinfo[savetodraw].lives == -42)
V_DrawRightAlignedThinString(x + 79, y, V_GRAYMAP, "NEW GAME");
else if (savegameinfo[savetodraw].lives == -666)
V_DrawRightAlignedThinString(x + 79, y, V_REDMAP, "CAN'T LOAD!");
else if (savegameinfo[savetodraw].gamemap & 8192)
V_DrawRightAlignedThinString(x + 79, y, V_GREENMAP, "CLEAR!");
else
V_DrawRightAlignedThinString(x + 79, y, V_YELLOWMAP, savegameinfo[savetodraw].levelname);
}
if ((savegameinfo[savetodraw].lives == -42)
|| (savegameinfo[savetodraw].lives == -666))
continue;
y += 64;
// tiny emeralds
{
INT32 j, workx = x + 6;
for (j = 0; j < 7; ++j)
{
if (savegameinfo[savetodraw].numemeralds & (1 << j))
V_DrawScaledPatch(workx, y, 0, tinyemeraldpics[j]);
workx += 10;
}
}
y -= 13;
// character heads, lives, and continues
{
spritedef_t *sprdef;
spriteframe_t *sprframe;
patch_t *patch;
UINT8 *colormap = NULL;
INT32 tempx = (x+40)<<FRACBITS, tempy = y<<FRACBITS, flip = 0, calc;
// botskin first
if (savegameinfo[savetodraw].botskin)
{
skin_t *charbotskin = &skins[savegameinfo[savetodraw].botskin-1];
sprdef = &charbotskin->sprites[SPR2_SIGN];
if (!sprdef->numframes)
goto skipbot;
colormap = R_GetTranslationColormap(savegameinfo[savetodraw].botskin, charbotskin->prefcolor, 0);
sprframe = &sprdef->spriteframes[0];
patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
V_DrawFixedPatch(
tempx + (18<<FRACBITS),
tempy - (4<<FRACBITS),
charbotskin->highresscale,
0, patch, colormap);
Z_Free(colormap);
tempx -= (15<<FRACBITS);
flip = V_FLIP;
}
skipbot:
// signpost image
if (!charskin) // shut up compiler
goto skipsign;
sprdef = &charskin->sprites[SPR2_SIGN];
colormap = R_GetTranslationColormap(savegameinfo[savetodraw].skinnum, charskin->prefcolor, 0);
if (!sprdef->numframes)
goto skipsign;
sprframe = &sprdef->spriteframes[0];
patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
if ((calc = SHORT(patch->topoffset) - 42) > 0)
tempy += ((4+calc)<<FRACBITS);
V_DrawFixedPatch(
tempx,
tempy,
charskin->highresscale,
flip, patch, colormap);
skipsign:
y += 25;
tempx = x + 10;
if (savegameinfo[savetodraw].lives != 0x7f
&& savegameinfo[savetodraw].lives > 9)
tempx -= 4;
if (!charskin) // shut up compiler
goto skiplife;
// lives
sprdef = &charskin->sprites[SPR2_LIFE];
if (!sprdef->numframes)
goto skiplife;
sprframe = &sprdef->spriteframes[0];
patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
V_DrawFixedPatch(
(tempx + 4)<<FRACBITS,
(y + 6)<<FRACBITS,
charskin->highresscale/2,
0, patch, colormap);
skiplife:
if (colormap)
Z_Free(colormap);
patch = W_CachePatchName("STLIVEX", PU_CACHE);
V_DrawScaledPatch(tempx + 9, y + 2, 0, patch);
tempx += 16;
if (savegameinfo[savetodraw].lives == 0x7f)
V_DrawCharacter(tempx, y + 1, '\x16', false);
else
V_DrawString(tempx, y, 0, va("%d", savegameinfo[savetodraw].lives));
tempx = x + 47;
if (savegameinfo[savetodraw].continues > 9)
tempx -= 4;
// continues
if (savegameinfo[savetodraw].continues > 0)
{
V_DrawSmallScaledPatch(tempx, y, 0, W_CachePatchName("CONTSAVE", PU_CACHE));
V_DrawScaledPatch(tempx + 9, y + 2, 0, patch);
V_DrawString(tempx + 16, y, 0, va("%d", savegameinfo[savetodraw].continues));
}
else
{
V_DrawSmallScaledPatch(tempx, y, 0, W_CachePatchName("CONTNONE", PU_CACHE));
V_DrawScaledPatch(tempx + 9, y + 2, 0, W_CachePatchName("STNONEX", PU_CACHE));
V_DrawString(tempx + 16, y, V_GRAYMAP, "0");
}
}
} }
}
//Draw cursors on both sides. static void M_DrawLoad(void)
V_DrawScaledPatch( 32, CURSORHEIGHT, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); {
V_DrawScaledPatch(274, CURSORHEIGHT, 0, W_CachePatchName("M_CURSOR", PU_CACHE)); M_DrawMenuTitle();
if (loadgamescroll > 1 || loadgamescroll < -1)
loadgamescroll = 2*loadgamescroll/3;
else
loadgamescroll = 0;
if (loadgameoffset > 1)
loadgameoffset = 2*loadgameoffset/3;
else
loadgameoffset = 0;
M_DrawLoadGameData(); M_DrawLoadGameData();
//finishing the movement illusion
if (menumovedir)
menumovedir += ((menumovedir > 0) ? 1 : -1);
if (abs(menumovedir) > 3)
menumovedir = 0;
} }
#undef LOADBARHEIGHT
#undef CURSORHEIGHT
// //
// User wants to load this game // User wants to load this game
@ -6239,7 +6369,7 @@ static void M_LoadSelect(INT32 choice)
if (saveSlotSelected == NOSAVESLOT) //last slot is play without saving if (saveSlotSelected == NOSAVESLOT) //last slot is play without saving
{ {
M_NewGame(); M_NewGame();
cursaveslot = -1; cursaveslot = 0;
return; return;
} }
@ -6248,8 +6378,8 @@ static void M_LoadSelect(INT32 choice)
// This slot is empty, so start a new game here. // This slot is empty, so start a new game here.
M_NewGame(); M_NewGame();
} }
else if (savegameinfo[saveSlotSelected].gamemap & 8192) // Completed else if (savegameinfo[saveSlotSelected-1].gamemap & 8192) // Completed
M_LoadGameLevelSelect(saveSlotSelected + 1); M_LoadGameLevelSelect(0);
else else
G_LoadGame((UINT32)saveSlotSelected, 0); G_LoadGame((UINT32)saveSlotSelected, 0);
@ -6271,12 +6401,11 @@ static void M_ReadSavegameInfo(UINT32 slot)
INT32 fake; // Dummy variable INT32 fake; // Dummy variable
char temp[sizeof(timeattackfolder)]; char temp[sizeof(timeattackfolder)];
char vcheck[VERSIONSIZE]; char vcheck[VERSIONSIZE];
#ifdef SAVEGAMES_OTHERVERSIONS
boolean oldversion = false;
#endif
sprintf(savename, savegamename, slot); sprintf(savename, savegamename, slot);
slot--;
length = FIL_ReadFile(savename, &savebuffer); length = FIL_ReadFile(savename, &savebuffer);
if (length == 0) if (length == 0)
{ {
@ -6292,14 +6421,7 @@ static void M_ReadSavegameInfo(UINT32 slot)
// Version check // Version check
memset(vcheck, 0, sizeof (vcheck)); memset(vcheck, 0, sizeof (vcheck));
sprintf(vcheck, "version %d", VERSION); sprintf(vcheck, "version %d", VERSION);
if (strcmp((const char *)save_p, (const char *)vcheck)) if (strcmp((const char *)save_p, (const char *)vcheck)) BADSAVE
{
#ifdef SAVEGAMES_OTHERVERSIONS
oldversion = true;
#else
BADSAVE // Incompatible versions?
#endif
}
save_p += VERSIONSIZE; save_p += VERSIONSIZE;
// dearchive all the modifications // dearchive all the modifications
@ -6311,30 +6433,19 @@ static void M_ReadSavegameInfo(UINT32 slot)
if (((fake-1) & 8191) >= NUMMAPS) BADSAVE if (((fake-1) & 8191) >= NUMMAPS) BADSAVE
if(!mapheaderinfo[(fake-1) & 8191]) if(!mapheaderinfo[(fake-1) & 8191])
{
savegameinfo[slot].levelname[0] = '\0'; savegameinfo[slot].levelname[0] = '\0';
savegameinfo[slot].actnum = 0;
}
else else
{ {
strcpy(savegameinfo[slot].levelname, mapheaderinfo[(fake-1) & 8191]->lvlttl); strlcpy(savegameinfo[slot].levelname, mapheaderinfo[(fake-1) & 8191]->lvlttl, 17+1);
savegameinfo[slot].actnum = mapheaderinfo[(fake-1) & 8191]->actnum;
if (strlen(mapheaderinfo[(fake-1) & 8191]->lvlttl) >= 17)
strcpy(savegameinfo[slot].levelname+17-3, "...");
} }
#ifdef SAVEGAMES_OTHERVERSIONS
if (oldversion)
{
if (fake == 24) //meh, let's count old Clear! saves too
fake |= 8192;
fake |= 16384; // marker for outdated version
}
#endif
savegameinfo[slot].gamemap = fake; savegameinfo[slot].gamemap = fake;
CHECKPOS CHECKPOS
fake = READUINT16(save_p)-357; // emeralds savegameinfo[slot].numemeralds = READUINT16(save_p)-357; // emeralds
savegameinfo[slot].numemeralds = (UINT8)fake;
CHECKPOS CHECKPOS
READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to
@ -6343,39 +6454,25 @@ static void M_ReadSavegameInfo(UINT32 slot)
// P_UnArchivePlayer() // P_UnArchivePlayer()
CHECKPOS CHECKPOS
savegameinfo[slot].skincolor = READUINT8(save_p); fake = READUINT16(save_p);
CHECKPOS savegameinfo[slot].skinnum = fake & ((1<<5) - 1);
savegameinfo[slot].skinnum = READUINT8(save_p); if (savegameinfo[slot].skinnum >= numskins
|| !R_SkinUsable(-1, savegameinfo[slot].skinnum))
BADSAVE
savegameinfo[slot].botskin = fake >> 5;
if (savegameinfo[slot].botskin-1 >= numskins
|| !R_SkinUsable(-1, savegameinfo[slot].botskin-1))
BADSAVE
CHECKPOS
savegameinfo[slot].numgameovers = READUINT8(save_p); // numgameovers
CHECKPOS
savegameinfo[slot].lives = READSINT8(save_p); // lives
CHECKPOS CHECKPOS
(void)READINT32(save_p); // Score (void)READINT32(save_p); // Score
CHECKPOS
savegameinfo[slot].lives = READINT32(save_p); // lives
CHECKPOS CHECKPOS
savegameinfo[slot].continues = READINT32(save_p); // continues savegameinfo[slot].continues = READINT32(save_p); // continues
if (fake & (1<<10))
{
CHECKPOS
savegameinfo[slot].botskin = READUINT8(save_p);
if (savegameinfo[slot].botskin-1 >= numskins)
savegameinfo[slot].botskin = 0;
CHECKPOS
savegameinfo[slot].botcolor = READUINT8(save_p); // because why not.
}
else
savegameinfo[slot].botskin = 0;
if (savegameinfo[slot].botskin)
snprintf(savegameinfo[slot].playername, 32, "%s & %s",
skins[savegameinfo[slot].skinnum].realname,
skins[savegameinfo[slot].botskin-1].realname);
else
strcpy(savegameinfo[slot].playername, skins[savegameinfo[slot].skinnum].realname);
savegameinfo[slot].playername[31] = 0;
// File end marker check // File end marker check
CHECKPOS CHECKPOS
if (READUINT8(save_p) != 0x1d) BADSAVE; if (READUINT8(save_p) != 0x1d) BADSAVE;
@ -6394,23 +6491,80 @@ static void M_ReadSavegameInfo(UINT32 slot)
static void M_ReadSaveStrings(void) static void M_ReadSaveStrings(void)
{ {
FILE *handle; FILE *handle;
UINT32 i; SINT8 i;
char name[256]; char name[256];
boolean nofile[MAXSAVEGAMES-1];
SINT8 tolerance = 3; // empty slots at any time
UINT8 lastseen = 0;
for (i = 0; i < MAXSAVEGAMES; i++) loadgamescroll = 0;
loadgameoffset = 14;
for (i = 1; (i < MAXSAVEGAMES); i++) // slot 0 is no save
{ {
snprintf(name, sizeof name, savegamename, i); snprintf(name, sizeof name, savegamename, i);
name[sizeof name - 1] = '\0'; name[sizeof name - 1] = '\0';
handle = fopen(name, "rb"); handle = fopen(name, "rb");
if (handle == NULL) if ((nofile[i-1] = (handle == NULL)))
continue;
fclose(handle);
lastseen = i;
}
if (savegameinfo)
Z_Free(savegameinfo);
savegameinfo = NULL;
if (lastseen < saveSlotSelected)
lastseen = saveSlotSelected;
i = lastseen;
for (; (lastseen > 0 && tolerance); lastseen--)
{
if (nofile[lastseen-1])
tolerance--;
}
if ((i += tolerance+1) > MAXSAVEGAMES) // show 3 empty slots at minimum
i = MAXSAVEGAMES;
numsaves = i;
savegameinfo = Z_Realloc(savegameinfo, numsaves*sizeof(saveinfo_t), PU_STATIC, NULL);
if (!savegameinfo)
I_Error("Insufficient memory to prepare save platter");
for (; i > 0; i--)
{
if (nofile[i-1] == true)
{ {
savegameinfo[i].lives = -42; savegameinfo[i-1].lives = -42;
continue; continue;
} }
fclose(handle);
M_ReadSavegameInfo(i); M_ReadSavegameInfo(i);
} }
if (savselp[0]) // never going to have some provided but not all, saves individually checking
{
W_UnlockCachedPatch(savselp[0]);
W_UnlockCachedPatch(savselp[1]);
W_UnlockCachedPatch(savselp[2]);
W_UnlockCachedPatch(savselp[3]);
W_UnlockCachedPatch(savselp[4]);
W_UnlockCachedPatch(savselp[5]);
W_UnlockCachedPatch(savselp[6]);
}
savselp[0] = W_CachePatchName("SAVEBACK", PU_STATIC);
savselp[1] = W_CachePatchName("SAVENONE", PU_STATIC);
savselp[2] = W_CachePatchName("ULTIMATE", PU_STATIC);
savselp[3] = W_CachePatchName("BLACKLVL", PU_STATIC);
savselp[4] = W_CachePatchName("BLACXLVL", PU_STATIC);
savselp[5] = W_CachePatchName("BLANKLVL", PU_STATIC);
savselp[6] = W_CachePatchName("GAMEDONE", PU_STATIC);
} }
// //
@ -6428,8 +6582,19 @@ static void M_SaveGameDeleteResponse(INT32 ch)
name[sizeof name - 1] = '\0'; name[sizeof name - 1] = '\0';
remove(name); remove(name);
// Refresh savegame menu info BwehHehHe();
M_ReadSaveStrings(); M_ReadSaveStrings(); // reload the menu
}
static void M_SaveGameUltimateResponse(INT32 ch)
{
if (ch != 'y' && ch != KEY_ENTER)
return;
S_StartSound(NULL, sfx_menu1);
M_LoadSelect(saveSlotSelected);
SP_PlayerDef.prevMenu = MessageDef.prevMenu;
MessageDef.prevMenu = &SP_PlayerDef;
} }
static void M_HandleLoadSave(INT32 choice) static void M_HandleLoadSave(INT32 choice)
@ -6438,26 +6603,46 @@ static void M_HandleLoadSave(INT32 choice)
switch (choice) switch (choice)
{ {
case KEY_DOWNARROW: case KEY_RIGHTARROW:
S_StartSound(NULL, sfx_menu1); S_StartSound(NULL, sfx_s3kb7);
++saveSlotSelected; ++saveSlotSelected;
if (saveSlotSelected >= MAXSAVEGAMES) if (saveSlotSelected >= numsaves)
saveSlotSelected -= MAXSAVEGAMES; saveSlotSelected -= numsaves;
menumovedir = 1; loadgamescroll = 90;
break; break;
case KEY_UPARROW: case KEY_LEFTARROW:
S_StartSound(NULL, sfx_menu1); S_StartSound(NULL, sfx_s3kb7);
--saveSlotSelected; --saveSlotSelected;
if (saveSlotSelected < 0) if (saveSlotSelected < 0)
saveSlotSelected += MAXSAVEGAMES; saveSlotSelected += numsaves;
menumovedir = -1; loadgamescroll = -90;
break; break;
case KEY_ENTER: case KEY_ENTER:
S_StartSound(NULL, sfx_menu1); if (ultimate_selectable && saveSlotSelected == NOSAVESLOT)
if (savegameinfo[saveSlotSelected].lives != -666) // don't allow loading of "bad saves" {
loadgamescroll = 0;
S_StartSound(NULL, sfx_skid);
M_StartMessage("Are you sure you want to play\n\x85ultimate mode\x80? It isn't remotely fair,\nand you don't even get an emblem for it.\n\n(Press 'Y' to confirm)\n",M_SaveGameUltimateResponse,MM_YESNO);
}
else if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected-1].lives == -42 && !(!modifiedgame || savemoddata))
{
loadgamescroll = 0;
S_StartSound(NULL, sfx_skid);
M_StartMessage(M_GetText("This cannot be done in a modified game.\n\n(Press a key)\n"), NULL, MM_NOTHING);
}
else if (saveSlotSelected == NOSAVESLOT || savegameinfo[saveSlotSelected-1].lives != -666) // don't allow loading of "bad saves"
{
loadgamescroll = 0;
S_StartSound(NULL, sfx_menu1);
M_LoadSelect(saveSlotSelected); M_LoadSelect(saveSlotSelected);
}
else if (!loadgameoffset)
{
S_StartSound(NULL, sfx_lose);
loadgameoffset = 14;
}
break; break;
case KEY_ESCAPE: case KEY_ESCAPE:
@ -6465,11 +6650,25 @@ static void M_HandleLoadSave(INT32 choice)
break; break;
case KEY_BACKSPACE: case KEY_BACKSPACE:
S_StartSound(NULL, sfx_menu1);
// Don't allow people to 'delete' "Play without Saving." // Don't allow people to 'delete' "Play without Saving."
// Nor allow people to 'delete' slots with no saves in them. // Nor allow people to 'delete' slots with no saves in them.
if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected].lives != -42) if (saveSlotSelected != NOSAVESLOT && savegameinfo[saveSlotSelected-1].lives != -42)
M_StartMessage(M_GetText("Are you sure you want to delete\nthis save game?\n\n(Press 'Y' to confirm)\n"),M_SaveGameDeleteResponse,MM_YESNO); {
loadgamescroll = 0;
S_StartSound(NULL, sfx_skid);
M_StartMessage(va("Are you sure you want to delete\nsave file %d?\n\n(Press 'Y' to confirm)\n", saveSlotSelected),M_SaveGameDeleteResponse,MM_YESNO);
}
else if (!loadgameoffset)
{
if (saveSlotSelected == NOSAVESLOT && ultimate_selectable)
{
ultimate_selectable = false;
S_StartSound(NULL, sfx_strpst);
}
else
S_StartSound(NULL, sfx_lose);
loadgameoffset = 14;
}
break; break;
} }
if (exitmenu) if (exitmenu)
@ -6478,6 +6677,8 @@ static void M_HandleLoadSave(INT32 choice)
M_SetupNextMenu(currentMenu->prevMenu); M_SetupNextMenu(currentMenu->prevMenu);
else else
M_ClearMenus(true); M_ClearMenus(true);
Z_Free(savegameinfo);
savegameinfo = NULL;
} }
} }
@ -6497,14 +6698,15 @@ static void M_LoadGame(INT32 choice)
// //
void M_ForceSaveSlotSelected(INT32 sslot) void M_ForceSaveSlotSelected(INT32 sslot)
{ {
// Already there? Out of bounds? Whatever, then! loadgameoffset = 14;
if (sslot == saveSlotSelected || sslot >= MAXSAVEGAMES)
// Already there? Whatever, then!
if (sslot == saveSlotSelected)
return; return;
// Figure out whether to display up movement or down movement loadgamescroll = 90;
menumovedir = (saveSlotSelected - sslot) > 0 ? -1 : 1; if (saveSlotSelected <= numsaves/2)
if (abs(saveSlotSelected - sslot) > (MAXSAVEGAMES>>1)) loadgamescroll = -loadgamescroll;
menumovedir *= -1;
saveSlotSelected = sslot; saveSlotSelected = sslot;
} }
@ -6775,7 +6977,7 @@ static void M_ChoosePlayer(INT32 choice)
} }
if (startmap != spstage_start) if (startmap != spstage_start)
cursaveslot = -1; cursaveslot = 0;
//lastmapsaved = 0; //lastmapsaved = 0;
gamecomplete = false; gamecomplete = false;
@ -6786,6 +6988,10 @@ static void M_ChoosePlayer(INT32 choice)
if (levelselect.rows) if (levelselect.rows)
Z_Free(levelselect.rows); Z_Free(levelselect.rows);
levelselect.rows = NULL; levelselect.rows = NULL;
if (savegameinfo)
Z_Free(savegameinfo);
savegameinfo = NULL;
} }
// =============== // ===============
@ -8719,7 +8925,7 @@ static void M_EraseDataResponse(INT32 ch)
totalplaytime = 0; totalplaytime = 0;
F_StartIntro(); F_StartIntro();
} }
S_StartSound(NULL, sfx_bewar1+M_RandomKey(4)); // Bweh heh he BwehHehHe();
M_ClearMenus(true); M_ClearMenus(true);
} }

View File

@ -216,18 +216,14 @@ typedef struct
// savegame struct for save game menu // savegame struct for save game menu
typedef struct typedef struct
{ {
char playername[32];
char levelname[32]; char levelname[32];
UINT8 actnum;
UINT8 skincolor;
UINT8 skinnum; UINT8 skinnum;
UINT8 botskin; UINT8 botskin;
UINT8 botcolor;
UINT8 numemeralds; UINT8 numemeralds;
UINT8 numgameovers;
INT32 lives; INT32 lives;
INT32 continues; INT32 continues;
INT32 gamemap; INT32 gamemap;
UINT8 netgame;
} saveinfo_t; } saveinfo_t;
extern description_t description[32]; extern description_t description[32];
@ -239,7 +235,9 @@ extern INT16 startmap;
extern INT32 ultimate_selectable; extern INT32 ultimate_selectable;
#define MAXSAVEGAMES 31 //note: last save game is "no save" #define MAXSAVEGAMES 31 //note: last save game is "no save"
#define NOSAVESLOT MAXSAVEGAMES-1 //slot where Play Without Saving appears #define NOSAVESLOT 0 //slot where Play Without Saving appears
#define BwehHehHe() S_StartSound(NULL, sfx_bewar1+M_RandomKey(4)) // Bweh heh he
void M_ForceSaveSlotSelected(INT32 sslot); void M_ForceSaveSlotSelected(INT32 sslot);

View File

@ -4644,6 +4644,7 @@ void A_CapeChase(mobj_t *actor)
fixed_t foffsetx, foffsety, boffsetx, boffsety; fixed_t foffsetx, foffsety, boffsetx, boffsety;
INT32 locvar1 = var1; INT32 locvar1 = var1;
INT32 locvar2 = var2; INT32 locvar2 = var2;
angle_t angle;
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (LUA_CallAction("A_CapeChase", actor)) if (LUA_CallAction("A_CapeChase", actor))
return; return;
@ -4665,11 +4666,13 @@ void A_CapeChase(mobj_t *actor)
return; return;
} }
foffsetx = P_ReturnThrustX(chaser, chaser->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale)); angle = (chaser->player ? chaser->player->drawangle : chaser->angle);
foffsety = P_ReturnThrustY(chaser, chaser->angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
boffsetx = P_ReturnThrustX(chaser, chaser->angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale)); foffsetx = P_ReturnThrustX(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
boffsety = P_ReturnThrustY(chaser, chaser->angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale)); foffsety = P_ReturnThrustY(chaser, angle, FixedMul((locvar2 >> 16)*FRACUNIT, actor->scale));
boffsetx = P_ReturnThrustX(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
boffsety = P_ReturnThrustY(chaser, angle-ANGLE_90, FixedMul((locvar2 & 65535)*FRACUNIT, actor->scale));
P_UnsetThingPosition(actor); P_UnsetThingPosition(actor);
actor->x = chaser->x + foffsetx + boffsetx; actor->x = chaser->x + foffsetx + boffsetx;
@ -4686,7 +4689,7 @@ void A_CapeChase(mobj_t *actor)
actor->flags2 &= ~MF2_OBJECTFLIP; actor->flags2 &= ~MF2_OBJECTFLIP;
actor->z = chaser->z + FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale); actor->z = chaser->z + FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale);
} }
actor->angle = chaser->angle; actor->angle = angle;
P_SetThingPosition(actor); P_SetThingPosition(actor);
} }

View File

@ -1532,30 +1532,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
P_SetMobjState(special, special->info->deathstate); P_SetMobjState(special, special->info->deathstate);
return; return;
case MT_SPECIALSPIKEBALL: case MT_SPECIALSPIKEBALL:
if (!(!useNightsSS && G_IsSpecialStage(gamemap))) // Only for old special stages if (!useNightsSS && G_IsSpecialStage(gamemap)) // Only for old special stages
{ P_SpecialStageDamage(player, special, NULL);
P_DamageMobj(toucher, special, special, 1, 0);
return;
}
if (player->powers[pw_invulnerability] || player->powers[pw_flashing]
|| player->powers[pw_super])
return;
if (player->powers[pw_shield] || player->bot) //If One-Hit Shield
{
P_RemoveShield(player);
S_StartSound(toucher, sfx_shldls); // Ba-Dum! Shield loss.
}
else else
{ P_DamageMobj(toucher, special, special, 1, 0);
P_PlayRinglossSound(toucher);
if (player->rings >= 10)
player->rings -= 10;
else
player->rings = 0;
}
P_DoPlayerPain(player, special, NULL);
return; return;
case MT_EGGMOBILE2_POGO: case MT_EGGMOBILE2_POGO:
// sanity checks // sanity checks
@ -1871,7 +1851,7 @@ void P_CheckTimeLimit(void)
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
if (!playeringame[i] || players[i].spectator if (!playeringame[i] || players[i].spectator
|| (players[i].pflags & PF_TAGGED) || (players[i].pflags & PF_TAGIT)) || (players[i].pflags & PF_GAMETYPEOVER) || (players[i].pflags & PF_TAGIT))
continue; continue;
CONS_Printf(M_GetText("%s received double points for surviving the round.\n"), player_names[i]); CONS_Printf(M_GetText("%s received double points for surviving the round.\n"), player_names[i]);
@ -2018,7 +1998,7 @@ void P_CheckSurvivors(void)
spectators++; spectators++;
else if (players[i].pflags & PF_TAGIT) else if (players[i].pflags & PF_TAGIT)
taggers++; taggers++;
else if (!(players[i].pflags & PF_TAGGED)) else if (!(players[i].pflags & PF_GAMETYPEOVER))
{ {
survivorarray[survivors] = i; survivorarray[survivors] = i;
survivors++; survivors++;
@ -2259,7 +2239,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0)) if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0))
; ;
else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) && (target->player->lives != 0x7f)
&& G_GametypeUsesLives()) && G_GametypeUsesLives())
{ {
target->player->lives -= 1; // Lose a life Tails 03-11-2000 target->player->lives -= 1; // Lose a life Tails 03-11-2000
@ -2289,6 +2269,13 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
S_StopMusic(); // Stop the Music! Tails 03-14-2000 S_StopMusic(); // Stop the Music! Tails 03-14-2000
S_ChangeMusicInternal("_gover", false); // Yousa dead now, Okieday? Tails 03-14-2000 S_ChangeMusicInternal("_gover", false); // Yousa dead now, Okieday? Tails 03-14-2000
} }
if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && numgameovers < maxgameovers)
{
numgameovers++;
if ((!modifiedgame || savemoddata) && cursaveslot > 0)
G_SaveGameOver((UINT32)cursaveslot, (target->player->continues <= 0));
}
} }
} }
target->player->playerstate = PST_DEAD; target->player->playerstate = PST_DEAD;
@ -2325,7 +2312,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
} }
else else
{ {
if (!(target->player->pflags & PF_TAGGED)) if (!(target->player->pflags & PF_GAMETYPEOVER))
{ {
//otherwise, increment the tagger's score. //otherwise, increment the tagger's score.
//in hide and seek, suiciding players are counted as found. //in hide and seek, suiciding players are counted as found.
@ -2337,7 +2324,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
P_AddPlayerScore(&players[w], 100); P_AddPlayerScore(&players[w], 100);
} }
target->player->pflags |= PF_TAGGED; target->player->pflags |= PF_GAMETYPEOVER;
CONS_Printf(M_GetText("%s was found!\n"), player_names[target->player-players]); CONS_Printf(M_GetText("%s was found!\n"), player_names[target->player-players]);
P_CheckSurvivors(); P_CheckSurvivors();
} }
@ -2793,7 +2780,7 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
} }
else else
{ {
player->pflags |= PF_TAGGED; //in hide and seek, the player is tagged and stays stationary. player->pflags |= PF_GAMETYPEOVER; //in hide and seek, the player is tagged and stays stationary.
CONS_Printf(M_GetText("%s was found!\n"), player_names[player-players]); // Tell everyone who is it! CONS_Printf(M_GetText("%s was found!\n"), player_names[player-players]); // Tell everyone who is it!
} }
@ -2979,7 +2966,17 @@ void P_RemoveShield(player_t *player)
else else
player->powers[pw_shield] &= SH_STACK; player->powers[pw_shield] &= SH_STACK;
} }
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_NONE) else if (player->powers[pw_shield] & SH_NOSTACK)
{ // First layer shields
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON) // Give them what's coming to them!
{
P_BlackOw(player); // BAM!
player->pflags |= PF_JUMPDOWN;
}
else
player->powers[pw_shield] &= SH_STACK;
}
else
{ // Second layer shields { // Second layer shields
if (((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) && !(player->powers[pw_super] || (mariomode && player->powers[pw_invulnerability]))) if (((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) && !(player->powers[pw_super] || (mariomode && player->powers[pw_invulnerability])))
{ {
@ -2988,13 +2985,6 @@ void P_RemoveShield(player_t *player)
} }
player->powers[pw_shield] = SH_NONE; player->powers[pw_shield] = SH_NONE;
} }
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON) // Give them what's coming to them!
{
P_BlackOw(player); // BAM!
player->pflags |= PF_JUMPDOWN;
}
else
player->powers[pw_shield] = player->powers[pw_shield] & SH_STACK;
} }
static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
@ -3064,6 +3054,38 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN
player->rings = 0; player->rings = 0;
} }
//
// P_SpecialStageDamage
//
// Do old special stage-style damaging
// Removes 10 rings from the player, or knocks off their shield if they have one.
// If they don't have anything, just knock the player back anyway (this doesn't kill them).
//
void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source)
{
if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super])
return;
if (player->powers[pw_shield] || player->bot) //If One-Hit Shield
{
P_RemoveShield(player);
S_StartSound(player->mo, sfx_shldls); // Ba-Dum! Shield loss.
}
else
{
P_PlayRinglossSound(player->mo);
if (player->rings >= 10)
player->rings -= 10;
else
player->rings = 0;
}
P_DoPlayerPain(player, inflictor, source);
if (gametype == GT_CTF && player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))
P_PlayerFlagBurst(player, false);
}
/** Damages an object, which may or may not be a player. /** Damages an object, which may or may not be a player.
* For melee attacks, source and inflictor are the same. * For melee attacks, source and inflictor are the same.
* *
@ -3208,7 +3230,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (player->pflags & PF_GODMODE) if (player->pflags & PF_GODMODE)
return false; return false;
if (!(target->player->powers[pw_carry] == CR_NIGHTSMODE || target->player->pflags & PF_NIGHTSFALL) && (maptol & TOL_NIGHTS)) if ((maptol & TOL_NIGHTS) && target->player->powers[pw_carry] != CR_NIGHTSMODE && target->player->powers[pw_carry] != CR_NIGHTSFALL)
return false; return false;
switch (damagetype) switch (damagetype)
@ -3409,7 +3431,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
if (player->rings <= 0) if (player->rings <= 0)
num_rings = 0; num_rings = 0;
if (num_rings > 32 && !(player->pflags & PF_NIGHTSFALL)) if (num_rings > 32 && player->powers[pw_carry] != CR_NIGHTSFALL)
num_rings = 32; num_rings = 32;
if (player->powers[pw_emeralds]) if (player->powers[pw_emeralds])
@ -3441,7 +3463,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
// Make rings spill out around the player in 16 directions like SA, but spill like Sonic 2. // Make rings spill out around the player in 16 directions like SA, but spill like Sonic 2.
// Technically a non-SA way of spilling rings. They just so happen to be a little similar. // Technically a non-SA way of spilling rings. They just so happen to be a little similar.
if (player->pflags & PF_NIGHTSFALL) if (player->powers[pw_carry] == CR_NIGHTSFALL)
{ {
ns = FixedMul(((i*FRACUNIT)/16)+2*FRACUNIT, mo->scale); ns = FixedMul(((i*FRACUNIT)/16)+2*FRACUNIT, mo->scale);
mo->momx = FixedMul(FINECOSINE(fa),ns); mo->momx = FixedMul(FINECOSINE(fa),ns);
@ -3481,13 +3503,13 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
} }
if (player->mo->eflags & MFE_VERTICALFLIP) if (player->mo->eflags & MFE_VERTICALFLIP)
mo->momz *= -1; mo->momz *= -1;
if (P_IsObjectOnGround(player->mo))
player->powers[pw_carry] = CR_NONE;
} }
player->losstime += 10*TICRATE; player->losstime += 10*TICRATE;
if (P_IsObjectOnGround(player->mo))
player->pflags &= ~PF_NIGHTSFALL;
return; return;
} }

View File

@ -140,6 +140,7 @@ boolean P_IsObjectOnGround(mobj_t *mo);
boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec); boolean P_IsObjectOnGroundIn(mobj_t *mo, sector_t *sec);
boolean P_InSpaceSector(mobj_t *mo); boolean P_InSpaceSector(mobj_t *mo);
boolean P_InQuicksand(mobj_t *mo); boolean P_InQuicksand(mobj_t *mo);
boolean P_PlayerHitFloor(player_t *player);
void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative); void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative);
void P_RestoreMusic(player_t *player); void P_RestoreMusic(player_t *player);
@ -417,6 +418,7 @@ void P_ForceFeed(const player_t *player, INT32 attack, INT32 fade, tic_t duratio
void P_ForceConstant(const BasicFF_t *FFInfo); void P_ForceConstant(const BasicFF_t *FFInfo);
void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End); void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End);
void P_RemoveShield(player_t *player); void P_RemoveShield(player_t *player);
void P_SpecialStageDamage(player_t *player, mobj_t *inflictor, mobj_t *source);
boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype);
void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype);
void P_PlayerRingBurst(player_t *player, INT32 num_rings); /// \todo better fit in p_user.c void P_PlayerRingBurst(player_t *player, INT32 num_rings); /// \todo better fit in p_user.c

View File

@ -195,16 +195,20 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
if (spring->flags & MF_ENEMY) // Spring shells if (spring->flags & MF_ENEMY) // Spring shells
P_SetTarget(&spring->target, object); P_SetTarget(&spring->target, object);
if (horizspeed && object->player->cmd.forwardmove == 0 && object->player->cmd.sidemove == 0) if (horizspeed)
{ {
object->angle = spring->angle; object->player->drawangle = spring->angle;
if (object->player->cmd.forwardmove == 0 && object->player->cmd.sidemove == 0)
if (!demoplayback || P_AnalogMove(object->player))
{ {
if (object->player == &players[consoleplayer]) object->angle = spring->angle;
localangle = spring->angle;
else if (object->player == &players[secondarydisplayplayer]) if (!demoplayback || P_AnalogMove(object->player))
localangle2 = spring->angle; {
if (object->player == &players[consoleplayer])
localangle = spring->angle;
else if (object->player == &players[secondarydisplayplayer])
localangle2 = spring->angle;
}
} }
} }

View File

@ -34,6 +34,7 @@
#ifdef ESLOPE #ifdef ESLOPE
#include "p_slopes.h" #include "p_slopes.h"
#endif #endif
#include "f_finale.h"
// protos. // protos.
static CV_PossibleValue_t viewheight_cons_t[] = {{16, "MIN"}, {56, "MAX"}, {0, NULL}}; static CV_PossibleValue_t viewheight_cons_t[] = {{16, "MIN"}, {56, "MAX"}, {0, NULL}};
@ -250,6 +251,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
player->panim = PA_EDGE; player->panim = PA_EDGE;
break; break;
case S_PLAY_WALK: case S_PLAY_WALK:
case S_PLAY_SKID:
case S_PLAY_FLOAT: case S_PLAY_FLOAT:
player->panim = PA_WALK; player->panim = PA_WALK;
break; break;
@ -1964,6 +1966,8 @@ void P_XYMovement(mobj_t *mo)
#endif #endif
P_SlideMove(mo); P_SlideMove(mo);
if (player)
player->powers[pw_pushing] = 3;
xmove = ymove = 0; xmove = ymove = 0;
#ifdef ESLOPE #ifdef ESLOPE
@ -3040,110 +3044,9 @@ static void P_PlayerZMovement(mobj_t *mo)
} }
} }
if (mo->health && !mo->player->spectator && !P_CheckDeathPitCollide(mo)) clipmomz = P_PlayerHitFloor(mo->player);
{
if ((mo->player->charability2 == CA2_SPINDASH) && !(mo->player->pflags & PF_THOKKED) && (mo->player->cmd.buttons & BT_USE) && (FixedHypot(mo->momx, mo->momy) > (5*mo->scale)))
{
mo->player->pflags |= PF_SPINNING;
P_SetPlayerMobjState(mo, S_PLAY_ROLL);
S_StartSound(mo, sfx_spin);
}
else
mo->player->pflags &= ~PF_SPINNING;
if (mo->player->pflags & PF_GLIDING) // ground gliding
{
mo->player->skidtime = TICRATE;
mo->tics = -1;
}
else if (mo->player->charability2 == CA2_MELEE && (mo->player->panim == PA_ABILITY2 && mo->state-states != S_PLAY_MELEE_LANDING))
{
P_SetPlayerMobjState(mo, S_PLAY_MELEE_LANDING);
mo->tics = (mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(mo->movefactor)))>>FRACBITS;
S_StartSound(mo, sfx_s3k8b);
mo->player->pflags |= PF_FULLSTASIS;
}
else if (mo->player->pflags & PF_JUMPED || !(mo->player->pflags & PF_SPINNING)
|| mo->player->powers[pw_tailsfly] || mo->state-states == S_PLAY_FLY_TIRED)
{
if (mo->player->cmomx || mo->player->cmomy)
{
if (mo->player->charflags & SF_DASHMODE && mo->player->dashmode >= 3*TICRATE && mo->player->panim != PA_DASH)
P_SetPlayerMobjState(mo, S_PLAY_DASH);
else if (mo->player->speed >= FixedMul(mo->player->runspeed, mo->scale)
&& (mo->player->panim != PA_RUN || mo->state-states == S_PLAY_FLOAT_RUN))
P_SetPlayerMobjState(mo, S_PLAY_RUN);
else if ((mo->player->rmomx || mo->player->rmomy)
&& (mo->player->panim != PA_WALK || mo->state-states == S_PLAY_FLOAT))
P_SetPlayerMobjState(mo, S_PLAY_WALK);
else if (!mo->player->rmomx && !mo->player->rmomy && mo->player->panim != PA_IDLE)
P_SetPlayerMobjState(mo, S_PLAY_STND);
}
else
{
if (mo->player->charflags & SF_DASHMODE && mo->player->dashmode >= 3*TICRATE && mo->player->panim != PA_DASH)
P_SetPlayerMobjState(mo, S_PLAY_DASH);
else if (mo->player->speed >= FixedMul(mo->player->runspeed, mo->scale)
&& (mo->player->panim != PA_RUN || mo->state-states == S_PLAY_FLOAT_RUN))
P_SetPlayerMobjState(mo, S_PLAY_RUN);
else if ((mo->momx || mo->momy)
&& (mo->player->panim != PA_WALK || mo->state-states == S_PLAY_FLOAT))
P_SetPlayerMobjState(mo, S_PLAY_WALK);
else if (!mo->momx && !mo->momy && mo->player->panim != PA_IDLE)
P_SetPlayerMobjState(mo, S_PLAY_STND);
}
}
if (!(mo->player->pflags & PF_GLIDING))
mo->player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
mo->player->pflags &= ~(PF_STARTJUMP|PF_THOKKED|PF_CANCARRY/*|PF_GLIDING*/);
mo->player->secondjump = 0;
mo->player->glidetime = 0;
mo->player->climbing = 0;
mo->player->powers[pw_tailsfly] = 0;
if (mo->player->pflags & PF_SHIELDABILITY)
{
mo->player->pflags &= ~PF_SHIELDABILITY;
if ((mo->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) // Elemental shield's stomp attack.
{
if (mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) // play a blunt sound
S_StartSound(mo, sfx_s3k4c);
else // create a fire pattern on the ground
{
S_StartSound(mo, sfx_s3k47);
P_ElementalFire(mo->player, true);
}
P_SetObjectMomZ(mo,
(mo->eflags & MFE_UNDERWATER)
? 6*FRACUNIT/5
: 5*FRACUNIT/2,
false);
P_SetPlayerMobjState(mo, S_PLAY_FALL);
mo->momx = mo->momy = 0;
clipmomz = false;
}
else if ((mo->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) // Bubble shield's bounce attack.
{
P_DoBubbleBounce(mo->player);
clipmomz = false;
}
}
if (mo->player->pflags & PF_BOUNCING)
{
P_MobjCheckWater(mo);
mo->momz *= -1;
P_DoAbilityBounce(mo->player, true);
if (mo->player->scoreadd)
mo->player->scoreadd--;
clipmomz = false;
}
}
} }
if (!(mo->player->pflags & PF_SPINNING)) if (!(mo->player->pflags & PF_SPINNING) && mo->player->powers[pw_carry] != CR_NIGHTSMODE)
mo->player->pflags &= ~PF_STARTDASH; mo->player->pflags &= ~PF_STARTDASH;
if (clipmomz) if (clipmomz)
@ -8415,6 +8318,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
#endif #endif
switch (mobj->type) switch (mobj->type)
{ {
case MT_ALTVIEWMAN:
if (titlemapinaction) mobj->flags &= ~MF_NOTHINK;
break;
case MT_CYBRAKDEMON_NAPALM_BOMB_LARGE: case MT_CYBRAKDEMON_NAPALM_BOMB_LARGE:
mobj->fuse = mobj->info->mass; mobj->fuse = mobj->info->mass;
break; break;
@ -9199,6 +9105,7 @@ void P_AfterPlayerSpawn(INT32 playernum)
} }
SV_SpawnPlayer(playernum, mobj->x, mobj->y, mobj->angle); SV_SpawnPlayer(playernum, mobj->x, mobj->y, mobj->angle);
p->drawangle = mobj->angle;
if (camera.chase) if (camera.chase)
{ {

View File

@ -64,22 +64,16 @@ typedef enum
static inline void P_ArchivePlayer(void) static inline void P_ArchivePlayer(void)
{ {
const player_t *player = &players[consoleplayer]; const player_t *player = &players[consoleplayer];
INT32 pllives = player->lives; INT16 skininfo = player->skin + (botskin<<5);
if (pllives < 3) // Bump up to 3 lives if the player SINT8 pllives = player->lives;
pllives = 3; // has less than that. if (pllives < startinglivesbalance[numgameovers]) // Bump up to 3 lives if the player
pllives = startinglivesbalance[numgameovers]; // has less than that.
WRITEUINT8(save_p, player->skincolor);
WRITEUINT8(save_p, player->skin);
WRITEUINT16(save_p, skininfo);
WRITEUINT8(save_p, numgameovers);
WRITESINT8(save_p, pllives);
WRITEUINT32(save_p, player->score); WRITEUINT32(save_p, player->score);
WRITEINT32(save_p, pllives);
WRITEINT32(save_p, player->continues); WRITEINT32(save_p, player->continues);
if (botskin)
{
WRITEUINT8(save_p, botskin);
WRITEUINT8(save_p, botcolor);
}
} }
// //
@ -87,22 +81,14 @@ static inline void P_ArchivePlayer(void)
// //
static inline void P_UnArchivePlayer(void) static inline void P_UnArchivePlayer(void)
{ {
savedata.skincolor = READUINT8(save_p); INT16 skininfo = READUINT16(save_p);
savedata.skin = READUINT8(save_p); savedata.skin = skininfo & ((1<<5) - 1);
savedata.botskin = skininfo >> 5;
savedata.score = READINT32(save_p); savedata.numgameovers = READUINT8(save_p);
savedata.lives = READINT32(save_p); savedata.lives = READSINT8(save_p);
savedata.score = READUINT32(save_p);
savedata.continues = READINT32(save_p); savedata.continues = READINT32(save_p);
if (savedata.botcolor)
{
savedata.botskin = READUINT8(save_p);
if (savedata.botskin-1 >= numskins)
savedata.botskin = 0;
savedata.botcolor = READUINT8(save_p);
}
else
savedata.botskin = 0;
} }
// //
@ -126,6 +112,7 @@ static void P_NetArchivePlayers(void)
// no longer send ticcmds, player name, skin, or color // no longer send ticcmds, player name, skin, or color
WRITEANGLE(save_p, players[i].aiming); WRITEANGLE(save_p, players[i].aiming);
WRITEANGLE(save_p, players[i].drawangle);
WRITEANGLE(save_p, players[i].awayviewaiming); WRITEANGLE(save_p, players[i].awayviewaiming);
WRITEINT32(save_p, players[i].awayviewtics); WRITEINT32(save_p, players[i].awayviewtics);
WRITEINT32(save_p, players[i].rings); WRITEINT32(save_p, players[i].rings);
@ -305,6 +292,7 @@ static void P_NetUnArchivePlayers(void)
// (that data is handled in the server config now) // (that data is handled in the server config now)
players[i].aiming = READANGLE(save_p); players[i].aiming = READANGLE(save_p);
players[i].drawangle = READANGLE(save_p);
players[i].awayviewaiming = READANGLE(save_p); players[i].awayviewaiming = READANGLE(save_p);
players[i].awayviewtics = READINT32(save_p); players[i].awayviewtics = READINT32(save_p);
players[i].rings = READINT32(save_p); players[i].rings = READINT32(save_p);
@ -3162,7 +3150,7 @@ static inline void P_ArchiveMisc(void)
//lastmapsaved = gamemap; //lastmapsaved = gamemap;
lastmaploaded = gamemap; lastmaploaded = gamemap;
WRITEUINT16(save_p, (botskin ? (emeralds|(1<<10)) : emeralds)+357); WRITEUINT16(save_p, emeralds+357);
WRITESTRINGN(save_p, timeattackfolder, sizeof(timeattackfolder)); WRITESTRINGN(save_p, timeattackfolder, sizeof(timeattackfolder));
} }
@ -3192,9 +3180,6 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride)
token = 0; token = 0;
savedata.emeralds = READUINT16(save_p)-357; savedata.emeralds = READUINT16(save_p)-357;
if (savedata.emeralds & (1<<10))
savedata.botcolor = 0xFF;
savedata.emeralds &= 0xff;
READSTRINGN(save_p, testname, sizeof(testname)); READSTRINGN(save_p, testname, sizeof(testname));

View File

@ -30,14 +30,13 @@ mobj_t *P_FindNewPosition(UINT32 oldposition);
typedef struct typedef struct
{ {
UINT8 skincolor;
UINT8 skin; UINT8 skin;
UINT8 botskin; UINT8 botskin;
UINT8 botcolor;
INT32 score; INT32 score;
INT32 lives; INT32 lives;
INT32 continues; INT32 continues;
UINT16 emeralds; UINT16 emeralds;
UINT8 numgameovers;
} savedata_t; } savedata_t;
extern savedata_t savedata; extern savedata_t savedata;

View File

@ -2244,7 +2244,7 @@ static void P_LevelInitStuff(void)
players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0; players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0;
players[i].rings = 0; players[i].rings = 0;
players[i].aiming = 0; players[i].aiming = 0;
players[i].pflags &= ~PF_TIMEOVER; players[i].pflags &= ~PF_GAMETYPEOVER;
players[i].losstime = 0; players[i].losstime = 0;
players[i].timeshit = 0; players[i].timeshit = 0;
@ -2667,7 +2667,9 @@ boolean P_SetupLevel(boolean skipprecip)
// As oddly named as this is, this handles music only. // As oddly named as this is, this handles music only.
// We should be fine starting it here. // We should be fine starting it here.
S_Start(); /// ... as long as this isn't a titlemap transition, that is
if (!titlemapinaction)
S_Start();
// Let's fade to black here // Let's fade to black here
// But only if we didn't do the special stage wipe // But only if we didn't do the special stage wipe
@ -2681,7 +2683,7 @@ boolean P_SetupLevel(boolean skipprecip)
} }
// Print "SPEEDING OFF TO [ZONE] [ACT 1]..." // Print "SPEEDING OFF TO [ZONE] [ACT 1]..."
if (rendermode != render_none) if (!titlemapinaction && rendermode != render_none)
{ {
// Don't include these in the fade! // Don't include these in the fade!
char tx[64]; char tx[64];
@ -3021,19 +3023,19 @@ boolean P_SetupLevel(boolean skipprecip)
P_RunCachedActions(); P_RunCachedActions();
if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || players[consoleplayer].lives <= 0) if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking || players[consoleplayer].lives <= 0)
&& (!modifiedgame || savemoddata) && cursaveslot >= 0 && CanSaveLevel(gamemap)) && (!modifiedgame || savemoddata) && cursaveslot > 0 && CanSaveLevel(gamemap))
G_SaveGame((UINT32)cursaveslot); G_SaveGame((UINT32)cursaveslot);
lastmaploaded = gamemap; // HAS to be set after saving!! lastmaploaded = gamemap; // HAS to be set after saving!!
if (savedata.lives > 0) if (savedata.lives > 0)
{ {
numgameovers = savedata.numgameovers;
players[consoleplayer].continues = savedata.continues; players[consoleplayer].continues = savedata.continues;
players[consoleplayer].lives = savedata.lives; players[consoleplayer].lives = savedata.lives;
players[consoleplayer].score = savedata.score; players[consoleplayer].score = savedata.score;
botskin = savedata.botskin; if ((botingame = ((botskin = savedata.botskin) != 0)))
botcolor = savedata.botcolor; botcolor = skins[botskin-1].prefcolor;
botingame = (savedata.botskin != 0);
emeralds = savedata.emeralds; emeralds = savedata.emeralds;
savedata.lives = 0; savedata.lives = 0;
} }
@ -3202,8 +3204,8 @@ boolean P_AddWadFile(const char *wadfilename, char **firstmapname)
ST_Start(); ST_Start();
// Prevent savefile cheating // Prevent savefile cheating
if (cursaveslot >= 0) if (cursaveslot > 0)
cursaveslot = -1; cursaveslot = 0;
if (replacedcurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer)) if (replacedcurrentmap && gamestate == GS_LEVEL && (netgame || multiplayer))
{ {

View File

@ -3525,31 +3525,14 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
S_StartSound(player->mo, sfx_itemup); S_StartSound(player->mo, sfx_itemup);
} }
break; break;
case 11: // Special Stage Damage - Kind of like a mini-P_DamageMobj() case 11: // Special Stage Damage
if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super] || player->exiting || player->bot) if (player->exiting || player->bot) // Don't do anything for bots or players who have just finished
break; break;
if (!(player->powers[pw_shield] || player->rings > 0)) // Don't do anything if no shield or rings anyway if (!(player->powers[pw_shield] || player->rings > 0)) // Don't do anything if no shield or rings anyway
break; break;
if (player->powers[pw_shield]) P_SpecialStageDamage(player, NULL, NULL);
{
P_RemoveShield(player);
S_StartSound(player->mo, sfx_shldls); // Ba-Dum! Shield loss.
}
else if (player->rings > 0)
{
P_PlayRinglossSound(player->mo);
if (player->rings >= 10)
player->rings -= 10;
else
player->rings = 0;
}
P_DoPlayerPain(player, NULL, NULL); // this does basically everything that was here before
if (gametype == GT_CTF && player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))
P_PlayerFlagBurst(player, false);
break; break;
case 12: // Space Countdown case 12: // Space Countdown
if (!(player->powers[pw_shield] & SH_PROTECTWATER) && !player->powers[pw_spacetime]) if (!(player->powers[pw_shield] & SH_PROTECTWATER) && !player->powers[pw_spacetime])
@ -3985,7 +3968,7 @@ DoneSection2:
player->powers[pw_carry] = CR_ZOOMTUBE; player->powers[pw_carry] = CR_ZOOMTUBE;
player->speed = speed; player->speed = speed;
player->pflags |= PF_SPINNING; player->pflags |= PF_SPINNING;
player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_SLIDING|PF_CANCARRY); player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_BOUNCING|PF_SLIDING|PF_CANCARRY);
player->climbing = 0; player->climbing = 0;
if (player->mo->state-states != S_PLAY_ROLL) if (player->mo->state-states != S_PLAY_ROLL)
@ -4065,7 +4048,7 @@ DoneSection2:
player->powers[pw_carry] = CR_ZOOMTUBE; player->powers[pw_carry] = CR_ZOOMTUBE;
player->speed = speed; player->speed = speed;
player->pflags |= PF_SPINNING; player->pflags |= PF_SPINNING;
player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_SLIDING|PF_CANCARRY); player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_BOUNCING|PF_SLIDING|PF_CANCARRY);
player->climbing = 0; player->climbing = 0;
if (player->mo->state-states != S_PLAY_ROLL) if (player->mo->state-states != S_PLAY_ROLL)
@ -4373,7 +4356,7 @@ DoneSection2:
S_StartSound(player->mo, sfx_s3k4a); S_StartSound(player->mo, sfx_s3k4a);
player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_SLIDING|PF_CANCARRY); player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_BOUNCING|PF_SLIDING|PF_CANCARRY);
player->climbing = 0; player->climbing = 0;
P_SetThingPosition(player->mo); P_SetThingPosition(player->mo);
P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);

View File

@ -533,7 +533,7 @@ static inline void P_DoTagStuff(void)
for (i=0; i < MAXPLAYERS; i++) for (i=0; i < MAXPLAYERS; i++)
{ {
if (playeringame[i] && !players[i].spectator && players[i].playerstate == PST_LIVE if (playeringame[i] && !players[i].spectator && players[i].playerstate == PST_LIVE
&& !(players[i].pflags & (PF_TAGIT|PF_TAGGED))) && !(players[i].pflags & (PF_TAGIT|PF_GAMETYPEOVER)))
//points given is the number of participating players divided by two. //points given is the number of participating players divided by two.
P_AddPlayerScore(&players[i], participants/2); P_AddPlayerScore(&players[i], participants/2);
} }

View File

@ -165,7 +165,7 @@ fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move)
boolean P_AutoPause(void) boolean P_AutoPause(void)
{ {
// Don't pause even on menu-up or focus-lost in netgames or record attack // Don't pause even on menu-up or focus-lost in netgames or record attack
if (netgame || modeattacking) if (netgame || modeattacking || gamestate == GS_TITLESCREEN)
return false; return false;
return (menuactive || window_notinfocus); return (menuactive || window_notinfocus);
@ -576,7 +576,7 @@ static void P_DeNightserizePlayer(player_t *player)
thinker_t *th; thinker_t *th;
mobj_t *mo2; mobj_t *mo2;
player->powers[pw_carry] = CR_NONE; player->powers[pw_carry] = CR_NIGHTSFALL;
player->powers[pw_underwater] = 0; player->powers[pw_underwater] = 0;
player->pflags &= ~(PF_USEDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING|PF_DRILLING|PF_TRANSFERTOCLOSEST); player->pflags &= ~(PF_USEDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_THOKKED|PF_SPINNING|PF_DRILLING|PF_TRANSFERTOCLOSEST);
@ -603,7 +603,6 @@ static void P_DeNightserizePlayer(player_t *player)
player->marescore = 0; player->marescore = 0;
P_SetPlayerMobjState(player->mo, S_PLAY_FALL); P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
player->pflags |= PF_NIGHTSFALL;
// If in a special stage, add some preliminary exit time. // If in a special stage, add some preliminary exit time.
if (G_IsSpecialStage(gamemap)) if (G_IsSpecialStage(gamemap))
@ -857,12 +856,13 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
fallbackspeed = FixedMul(4*FRACUNIT, player->mo->scale); fallbackspeed = FixedMul(4*FRACUNIT, player->mo->scale);
} }
player->drawangle = ang + ANGLE_180;
P_InstaThrust(player->mo, ang, fallbackspeed); P_InstaThrust(player->mo, ang, fallbackspeed);
} }
// Point penalty for hitting a hazard during tag. // Point penalty for hitting a hazard during tag.
// Discourages players from intentionally hurting themselves to avoid being tagged. // Discourages players from intentionally hurting themselves to avoid being tagged.
if (gametype == GT_TAG && (!(player->pflags & PF_TAGGED) && !(player->pflags & PF_TAGIT))) if (gametype == GT_TAG && (!(player->pflags & PF_GAMETYPEOVER) && !(player->pflags & PF_TAGIT)))
{ {
if (player->score >= 50) if (player->score >= 50)
player->score -= 50; player->score -= 50;
@ -884,7 +884,7 @@ void P_ResetPlayer(player_t *player)
{ {
player->pflags &= ~(PF_SPINNING|PF_STARTDASH|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_THOKKED|PF_CANCARRY|PF_SHIELDABILITY|PF_BOUNCING); player->pflags &= ~(PF_SPINNING|PF_STARTDASH|PF_STARTJUMP|PF_JUMPED|PF_NOJUMPDAMAGE|PF_GLIDING|PF_THOKKED|PF_CANCARRY|PF_SHIELDABILITY|PF_BOUNCING);
if (!(player->powers[pw_carry] == CR_NIGHTSMODE || player->powers[pw_carry] == CR_BRAKGOOP)) if (!(player->powers[pw_carry] == CR_NIGHTSMODE || player->powers[pw_carry] == CR_NIGHTSFALL || player->powers[pw_carry] == CR_BRAKGOOP))
player->powers[pw_carry] = CR_NONE; player->powers[pw_carry] = CR_NONE;
player->secondjump = 0; player->secondjump = 0;
@ -950,6 +950,8 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings)
// //
void P_GivePlayerLives(player_t *player, INT32 numlives) void P_GivePlayerLives(player_t *player, INT32 numlives)
{ {
if (player->lives == 0x7f) return;
player->lives += numlives; player->lives += numlives;
if (player->lives > 99) if (player->lives > 99)
@ -1153,7 +1155,9 @@ void P_PlayLivesJingle(player_t *player)
if (player && !P_IsLocalPlayer(player)) if (player && !P_IsLocalPlayer(player))
return; return;
if (gametype == GT_COOP && (netgame || multiplayer) && cv_cooplives.value == 0) if ((player && player->lives == 0x7f)
|| (!player && &players[consoleplayer] && players[consoleplayer].lives == 0x7f)
|| (gametype == GT_COOP && (netgame || multiplayer) && cv_cooplives.value == 0))
S_StartSound(NULL, sfx_lose); S_StartSound(NULL, sfx_lose);
else if (use1upSound) else if (use1upSound)
S_StartSound(NULL, sfx_oneup); S_StartSound(NULL, sfx_oneup);
@ -1562,7 +1566,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
ghost->color = mobj->color; ghost->color = mobj->color;
ghost->angle = mobj->angle; ghost->angle = (mobj->player ? mobj->player->drawangle : mobj->angle);
ghost->sprite = mobj->sprite; ghost->sprite = mobj->sprite;
ghost->sprite2 = mobj->sprite2; ghost->sprite2 = mobj->sprite2;
ghost->frame = mobj->frame; ghost->frame = mobj->frame;
@ -1612,7 +1616,7 @@ void P_SpawnThokMobj(player_t *player)
mobj = P_SpawnMobj(player->mo->x, player->mo->y, zheight, type); mobj = P_SpawnMobj(player->mo->x, player->mo->y, zheight, type);
// set to player's angle, just in case // set to player's angle, just in case
mobj->angle = player->mo->angle; mobj->angle = player->drawangle;
// color and skin // color and skin
mobj->color = player->mo->color; mobj->color = player->mo->color;
@ -1672,7 +1676,7 @@ void P_SpawnSpinMobj(player_t *player, mobjtype_t type)
mobj = P_SpawnMobj(player->mo->x, player->mo->y, zheight, type); mobj = P_SpawnMobj(player->mo->x, player->mo->y, zheight, type);
// set to player's angle, just in case // set to player's angle, just in case
mobj->angle = player->mo->angle; mobj->angle = player->drawangle;
// color and skin // color and skin
mobj->color = player->mo->color; mobj->color = player->mo->color;
@ -1777,6 +1781,122 @@ boolean P_InSpaceSector(mobj_t *mo) // Returns true if you are in space
return false; // No vacuum here, Captain! return false; // No vacuum here, Captain!
} }
//
// P_PlayerHitFloor
//
// Handles player hitting floor surface.
// Returns whether to clip momz.
boolean P_PlayerHitFloor(player_t *player)
{
boolean clipmomz;
I_Assert(player->mo != NULL);
if ((clipmomz = !(P_CheckDeathPitCollide(player->mo))) && player->mo->health && !player->spectator)
{
if ((player->charability2 == CA2_SPINDASH) && !(player->pflags & PF_THOKKED) && (player->cmd.buttons & BT_USE) && (FixedHypot(player->mo->momx, player->mo->momy) > (5*player->mo->scale)))
{
player->pflags |= PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
S_StartSound(player->mo, sfx_spin);
}
else
player->pflags &= ~PF_SPINNING;
if (player->pflags & PF_GLIDING) // ground gliding
{
player->skidtime = TICRATE;
player->mo->tics = -1;
}
else if (player->charability2 == CA2_MELEE && (player->panim == PA_ABILITY2 && player->mo->state-states != S_PLAY_MELEE_LANDING))
{
P_SetPlayerMobjState(player->mo, S_PLAY_MELEE_LANDING);
player->mo->tics = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS;
S_StartSound(player->mo, sfx_s3k8b);
player->pflags |= PF_FULLSTASIS;
}
else if (player->pflags & PF_JUMPED || !(player->pflags & PF_SPINNING)
|| player->powers[pw_tailsfly] || player->mo->state-states == S_PLAY_FLY_TIRED)
{
if (player->cmomx || player->cmomy)
{
if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH)
P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
else if (player->speed >= FixedMul(player->runspeed, player->mo->scale)
&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
else if ((player->rmomx || player->rmomy)
&& (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT))
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
else if (!player->rmomx && !player->rmomy && player->panim != PA_IDLE)
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
}
else
{
if (player->charflags & SF_DASHMODE && player->dashmode >= 3*TICRATE && player->panim != PA_DASH)
P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
else if (player->speed >= FixedMul(player->runspeed, player->mo->scale)
&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
else if ((player->mo->momx || player->mo->momy)
&& (player->panim != PA_WALK || player->mo->state-states == S_PLAY_FLOAT))
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
else if (!player->mo->momx && !player->mo->momy && player->panim != PA_IDLE)
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
}
}
if (!(player->pflags & PF_GLIDING))
player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
player->pflags &= ~(PF_STARTJUMP|PF_THOKKED|PF_CANCARRY/*|PF_GLIDING*/);
player->secondjump = 0;
player->glidetime = 0;
player->climbing = 0;
player->powers[pw_tailsfly] = 0;
if (player->pflags & PF_SHIELDABILITY)
{
player->pflags &= ~PF_SHIELDABILITY;
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) // Elemental shield's stomp attack.
{
if (player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) // play a blunt sound
S_StartSound(player->mo, sfx_s3k4c);
else // create a fire pattern on the ground
{
S_StartSound(player->mo, sfx_s3k47);
P_ElementalFire(player, true);
}
P_SetObjectMomZ(player->mo,
(player->mo->eflags & MFE_UNDERWATER)
? 6*FRACUNIT/5
: 5*FRACUNIT/2,
false);
P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
player->mo->momx = player->mo->momy = 0;
clipmomz = false;
}
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) // Bubble shield's bounce attack.
{
P_DoBubbleBounce(player);
clipmomz = false;
}
}
if (player->pflags & PF_BOUNCING)
{
P_MobjCheckWater(player->mo);
player->mo->momz *= -1;
P_DoAbilityBounce(player, true);
if (player->scoreadd)
player->scoreadd--;
clipmomz = false;
}
}
return clipmomz;
}
boolean P_InQuicksand(mobj_t *mo) // Returns true if you are in quicksand boolean P_InQuicksand(mobj_t *mo) // Returns true if you are in quicksand
{ {
sector_t *sector = mo->subsector->sector; sector_t *sector = mo->subsector->sector;
@ -2152,6 +2272,9 @@ static void P_CheckQuicksand(player_t *player)
if (player->mo->z + player->mo->height >= ceilingheight) if (player->mo->z + player->mo->height >= ceilingheight)
player->mo->z = ceilingheight - player->mo->height; player->mo->z = ceilingheight - player->mo->height;
if (player->mo->momz <= 0)
P_PlayerHitFloor(player);
} }
else else
{ {
@ -2161,6 +2284,9 @@ static void P_CheckQuicksand(player_t *player)
if (player->mo->z <= floorheight) if (player->mo->z <= floorheight)
player->mo->z = floorheight; player->mo->z = floorheight;
if (player->mo->momz >= 0)
P_PlayerHitFloor(player);
} }
friction = abs(rover->master->v1->y - rover->master->v2->y)>>6; friction = abs(rover->master->v1->y - rover->master->v2->y)>>6;
@ -3667,7 +3793,7 @@ void P_DoJump(player_t *player, boolean soundandstate)
else else
player->mo->momz = 15*(FRACUNIT/4); player->mo->momz = 15*(FRACUNIT/4);
player->mo->angle = player->mo->angle - ANGLE_180; // Turn around from the wall you were climbing. player->drawangle = player->mo->angle = player->mo->angle - ANGLE_180; // Turn around from the wall you were climbing.
if (!demoplayback || P_AnalogMove(player)) if (!demoplayback || P_AnalogMove(player))
{ {
@ -3825,7 +3951,7 @@ static void P_DoSpinDashDust(player_t *player)
prandom[2] = P_RandomFixed()<<3; // P_RandomByte()<<11 prandom[2] = P_RandomFixed()<<3; // P_RandomByte()<<11
P_SetObjectMomZ(particle, player->dashspeed/50 + prandom[0], false); P_SetObjectMomZ(particle, player->dashspeed/50 + prandom[0], false);
P_InstaThrust(particle, P_InstaThrust(particle,
player->mo->angle + (prandom[1]*ANG1), player->drawangle + (prandom[1]*ANG1),
-FixedMul(player->dashspeed/12 + FRACUNIT + prandom[2], player->mo->scale)); -FixedMul(player->dashspeed/12 + FRACUNIT + prandom[2], player->mo->scale));
P_TryMove(particle, particle->x+particle->momx, particle->y+particle->momy, true); P_TryMove(particle, particle->x+particle->momx, particle->y+particle->momy, true);
} }
@ -3980,6 +4106,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
bullet->momy >>= 1; bullet->momy >>= 1;
} }
} }
player->drawangle = player->mo->angle;
#undef zpos #undef zpos
P_SetTarget(&player->mo->tracer, NULL); P_SetTarget(&player->mo->tracer, NULL);
@ -4015,7 +4142,10 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
if (player->mo->eflags & MFE_UNDERWATER) if (player->mo->eflags & MFE_UNDERWATER)
player->mo->momz >>= 1; player->mo->momz >>= 1;
if (FixedMul(player->speed, FINECOSINE(((player->mo->angle - R_PointToAngle2(0, 0, player->rmomx, player->rmomy)) >> ANGLETOFINESHIFT) & FINEMASK)) < FixedMul(player->maxdash, player->mo->scale)) if (FixedMul(player->speed, FINECOSINE(((player->mo->angle - R_PointToAngle2(0, 0, player->rmomx, player->rmomy)) >> ANGLETOFINESHIFT) & FINEMASK)) < FixedMul(player->maxdash, player->mo->scale))
{
player->drawangle = player->mo->angle;
P_InstaThrust(player->mo, player->mo->angle, FixedMul(player->maxdash, player->mo->scale)); P_InstaThrust(player->mo, player->mo->angle, FixedMul(player->maxdash, player->mo->scale));
}
player->mo->momx += player->cmomx; player->mo->momx += player->cmomx;
player->mo->momy += player->cmomy; player->mo->momy += player->cmomy;
P_SetPlayerMobjState(player->mo, S_PLAY_MELEE); P_SetPlayerMobjState(player->mo, S_PLAY_MELEE);
@ -4280,6 +4410,8 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
P_InstaThrust(player->mo, player->mo->angle, FixedMul(player->normalspeed, player->mo->scale)*(80-player->flyangle - (player->actionspd>>FRACBITS)/2)/80); P_InstaThrust(player->mo, player->mo->angle, FixedMul(player->normalspeed, player->mo->scale)*(80-player->flyangle - (player->actionspd>>FRACBITS)/2)/80);
else else
P_InstaThrust(player->mo, player->mo->angle, ((FixedMul(player->normalspeed - player->actionspd/4, player->mo->scale))*2)/3); P_InstaThrust(player->mo, player->mo->angle, ((FixedMul(player->normalspeed - player->actionspd/4, player->mo->scale))*2)/3);
player->drawangle = player->mo->angle;
} }
} }
} }
@ -4353,6 +4485,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
} }
P_InstaThrust(player->mo, player->mo->angle, FixedMul(actionspd, player->mo->scale)); P_InstaThrust(player->mo, player->mo->angle, FixedMul(actionspd, player->mo->scale));
player->drawangle = player->mo->angle;
if (maptol & TOL_2D) if (maptol & TOL_2D)
{ {
@ -4370,6 +4503,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockon)); P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockon));
if (lockon) if (lockon)
{ {
P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockon->x, lockon->y); player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockon->x, lockon->y);
player->homing = 3*TICRATE; player->homing = 3*TICRATE;
} }
@ -6274,7 +6408,7 @@ static void P_NiGHTSMovement(player_t *player)
&& ((cmd->buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT) && ((cmd->buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT)
|| (cmd->buttons & BT_USE))) || (cmd->buttons & BT_USE)))
{ {
if (!(player->pflags & PF_SKIDDOWN)) if (!(player->pflags & PF_STARTDASH))
S_StartSound(player->mo, sfx_ngskid); S_StartSound(player->mo, sfx_ngskid);
// You can tap the button to only slow down a bit, // You can tap the button to only slow down a bit,
@ -6292,10 +6426,10 @@ static void P_NiGHTSMovement(player_t *player)
} }
} }
player->pflags |= PF_SKIDDOWN; player->pflags |= PF_STARTDASH;
} }
else else
player->pflags &= ~PF_SKIDDOWN; player->pflags &= ~PF_STARTDASH;
{ {
const angle_t fa = (FixedAngle(player->flyangle*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK; const angle_t fa = (FixedAngle(player->flyangle*FRACUNIT)>>ANGLETOFINESHIFT) & FINEMASK;
@ -6674,8 +6808,8 @@ static void P_SkidStuff(player_t *player)
// If your push angle is more than this close to a full 180 degrees, trigger a skid. // If your push angle is more than this close to a full 180 degrees, trigger a skid.
if (dang > ANGLE_157h) if (dang > ANGLE_157h)
{ {
if (player->panim != PA_WALK) if (player->mo->state-states != S_PLAY_SKID)
P_SetPlayerMobjState(player->mo, S_PLAY_WALK); P_SetPlayerMobjState(player->mo, S_PLAY_SKID);
player->mo->tics = player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS; player->mo->tics = player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS;
S_StartSound(player->mo, sfx_skid); S_StartSound(player->mo, sfx_skid);
} }
@ -6741,7 +6875,7 @@ static void P_MovePlayer(player_t *player)
if (!(player->pflags & PF_TAGIT)) if (!(player->pflags & PF_TAGIT))
{ {
forcestasis = true; forcestasis = true;
if (player->pflags & PF_TAGGED) // Already hit. if (player->pflags & PF_GAMETYPEOVER) // Already hit.
player->powers[pw_flashing] = 5; player->powers[pw_flashing] = 5;
} }
} }
@ -6811,8 +6945,7 @@ static void P_MovePlayer(player_t *player)
P_CheckQuicksand(player); P_CheckQuicksand(player);
return; return;
} }
else if (player->powers[pw_carry] == CR_NIGHTSFALL && P_IsObjectOnGround(player->mo))
if (player->pflags & PF_NIGHTSFALL && P_IsObjectOnGround(player->mo))
{ {
if (G_IsSpecialStage(gamemap)) if (G_IsSpecialStage(gamemap))
{ {
@ -6824,7 +6957,7 @@ static void P_MovePlayer(player_t *player)
} }
else if (player->rings > 0) else if (player->rings > 0)
P_DamageMobj(player->mo, NULL, NULL, 1, 0); P_DamageMobj(player->mo, NULL, NULL, 1, 0);
player->pflags &= ~PF_NIGHTSFALL; player->powers[pw_carry] = CR_NONE;
} }
} }
@ -6917,8 +7050,6 @@ static void P_MovePlayer(player_t *player)
if (!player->mo->momx && !player->mo->momy && !player->mo->momz && player->panim == PA_WALK) if (!player->mo->momx && !player->mo->momy && !player->mo->momz && player->panim == PA_WALK)
P_SetPlayerMobjState(player->mo, S_PLAY_STND); P_SetPlayerMobjState(player->mo, S_PLAY_STND);
player->mo->movefactor = FRACUNIT; // We're not going to do any more with this, so let's change it back for the next frame.
////////////////// //////////////////
//GAMEPLAY STUFF// //GAMEPLAY STUFF//
////////////////// //////////////////
@ -7186,7 +7317,7 @@ static void P_MovePlayer(player_t *player)
//////////////////////////// ////////////////////////////
// If the player isn't on the ground, make sure they aren't in a "starting dash" position. // If the player isn't on the ground, make sure they aren't in a "starting dash" position.
if (!onground) if (!onground && player->powers[pw_carry] != CR_NIGHTSMODE)
{ {
player->pflags &= ~PF_STARTDASH; player->pflags &= ~PF_STARTDASH;
player->dashspeed = 0; player->dashspeed = 0;
@ -7203,7 +7334,7 @@ static void P_MovePlayer(player_t *player)
P_DoJumpStuff(player, cmd); P_DoJumpStuff(player, cmd);
// If you're not spinning, you'd better not be spindashing! // If you're not spinning, you'd better not be spindashing!
if (!(player->pflags & PF_SPINNING)) if (!(player->pflags & PF_SPINNING) && player->powers[pw_carry] != CR_NIGHTSMODE)
player->pflags &= ~PF_STARTDASH; player->pflags &= ~PF_STARTDASH;
////////////////// //////////////////
@ -7365,6 +7496,7 @@ static void P_MovePlayer(player_t *player)
case SH_FLAMEAURA: case SH_FLAMEAURA:
player->pflags |= PF_THOKKED|PF_SHIELDABILITY; player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
P_Thrust(player->mo, player->mo->angle, FixedMul(30*FRACUNIT - FixedSqrt(FixedDiv(player->speed, player->mo->scale)), player->mo->scale)); P_Thrust(player->mo, player->mo->angle, FixedMul(30*FRACUNIT - FixedSqrt(FixedDiv(player->speed, player->mo->scale)), player->mo->scale));
player->drawangle = player->mo->angle;
S_StartSound(player->mo, sfx_s3k43); S_StartSound(player->mo, sfx_s3k43);
default: default:
break; break;
@ -7418,6 +7550,8 @@ static void P_MovePlayer(player_t *player)
if (!(player->mo->tracer->flags & MF_BOSS)) if (!(player->mo->tracer->flags & MF_BOSS))
player->pflags &= ~PF_THOKKED; player->pflags &= ~PF_THOKKED;
// P_SetPlayerMobjState(player->mo, S_PLAY_SPRING); -- Speed didn't like it, RIP
} }
} }
@ -8084,12 +8218,16 @@ void P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target
// change angle // change angle
source->angle = R_PointToAngle2(source->x, source->y, enemy->x, enemy->y); source->angle = R_PointToAngle2(source->x, source->y, enemy->x, enemy->y);
if (source->player && (!demoplayback || P_AnalogMove(source->player))) if (source->player)
{ {
if (source->player == &players[consoleplayer]) source->player->drawangle = source->angle;
localangle = source->angle; if (!demoplayback || P_AnalogMove(source->player))
else if (source->player == &players[secondarydisplayplayer]) {
localangle2 = source->angle; if (source->player == &players[consoleplayer])
localangle = source->angle;
else if (source->player == &players[secondarydisplayplayer])
localangle2 = source->angle;
}
} }
// change slope // change slope
@ -8160,7 +8298,8 @@ boolean P_GetLives(player_t *player)
INT32 i, maxlivesplayer = -1, livescheck = 1; INT32 i, maxlivesplayer = -1, livescheck = 1;
if (!(netgame || multiplayer) if (!(netgame || multiplayer)
|| (gametype != GT_COOP) || (gametype != GT_COOP)
|| (cv_cooplives.value == 1)) || (cv_cooplives.value == 1)
|| (player->lives == 0x7f))
return true; return true;
if ((cv_cooplives.value == 2 || cv_cooplives.value == 0) && player->lives > 0) if ((cv_cooplives.value == 2 || cv_cooplives.value == 0) && player->lives > 0)
@ -8187,7 +8326,8 @@ boolean P_GetLives(player_t *player)
{ {
if (cv_cooplives.value == 2 && (P_IsLocalPlayer(player) || P_IsLocalPlayer(&players[maxlivesplayer]))) if (cv_cooplives.value == 2 && (P_IsLocalPlayer(player) || P_IsLocalPlayer(&players[maxlivesplayer])))
S_StartSound(NULL, sfx_jshard); // placeholder S_StartSound(NULL, sfx_jshard); // placeholder
players[maxlivesplayer].lives--; if (players[maxlivesplayer].lives != 0x7f)
players[maxlivesplayer].lives--;
player->lives++; player->lives++;
if (player->lives < 1) if (player->lives < 1)
player->lives = 1; player->lives = 1;
@ -8379,7 +8519,7 @@ static void P_DeathThink(player_t *player)
if (gametype == GT_RACE || gametype == GT_COMPETITION || (gametype == GT_COOP && (multiplayer || netgame))) if (gametype == GT_RACE || gametype == GT_COMPETITION || (gametype == GT_COOP && (multiplayer || netgame)))
{ {
// Keep time rolling in race mode // Keep time rolling in race mode
if (!(countdown2 && !countdown) && !player->exiting && !(player->pflags & PF_TIMEOVER)) if (!(countdown2 && !countdown) && !player->exiting && !(player->pflags & PF_GAMETYPEOVER))
{ {
if (gametype == GT_RACE || gametype == GT_COMPETITION) if (gametype == GT_RACE || gametype == GT_COMPETITION)
{ {
@ -9404,7 +9544,7 @@ void P_PlayerThink(player_t *player)
if (netgame && player->mo->health > 0) if (netgame && player->mo->health > 0)
CONS_Printf(M_GetText("%s ran out of time.\n"), player_names[player-players]); CONS_Printf(M_GetText("%s ran out of time.\n"), player_names[player-players]);
player->pflags |= PF_TIMEOVER; player->pflags |= PF_GAMETYPEOVER;
if (player->powers[pw_carry] == CR_NIGHTSMODE) if (player->powers[pw_carry] == CR_NIGHTSMODE)
{ {
@ -9603,6 +9743,135 @@ void P_PlayerThink(player_t *player)
if (!player->mo) if (!player->mo)
return; // P_MovePlayer removed player->mo. return; // P_MovePlayer removed player->mo.
// deez New User eXperiences.
{
// Directionchar!
// Camera angle stuff.
if (player->exiting) // no control, no modification
;
else if (!(player->pflags & PF_DIRECTIONCHAR)
|| (player->climbing // stuff where the direction is forced at all times
|| (player->pflags & PF_GLIDING))
|| (player->powers[pw_carry] == CR_NIGHTSMODE)
|| (P_AnalogMove(player) || twodlevel || player->mo->flags2 & MF2_TWOD) // keep things synchronised up there, since the camera IS seperate from player motion when that happens
|| G_RingSlingerGametype()) // no firing rings in directions your player isn't aiming
player->drawangle = player->mo->angle;
else if (P_PlayerInPain(player))
;
else if (player->powers[pw_carry] && player->mo->tracer) // carry
{
switch (player->powers[pw_carry])
{
case CR_PLAYER:
player->drawangle = (player->mo->tracer->player ? player->mo->tracer->player->drawangle : player->mo->tracer->angle);
break;
/* -- in case we wanted to have the camera freely movable during zoom tubes
case CR_ZOOMTUBE:*/
case CR_ROPEHANG:
if (player->mo->momx || player->mo->momy)
{
player->drawangle = R_PointToAngle2(0, 0, player->mo->momx, player->mo->momy);
break;
}
default:
player->drawangle = player->mo->angle;
break;
}
}
else if ((player->skidtime > (TICRATE/2 - 2) || ((player->pflags & (PF_SPINNING|PF_STARTDASH)) == PF_SPINNING)) && (abs(player->rmomx) > 5*player->mo->scale || abs(player->rmomy) > 5*player->mo->scale)) // spin/skid force
player->drawangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
else if (((player->charability2 == CA2_GUNSLINGER || player->charability2 == CA2_MELEE) && player->panim == PA_ABILITY2) || player->pflags & PF_STASIS || player->skidtime)
;
else
{
angle_t diff;
UINT8 factor;
if (player->pflags & PF_SLIDING)
{
#if 0 // fun hydrocity style horizontal spin
if (player->mo->eflags & MFE_TOUCHWATER || player->powers[pw_flashing] > (flashingtics/4)*3)
{
diff = (player->mo->angle - player->drawangle);
factor = 4;
}
else
{
diff = factor = 0;
player->drawangle += ANGLE_22h;
}
#else
diff = (player->mo->angle - player->drawangle);
factor = 4;
#endif
}
else if (player->pflags & PF_STARTDASH)
{
diff = (player->mo->angle - player->drawangle);
factor = 4;
}
else if (cmd->forwardmove || cmd->sidemove) // only when you're pressing movement keys
{
diff = ((player->mo->angle + R_PointToAngle2(0, 0, cmd->forwardmove<<FRACBITS, -cmd->sidemove<<FRACBITS)) - player->drawangle);
factor = 4;
}
else if (player->rmomx || player->rmomy)
diff = factor = 0;
else
{
diff = (player->mo->angle - player->drawangle);
factor = 8;
}
if (diff)
{
if (diff > ANGLE_180)
diff = InvAngle(InvAngle(diff)/factor);
else
diff /= factor;
player->drawangle += diff;
}
}
// Autobrake!
{
boolean currentlyonground = P_IsObjectOnGround(player->mo);
if (!player->powers[pw_carry]
&& ((player->pflags & (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE)) == (PF_AUTOBRAKE|PF_APPLYAUTOBRAKE))
&& !(cmd->forwardmove || cmd->sidemove)
&& (player->rmomx || player->rmomy))
{
fixed_t acceleration = (player->accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration) * player->thrustfactor * 20;
angle_t moveAngle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
if (!currentlyonground)
acceleration /= 2;
if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration...
acceleration = FixedMul(acceleration<<FRACBITS, player->mo->movefactor)>>FRACBITS;
P_Thrust(player->mo, moveAngle, -acceleration);
}
if (!(player->pflags & PF_AUTOBRAKE)
|| player->powers[pw_carry]
|| player->panim == PA_SPRING
|| player->panim == PA_PAIN
|| !player->mo->health
|| player->climbing
|| player->pflags & (PF_SPINNING|PF_SLIDING))
player->pflags &= ~PF_APPLYAUTOBRAKE;
else if (currentlyonground)
player->pflags |= PF_APPLYAUTOBRAKE;
}
}
if (player->powers[pw_pushing])
player->powers[pw_pushing]--;
player->mo->movefactor = FRACUNIT; // We're not going to do any more with this, so let's change it back for the next frame.
// Unset statis flags after moving. // Unset statis flags after moving.
// In other words, if you manually set stasis via code, // In other words, if you manually set stasis via code,
// it lasts for one tic. // it lasts for one tic.

View File

@ -914,7 +914,7 @@ void R_SetupFrame(player_t *player, boolean skybox)
chasecam = (cv_chasecam.value != 0); chasecam = (cv_chasecam.value != 0);
} }
if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD) if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN)
chasecam = true; // force chasecam on chasecam = true; // force chasecam on
else if (player->spectator) // no spectator chasecam else if (player->spectator) // no spectator chasecam
chasecam = false; // force chasecam off chasecam = false; // force chasecam off

View File

@ -64,10 +64,6 @@ void R_SetupSkyDraw(void)
// the horizon line in a 256x128 sky texture // the horizon line in a 256x128 sky texture
skytexturemid = (textures[skytexture]->height/2)<<FRACBITS; skytexturemid = (textures[skytexture]->height/2)<<FRACBITS;
// get the right drawer, it was set by screen.c, depending on the
// current video mode bytes per pixel (quick fix)
wallcolfunc = walldrawerfunc;
R_SetSkyScale(); R_SetSkyScale();
} }

View File

@ -1104,7 +1104,7 @@ static void R_ProjectSprite(mobj_t *thing)
if (sprframe->rotate != SRF_SINGLE || papersprite) if (sprframe->rotate != SRF_SINGLE || papersprite)
{ {
ang = R_PointToAngle (thing->x, thing->y) - thing->angle; ang = R_PointToAngle (thing->x, thing->y) - (thing->player ? thing->player->drawangle : thing->angle);
if (papersprite) if (papersprite)
ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT)); ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT));
} }
@ -2431,176 +2431,39 @@ CV_PossibleValue_t skin_cons_t[MAXSKINS+1];
UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player) UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player)
{ {
UINT8 super = (spr2 & FF_SPR2SUPER); UINT8 super = 0, i = 0;
if (!skin) if (!skin)
return 0; return 0;
while (!(skin->sprites[spr2].numframes) while (!(skin->sprites[spr2].numframes)
&& spr2 != SPR2_STND) && spr2 != SPR2_STND
&& ++i != 32) // recursion limiter
{ {
if (spr2 & FF_SPR2SUPER) if (spr2 & FF_SPR2SUPER)
{ {
super = FF_SPR2SUPER;
spr2 &= ~FF_SPR2SUPER; spr2 &= ~FF_SPR2SUPER;
continue; continue;
} }
switch(spr2) switch(spr2)
{ {
case SPR2_RUN:
spr2 = SPR2_WALK; // Normal special cases.
break;
case SPR2_STUN:
spr2 = SPR2_PAIN;
break;
case SPR2_DRWN:
spr2 = SPR2_DEAD;
break;
case SPR2_SPIN:
spr2 = SPR2_ROLL;
break;
case SPR2_GASP:
spr2 = SPR2_SPNG;
break;
case SPR2_JUMP: case SPR2_JUMP:
spr2 = ((player spr2 = ((player
? player->charflags ? player->charflags
: skin->flags) : skin->flags)
& SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_ROLL; & SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_ROLL;
break; break;
case SPR2_SPNG: // spring
spr2 = SPR2_FALL;
break;
case SPR2_FALL:
spr2 = SPR2_WALK;
break;
case SPR2_RIDE:
spr2 = SPR2_FALL;
break;
case SPR2_FLY :
spr2 = SPR2_SPNG;
break;
case SPR2_SWIM:
spr2 = SPR2_FLY ;
break;
case SPR2_TIRE: case SPR2_TIRE:
spr2 = (player && player->charability == CA_SWIM) ? SPR2_SWIM : SPR2_FLY; spr2 = (player && player->charability == CA_SWIM) ? SPR2_SWIM : SPR2_FLY;
break; break;
case SPR2_GLID: // Use the handy list, that's what it's there for!
spr2 = SPR2_FLY;
break;
case SPR2_CLMB:
spr2 = SPR2_ROLL;
break;
case SPR2_CLNG:
spr2 = SPR2_CLMB;
break;
case SPR2_FLT :
spr2 = SPR2_WALK;
break;
case SPR2_FRUN:
spr2 = SPR2_RUN ;
break;
case SPR2_DASH:
spr2 = SPR2_FRUN;
break;
case SPR2_BNCE:
spr2 = SPR2_FALL;
break;
case SPR2_BLND:
spr2 = SPR2_ROLL;
break;
case SPR2_TWIN:
spr2 = SPR2_ROLL;
break;
case SPR2_MLEE:
spr2 = SPR2_TWIN;
break;
// NiGHTS sprites.
case SPR2_NSTD:
spr2 = SPR2_STND;
super = FF_SPR2SUPER;
break;
case SPR2_NFLT:
spr2 = SPR2_FLT ;
super = FF_SPR2SUPER;
break;
case SPR2_NSTN:
spr2 = SPR2_STUN;
break;
case SPR2_NPUL:
spr2 = SPR2_NSTN;
break;
case SPR2_NATK:
spr2 = SPR2_ROLL;
super = FF_SPR2SUPER;
break;
/*case SPR2_NGT0:
spr2 = SPR2_NFLT;
break;*/
case SPR2_NGT1:
case SPR2_NGT7:
case SPR2_DRL0:
spr2 = SPR2_NGT0;
break;
case SPR2_NGT2:
case SPR2_DRL1:
spr2 = SPR2_NGT1;
break;
case SPR2_NGT3:
case SPR2_DRL2:
spr2 = SPR2_NGT2;
break;
case SPR2_NGT4:
case SPR2_DRL3:
spr2 = SPR2_NGT3;
break;
case SPR2_NGT5:
case SPR2_DRL4:
spr2 = SPR2_NGT4;
break;
case SPR2_NGT6:
case SPR2_DRL5:
spr2 = SPR2_NGT5;
break;
case SPR2_DRL6:
spr2 = SPR2_NGT6;
break;
case SPR2_NGT8:
case SPR2_DRL7:
spr2 = SPR2_NGT7;
break;
case SPR2_NGT9:
case SPR2_DRL8:
spr2 = SPR2_NGT8;
break;
case SPR2_NGTA:
case SPR2_DRL9:
spr2 = SPR2_NGT9;
break;
case SPR2_NGTB:
case SPR2_DRLA:
spr2 = SPR2_NGTA;
break;
case SPR2_NGTC:
case SPR2_DRLB:
spr2 = SPR2_NGTB;
break;
case SPR2_DRLC:
spr2 = SPR2_NGTC;
break;
// Dunno? Just go to standing then.
default: default:
spr2 = SPR2_STND; spr2 = spr2defaults[spr2];
break; break;
} }

View File

@ -173,6 +173,9 @@ void SCR_SetMode(void)
if (SCR_IsAspectCorrect(vid.width, vid.height)) if (SCR_IsAspectCorrect(vid.width, vid.height))
CONS_Alert(CONS_WARNING, M_GetText("Resolution is not aspect-correct!\nUse a multiple of %dx%d\n"), BASEVIDWIDTH, BASEVIDHEIGHT); CONS_Alert(CONS_WARNING, M_GetText("Resolution is not aspect-correct!\nUse a multiple of %dx%d\n"), BASEVIDWIDTH, BASEVIDHEIGHT);
#endif*/ #endif*/
wallcolfunc = walldrawerfunc;
// set the apprpriate drawer for the sky (tall or INT16) // set the apprpriate drawer for the sky (tall or INT16)
setmodeneeded = 0; setmodeneeded = 0;
} }

View File

@ -684,6 +684,8 @@ static inline void ST_drawRings(void)
static void ST_drawLives(void) static void ST_drawLives(void)
{ {
const INT32 v_splitflag = (splitscreen && stplyr == &players[displayplayer] ? V_SPLITSCREEN : 0); const INT32 v_splitflag = (splitscreen && stplyr == &players[displayplayer] ? V_SPLITSCREEN : 0);
INT32 livescount;
boolean notgreyedout;
if (!stplyr->skincolor) if (!stplyr->skincolor)
return; // Just joined a server, skin isn't loaded yet! return; // Just joined a server, skin isn't loaded yet!
@ -723,66 +725,47 @@ static void ST_drawLives(void)
V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, stlivex); V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, stlivex);
// lives number // lives number
if ((netgame || multiplayer) && gametype == GT_COOP) if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 3)
{ {
switch (cv_cooplives.value) INT32 i;
livescount = 0;
notgreyedout = (stplyr->lives > 0);
for (i = 0; i < MAXPLAYERS; i++)
{ {
case 0: if (!playeringame[i])
V_DrawCharacter(hudinfo[HUD_LIVESNUM].x - 8, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), '\x16' | 0x80 | V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, false); continue;
return;
case 3:
{
INT32 i, sum = 0;
boolean canrespawn = (stplyr->lives > 0);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].lives < 1) if (players[i].lives < 1)
continue; continue;
if (players[i].lives > 1) if (players[i].lives > 1)
canrespawn = true; notgreyedout = true;
sum += (players[i].lives); if (players[i].lives == 0x7f)
} {
V_DrawRightAlignedString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), livescount = 0x7f;
V_SNAPTOLEFT|V_SNAPTOBOTTOM|(canrespawn ? V_HUDTRANS : V_HUDTRANSHALF)|v_splitflag,
va("%d",sum));
return;
}
#if 0 // render the number of lives you COULD steal
case 2:
{
INT32 i, sum = 0;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (&players[i] == stplyr)
continue;
if (players[i].lives < 2)
continue;
sum += (players[i].lives - 1);
}
V_DrawString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0),
V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANSHALF|v_splitflag, va("/%d",sum));
}
// intentional fallthrough
#endif
default:
// don't return so the SP one can be drawn below
break; break;
}
else if (livescount < 99)
livescount += (players[i].lives);
} }
} }
else
{
livescount = stplyr->lives;
notgreyedout = true;
}
V_DrawRightAlignedString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), if (livescount == 0x7f)
V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, V_DrawCharacter(hudinfo[HUD_LIVESNUM].x - 8, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), '\x16' | 0x80 | V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, false);
va("%d",stplyr->lives)); else
{
if (livescount > 99)
livescount = 99;
V_DrawRightAlignedString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0),
V_SNAPTOLEFT|V_SNAPTOBOTTOM|(notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF)|v_splitflag,
((livescount > 99) ? "!!" : va("%d",livescount)));
}
} }
static void ST_drawLevelTitle(void) static void ST_drawLevelTitle(void)

View File

@ -524,12 +524,21 @@ static inline UINT8 transmappedpdraw(const UINT8 *dest, const UINT8 *source, fix
{ {
return *(v_translevel + (((*(v_colormap + source[ofs>>FRACBITS]))<<8)&0xff00) + (*dest&0xff)); return *(v_translevel + (((*(v_colormap + source[ofs>>FRACBITS]))<<8)&0xff00) + (*dest&0xff));
} }
static UINT8 staticstep = 0;
static fixed_t staticval = 0;
static inline UINT8 staticpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs) static inline UINT8 staticpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs)
{ {
UINT8 val = source[ofs>>FRACBITS]; UINT8 val = source[ofs>>FRACBITS];
(void)dest; (void)dest;
if ((++staticstep) >= 4)
{
staticstep = 0;
staticval = M_RandomFixed();
}
if (val < 7) return val; if (val < 7) return val;
return M_RandomKey(7+1)+(val-7);//M_RandomByte(); return ((staticval>>staticstep)&7)+(val-7);
} }
// Draws a patch scaled to arbitrary size. // Draws a patch scaled to arbitrary size.
@ -660,30 +669,10 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t
y = FixedMul(y,dupy<<FRACBITS); y = FixedMul(y,dupy<<FRACBITS);
x >>= FRACBITS; x >>= FRACBITS;
y >>= FRACBITS; y >>= FRACBITS;
desttop += (y*vid.width) + x;
// Center it if necessary // Center it if necessary
if (!(scrn & V_SCALEPATCHMASK)) if (!(scrn & V_SCALEPATCHMASK))
{ {
if (vid.width != BASEVIDWIDTH * dupx)
{
// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
// so center this imaginary screen
if (scrn & V_SNAPTORIGHT)
desttop += (vid.width - (BASEVIDWIDTH * dupx));
else if (!(scrn & V_SNAPTOLEFT))
desttop += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
}
if (vid.height != BASEVIDHEIGHT * dupy)
{
// same thing here
if ((scrn & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM))
desttop += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy)) * vid.width;
else if (scrn & V_SNAPTOBOTTOM)
desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width;
else if (!(scrn & V_SNAPTOTOP))
desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2;
}
// if it's meant to cover the whole screen, black out the rest // if it's meant to cover the whole screen, black out the rest
if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT) if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT)
{ {
@ -691,7 +680,29 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t
source = (const UINT8 *)(column) + 3; source = (const UINT8 *)(column) + 3;
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0]));
} }
if (vid.width != BASEVIDWIDTH * dupx)
{
// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
// so center this imaginary screen
if (scrn & V_SNAPTORIGHT)
x += (vid.width - (BASEVIDWIDTH * dupx));
else if (!(scrn & V_SNAPTOLEFT))
x += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
}
if (vid.height != BASEVIDHEIGHT * dupy)
{
// same thing here
if ((scrn & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM))
y += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy));
else if (scrn & V_SNAPTOBOTTOM)
y += (vid.height - (BASEVIDHEIGHT * dupy));
else if (!(scrn & V_SNAPTOTOP))
y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2;
}
} }
desttop += (y*vid.width) + x;
} }
if (pscale != FRACUNIT) // scale width properly if (pscale != FRACUNIT) // scale width properly
@ -797,28 +808,10 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_
y = FixedMul(y,dupy<<FRACBITS); y = FixedMul(y,dupy<<FRACBITS);
x >>= FRACBITS; x >>= FRACBITS;
y >>= FRACBITS; y >>= FRACBITS;
desttop += (y*vid.width) + x;
// Center it if necessary // Center it if necessary
if (!(scrn & V_SCALEPATCHMASK)) if (!(scrn & V_SCALEPATCHMASK))
{ {
if (vid.width != BASEVIDWIDTH * dupx)
{
// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
// so center this imaginary screen
if (scrn & V_SNAPTORIGHT)
desttop += (vid.width - (BASEVIDWIDTH * dupx));
else if (!(scrn & V_SNAPTOLEFT))
desttop += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
}
if (vid.height != BASEVIDHEIGHT * dupy)
{
// same thing here
if (scrn & V_SNAPTOBOTTOM)
desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width;
else if (!(scrn & V_SNAPTOTOP))
desttop += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2;
}
// if it's meant to cover the whole screen, black out the rest // if it's meant to cover the whole screen, black out the rest
if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT) if (x == 0 && SHORT(patch->width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT)
{ {
@ -826,7 +819,29 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_
source = (const UINT8 *)(column) + 3; source = (const UINT8 *)(column) + 3;
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0]));
} }
if (vid.width != BASEVIDWIDTH * dupx)
{
// dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
// so center this imaginary screen
if (scrn & V_SNAPTORIGHT)
x += (vid.width - (BASEVIDWIDTH * dupx));
else if (!(scrn & V_SNAPTOLEFT))
x += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
}
if (vid.height != BASEVIDHEIGHT * dupy)
{
// same thing here
if ((scrn & (V_SPLITSCREEN|V_SNAPTOBOTTOM)) == (V_SPLITSCREEN|V_SNAPTOBOTTOM))
y += (vid.height/2 - (BASEVIDHEIGHT/2 * dupy));
else if (scrn & V_SNAPTOBOTTOM)
y += (vid.height - (BASEVIDHEIGHT * dupy));
else if (!(scrn & V_SNAPTOTOP))
y += (vid.height - (BASEVIDHEIGHT * dupy)) / 2;
}
} }
desttop += (y*vid.width) + x;
} }
for (col = sx<<FRACBITS; (col>>FRACBITS) < SHORT(patch->width) && (col>>FRACBITS) < w; col += colfrac, ++x, desttop++) for (col = sx<<FRACBITS; (col>>FRACBITS) < SHORT(patch->width) && (col>>FRACBITS) < w; col += colfrac, ++x, desttop++)
@ -1346,7 +1361,7 @@ char *V_WordWrap(INT32 x, INT32 w, INT32 option, const char *string)
// //
void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string)
{ {
INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth = BASEVIDWIDTH, center = 0; INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0;
const char *ch = string; const char *ch = string;
INT32 charflags = 0; INT32 charflags = 0;
const UINT8 *colormap = NULL; const UINT8 *colormap = NULL;
@ -1362,7 +1377,11 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string)
scrwidth = vid.width; scrwidth = vid.width;
} }
else else
{
dupx = dupy = 1; dupx = dupy = 1;
scrwidth = vid.width/vid.dupx;
left = (scrwidth - BASEVIDWIDTH)/2;
}
charflags = (option & V_CHARCOLORMASK); charflags = (option & V_CHARCOLORMASK);
@ -1422,9 +1441,9 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string)
else else
w = SHORT(hu_font[c]->width) * dupx; w = SHORT(hu_font[c]->width) * dupx;
if (cx + w > scrwidth) if (cx+left > scrwidth)
break; break;
if (cx < 0) //left boundary check if (cx+left + w < 0) //left boundary check
{ {
cx += w; cx += w;
continue; continue;
@ -1455,7 +1474,7 @@ void V_DrawRightAlignedString(INT32 x, INT32 y, INT32 option, const char *string
// //
void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string) void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string)
{ {
INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth = BASEVIDWIDTH, center = 0; INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, center = 0, left = 0;
const char *ch = string; const char *ch = string;
INT32 charflags = 0; INT32 charflags = 0;
const UINT8 *colormap = NULL; const UINT8 *colormap = NULL;
@ -1471,7 +1490,11 @@ void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string)
scrwidth = vid.width; scrwidth = vid.width;
} }
else else
{
dupx = dupy = 1; dupx = dupy = 1;
scrwidth = vid.width/vid.dupx;
left = (scrwidth - BASEVIDWIDTH)/2;
}
charflags = (option & V_CHARCOLORMASK); charflags = (option & V_CHARCOLORMASK);
@ -1529,9 +1552,9 @@ void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string)
} }
else else
w = SHORT(hu_font[c]->width) * dupx / 2; w = SHORT(hu_font[c]->width) * dupx / 2;
if (cx + w > scrwidth) if (cx+left > scrwidth)
break; break;
if (cx < 0) //left boundary check if (cx+left + w < 0) //left boundary check
{ {
cx += w; cx += w;
continue; continue;
@ -1556,7 +1579,7 @@ void V_DrawRightAlignedSmallString(INT32 x, INT32 y, INT32 option, const char *s
// //
void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string) void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string)
{ {
INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth = BASEVIDWIDTH; INT32 w, c, cx = x, cy = y, dupx, dupy, scrwidth, left = 0;
const char *ch = string; const char *ch = string;
INT32 charflags = 0; INT32 charflags = 0;
const UINT8 *colormap = NULL; const UINT8 *colormap = NULL;
@ -1572,7 +1595,11 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string)
scrwidth = vid.width; scrwidth = vid.width;
} }
else else
{
dupx = dupy = 1; dupx = dupy = 1;
scrwidth = vid.width/vid.dupx;
left = (scrwidth - BASEVIDWIDTH)/2;
}
charflags = (option & V_CHARCOLORMASK); charflags = (option & V_CHARCOLORMASK);
@ -1628,9 +1655,9 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string)
else else
w = (SHORT(tny_font[c]->width) * dupx); w = (SHORT(tny_font[c]->width) * dupx);
if (cx + w > scrwidth) if (cx+left > scrwidth)
break; break;
if (cx < 0) //left boundary check if (cx+left + w < 0) //left boundary check
{ {
cx += w; cx += w;
continue; continue;
@ -1653,7 +1680,7 @@ void V_DrawRightAlignedThinString(INT32 x, INT32 y, INT32 option, const char *st
void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
{ {
fixed_t cx = x, cy = y; fixed_t cx = x, cy = y;
INT32 w, c, dupx, dupy, scrwidth = BASEVIDWIDTH, center = 0; INT32 w, c, dupx, dupy, scrwidth, center = 0, left = 0;
const char *ch = string; const char *ch = string;
INT32 spacewidth = 4, charwidth = 0; INT32 spacewidth = 4, charwidth = 0;
@ -1667,7 +1694,11 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
scrwidth = vid.width; scrwidth = vid.width;
} }
else else
{
dupx = dupy = 1; dupx = dupy = 1;
scrwidth = vid.width/vid.dupx;
left = (scrwidth - BASEVIDWIDTH)/2;
}
switch (option & V_SPACINGMASK) switch (option & V_SPACINGMASK)
{ {
@ -1720,9 +1751,9 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string)
else else
w = SHORT(hu_font[c]->width) * dupx; w = SHORT(hu_font[c]->width) * dupx;
if ((cx>>FRACBITS) + w > scrwidth) if ((cx>>FRACBITS)+left > scrwidth)
break; break;
if (cx < 0) //left boundary check if (cx+left + w < 0) //left boundary check
{ {
cx += w<<FRACBITS; cx += w<<FRACBITS;
continue; continue;

View File

@ -475,7 +475,7 @@ void Y_IntermissionDrawer(void)
V_DrawRightAlignedString(x+152, y, 0, va("%i", data.match.scores[i])); V_DrawRightAlignedString(x+152, y, 0, va("%i", data.match.scores[i]));
else if (intertype == int_race) else if (intertype == int_race)
{ {
if (players[data.match.num[i]].pflags & PF_TIMEOVER) if (players[data.match.num[i]].pflags & PF_GAMETYPEOVER)
snprintf(strtime, sizeof strtime, "DNF"); snprintf(strtime, sizeof strtime, "DNF");
else else
snprintf(strtime, sizeof strtime, snprintf(strtime, sizeof strtime,
@ -493,7 +493,7 @@ void Y_IntermissionDrawer(void)
V_DrawRightAlignedString(x+152+BASEVIDWIDTH/2, y, 0, va("%u", data.match.scores[i])); V_DrawRightAlignedString(x+152+BASEVIDWIDTH/2, y, 0, va("%u", data.match.scores[i]));
else if (intertype == int_race) else if (intertype == int_race)
{ {
if (players[data.match.num[i]].pflags & PF_TIMEOVER) if (players[data.match.num[i]].pflags & PF_GAMETYPEOVER)
snprintf(strtime, sizeof strtime, "DNF"); snprintf(strtime, sizeof strtime, "DNF");
else else
snprintf(strtime, sizeof strtime, "%i:%02i.%02i", G_TicsToMinutes(data.match.scores[i], true), snprintf(strtime, sizeof strtime, "%i:%02i.%02i", G_TicsToMinutes(data.match.scores[i], true),
@ -643,7 +643,7 @@ void Y_IntermissionDrawer(void)
// already constrained to 8 characters // already constrained to 8 characters
V_DrawString(x+36, y, V_ALLOWLOWERCASE, data.competition.name[i]); V_DrawString(x+36, y, V_ALLOWLOWERCASE, data.competition.name[i]);
if (players[data.competition.num[i]].pflags & PF_TIMEOVER) if (players[data.competition.num[i]].pflags & PF_GAMETYPEOVER)
snprintf(sstrtime, sizeof sstrtime, "Time Over"); snprintf(sstrtime, sizeof sstrtime, "Time Over");
else if (players[data.competition.num[i]].lives <= 0) else if (players[data.competition.num[i]].lives <= 0)
snprintf(sstrtime, sizeof sstrtime, "Game Over"); snprintf(sstrtime, sizeof sstrtime, "Game Over");