Merge remote-tracking branch 'origin/next' into next-newcontrols

This commit is contained in:
fickleheart 2020-01-04 09:57:09 -06:00
commit f8a3e3b898
32 changed files with 1061 additions and 442 deletions

View File

@ -339,27 +339,6 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
jump_last = jump; jump_last = jump;
spin_last = spin; spin_last = spin;
// ********
// Thinkfly overlay
if (thinkfly)
{
if (!tails->hnext)
{
P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
if (tails->hnext)
{
P_SetTarget(&tails->hnext->target, tails);
P_SetTarget(&tails->hnext->hprev, tails);
P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR);
}
}
}
else if (tails->hnext && tails->hnext->type == MT_OVERLAY && tails->hnext->state == states+S_FLIGHTINDICATOR)
{
P_RemoveMobj(tails->hnext);
P_SetTarget(&tails->hnext, NULL);
}
// Turn the virtual keypresses into ticcmd_t. // Turn the virtual keypresses into ticcmd_t.
B_KeysToTiccmd(tails, cmd, forward, backward, left, right, false, false, jump, spin); B_KeysToTiccmd(tails, cmd, forward, backward, left, right, false, false, jump, spin);
@ -569,3 +548,30 @@ void B_RespawnBot(INT32 playernum)
P_SetScale(tails, sonic->scale); P_SetScale(tails, sonic->scale);
tails->destscale = sonic->destscale; tails->destscale = sonic->destscale;
} }
void B_HandleFlightIndicator(player_t *player)
{
mobj_t *tails = player->mo;
if (!tails)
return;
if (thinkfly && player->bot == 1 && tails->health)
{
if (!tails->hnext)
{
P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
if (tails->hnext)
{
P_SetTarget(&tails->hnext->target, tails);
P_SetTarget(&tails->hnext->hprev, tails);
P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR);
}
}
}
else if (tails->hnext && tails->hnext->type == MT_OVERLAY && tails->hnext->state == states+S_FLIGHTINDICATOR)
{
P_RemoveMobj(tails->hnext);
P_SetTarget(&tails->hnext, NULL);
}
}

View File

@ -15,3 +15,4 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
boolean B_CheckRespawn(player_t *player); boolean B_CheckRespawn(player_t *player);
void B_MoveBlocked(player_t *player); void B_MoveBlocked(player_t *player);
void B_RespawnBot(INT32 playernum); void B_RespawnBot(INT32 playernum);
void B_HandleFlightIndicator(player_t *player);

View File

@ -54,7 +54,7 @@ static void COM_Add_f(void);
static void CV_EnforceExecVersion(void); static void CV_EnforceExecVersion(void);
static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr); static boolean CV_FilterVarByVersion(consvar_t *v, const char *valstr);
static boolean CV_Command(void); static boolean CV_Command(void);
static consvar_t *CV_FindVar(const char *name); consvar_t *CV_FindVar(const char *name);
static const char *CV_StringValue(const char *var_name); static const char *CV_StringValue(const char *var_name);
static consvar_t *consvar_vars; // list of registered console variables static consvar_t *consvar_vars; // list of registered console variables
@ -1106,7 +1106,7 @@ static const char *cv_null_string = "";
* \return Pointer to the variable if found, or NULL. * \return Pointer to the variable if found, or NULL.
* \sa CV_FindNetVar * \sa CV_FindNetVar
*/ */
static consvar_t *CV_FindVar(const char *name) consvar_t *CV_FindVar(const char *name)
{ {
consvar_t *cvar; consvar_t *cvar;

View File

@ -149,6 +149,9 @@ void CV_ToggleExecVersion(boolean enable);
// register a variable for use at the console // register a variable for use at the console
void CV_RegisterVar(consvar_t *variable); void CV_RegisterVar(consvar_t *variable);
// returns a console variable by name
consvar_t *CV_FindVar(const char *name);
// sets changed to 0 for every console variable // sets changed to 0 for every console variable
void CV_ClearChangedFlags(void); void CV_ClearChangedFlags(void);

View File

@ -1293,7 +1293,8 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value; netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
netbuffer->u.serverinfo.gametype = (UINT8)gametype; strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype],
sizeof netbuffer->u.serverinfo.gametypename);
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated; netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated;
@ -2122,13 +2123,10 @@ static void CL_ConnectToServer(boolean viams)
if (i != -1) if (i != -1)
{ {
UINT16 num = serverlist[i].info.gametype; char *gametypestr = serverlist[i].info.gametypename;
const char *gametypestr = NULL;
CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername);
if (num < gametypecount) gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0';
gametypestr = Gametype_Names[num]; CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr);
if (gametypestr)
CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr);
CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100, CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100,
serverlist[i].info.version%100, serverlist[i].info.subversion); serverlist[i].info.version%100, serverlist[i].info.subversion);
} }
@ -3656,6 +3654,8 @@ static void HandleServerInfo(SINT8 node)
netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0; netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0;
netbuffer->u.serverinfo.application netbuffer->u.serverinfo.application
[sizeof netbuffer->u.serverinfo.application - 1] = '\0'; [sizeof netbuffer->u.serverinfo.application - 1] = '\0';
netbuffer->u.serverinfo.gametypename
[sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0';
SL_InsertServer(&netbuffer->u.serverinfo, node); SL_InsertServer(&netbuffer->u.serverinfo, node);
} }

View File

@ -27,7 +27,7 @@ This version is independent of the mod name, and standard
version and subversion. It should only account for the version and subversion. It should only account for the
basic fields of the packet, and change infrequently. basic fields of the packet, and change infrequently.
*/ */
#define PACKETVERSION 0 #define PACKETVERSION 1
// Network play related stuff. // Network play related stuff.
// There is a data struct that stores network // There is a data struct that stores network
@ -361,7 +361,7 @@ typedef struct
UINT8 subversion; UINT8 subversion;
UINT8 numberofplayer; UINT8 numberofplayer;
UINT8 maxplayer; UINT8 maxplayer;
UINT8 gametype; char gametypename[24];
UINT8 modifiedgame; UINT8 modifiedgame;
UINT8 cheatsenabled; UINT8 cheatsenabled;
UINT8 isdedicated; UINT8 isdedicated;

View File

@ -766,7 +766,7 @@ void D_StartTitle(void)
if (netgame) if (netgame)
{ {
if (gametype == GT_COOP) if (gametyperules & GTR_CAMPAIGN)
{ {
G_SetGamestate(GS_WAITINGPLAYERS); // hack to prevent a command repeat G_SetGamestate(GS_WAITINGPLAYERS); // hack to prevent a command repeat

View File

@ -3726,7 +3726,7 @@ static void CoopStarposts_OnChange(void)
{ {
INT32 i; INT32 i;
if (!(netgame || multiplayer) || gametype != GT_COOP) if (!(netgame || multiplayer) || !G_GametypeUsesCoopStarposts())
return; return;
switch (cv_coopstarposts.value) switch (cv_coopstarposts.value)
@ -3781,7 +3781,7 @@ static void CoopLives_OnChange(void)
{ {
INT32 i; INT32 i;
if (!(netgame || multiplayer) || gametype != GT_COOP) if (!(netgame || multiplayer) || !G_GametypeUsesCoopLives())
return; return;
switch (cv_cooplives.value) switch (cv_cooplives.value)

View File

@ -8906,32 +8906,35 @@ static const char *const GAMETYPERULE_LIST[] = {
"CAMPAIGN", "CAMPAIGN",
"RINGSLINGER", "RINGSLINGER",
"SPECTATORS", "SPECTATORS",
"FRIENDLYFIRE",
"LIVES", "LIVES",
"TEAMS", "TEAMS",
"FIRSTPERSON",
"POWERSTONES",
"TEAMFLAGS",
"FRIENDLY",
"SPECIALSTAGES",
"EMERALDTOKENS",
"EMERALDHUNT",
"RACE", "RACE",
"TAG", "TAG",
"POINTLIMIT", "POINTLIMIT",
"TIMELIMIT", "TIMELIMIT",
"HIDETIME", "OVERTIME",
"HURTMESSAGES",
"FRIENDLYFIRE",
"STARTCOUNTDOWN",
"HIDEFROZEN", "HIDEFROZEN",
"BLINDFOLDED", "BLINDFOLDED",
"FIRSTPERSON", "RESPAWNDELAY",
"MATCHEMERALDS",
"TEAMFLAGS",
"PITYSHIELD", "PITYSHIELD",
"DEATHPENALTY", "DEATHPENALTY",
"NOSPECTATORSPAWN", "NOSPECTATORSPAWN",
"DEATHMATCHSTARTS", "DEATHMATCHSTARTS",
"SPECIALSTAGES", "SPAWNINVUL",
"EMERALDTOKENS",
"EMERALDHUNT",
"SPAWNENEMIES", "SPAWNENEMIES",
"ALLOWEXIT", "ALLOWEXIT",
"NOTITLECARD", "NOTITLECARD",
"OVERTIME", "CUTSCENES",
"HURTMESSAGES",
"SPAWNINVUL",
NULL NULL
}; };

View File

@ -464,6 +464,8 @@ extern void *(*M_Memcpy)(void* dest, const void* src, size_t n) FUNCNONNULL;
char *va(const char *format, ...) FUNCPRINTF; char *va(const char *format, ...) FUNCPRINTF;
char *M_GetToken(const char *inputString); char *M_GetToken(const char *inputString);
void M_UnGetToken(void); void M_UnGetToken(void);
UINT32 M_GetTokenPos(void);
void M_SetTokenPos(UINT32 newPos);
char *sizeu1(size_t num); char *sizeu1(size_t num);
char *sizeu2(size_t num); char *sizeu2(size_t num);
char *sizeu3(size_t num); char *sizeu3(size_t num);

View File

@ -397,32 +397,35 @@ enum GameTypeRules
GTR_CAMPAIGN = 1, // Linear Co-op map progression, don't allow random maps GTR_CAMPAIGN = 1, // Linear Co-op map progression, don't allow random maps
GTR_RINGSLINGER = 1<<1, // Outside of Co-op, Competition, and Race (overriden by cv_ringslinger) GTR_RINGSLINGER = 1<<1, // Outside of Co-op, Competition, and Race (overriden by cv_ringslinger)
GTR_SPECTATORS = 1<<2, // Outside of Co-op, Competition, and Race GTR_SPECTATORS = 1<<2, // Outside of Co-op, Competition, and Race
GTR_FRIENDLYFIRE = 1<<3, // Always allow friendly fire GTR_LIVES = 1<<3, // Co-op and Competition
GTR_LIVES = 1<<4, // Co-op and Competition GTR_TEAMS = 1<<4, // Team Match, CTF
GTR_TEAMS = 1<<5, // Team Match, CTF GTR_FIRSTPERSON = 1<<5, // First person camera
GTR_RACE = 1<<6, // Race and Competition GTR_POWERSTONES = 1<<6, // Power stones (Match and CTF)
GTR_TAG = 1<<7, // Tag and Hide and Seek GTR_TEAMFLAGS = 1<<7, // Gametype has team flags (CTF)
GTR_POINTLIMIT = 1<<8, // Ringslinger point limit GTR_FRIENDLY = 1<<8, // Co-op
GTR_TIMELIMIT = 1<<9, // Ringslinger time limit GTR_SPECIALSTAGES = 1<<9, // Allow special stages
GTR_HIDETIME = 1<<10, // Hide time (Tag and Hide and Seek) GTR_EMERALDTOKENS = 1<<10, // Spawn emerald tokens
GTR_HIDEFROZEN = 1<<11, // Frozen after hide time (Hide and Seek, but not Tag) GTR_EMERALDHUNT = 1<<11, // Emerald Hunt
GTR_BLINDFOLDED = 1<<12, // Blindfolded view (Tag and Hide and Seek) GTR_RACE = 1<<12, // Race and Competition
GTR_FIRSTPERSON = 1<<13, // First person camera GTR_TAG = 1<<13, // Tag and Hide and Seek
GTR_MATCHEMERALDS = 1<<14, // Ringslinger emeralds (Match and CTF) GTR_POINTLIMIT = 1<<14, // Ringslinger point limit
GTR_TEAMFLAGS = 1<<15, // Gametype has team flags (CTF) GTR_TIMELIMIT = 1<<15, // Ringslinger time limit
GTR_PITYSHIELD = 1<<16, // Award pity shield GTR_OVERTIME = 1<<16, // Allow overtime
GTR_DEATHPENALTY = 1<<17, // Death score penalty GTR_HURTMESSAGES = 1<<17, // Hit and death messages
GTR_NOSPECTATORSPAWN = 1<<18, // Use with GTR_SPECTATORS, spawn in the map instead of with the spectators GTR_FRIENDLYFIRE = 1<<18, // Always allow friendly fire
GTR_DEATHMATCHSTARTS = 1<<19, // Use deathmatch starts GTR_STARTCOUNTDOWN = 1<<19, // Hide time countdown (Tag and Hide and Seek)
GTR_SPECIALSTAGES = 1<<20, // Allow special stages GTR_HIDEFROZEN = 1<<20, // Frozen after hide time (Hide and Seek, but not Tag)
GTR_EMERALDTOKENS = 1<<21, // Spawn emerald tokens GTR_BLINDFOLDED = 1<<21, // Blindfolded view (Tag and Hide and Seek)
GTR_EMERALDHUNT = 1<<22, // Emerald Hunt GTR_RESPAWNDELAY = 1<<22, // Respawn delay
GTR_SPAWNENEMIES = 1<<23, // Spawn enemies GTR_PITYSHIELD = 1<<23, // Award pity shield
GTR_ALLOWEXIT = 1<<24, // Allow exit sectors GTR_DEATHPENALTY = 1<<24, // Death score penalty
GTR_NOTITLECARD = 1<<25, // Don't show the title card GTR_NOSPECTATORSPAWN = 1<<25, // Use with GTR_SPECTATORS, spawn in the map instead of with the spectators
GTR_OVERTIME = 1<<26, // Allow overtime GTR_DEATHMATCHSTARTS = 1<<26, // Use deathmatch starts
GTR_HURTMESSAGES = 1<<27, // Hit and death messages GTR_SPAWNINVUL = 1<<27, // Babysitting deterrent
GTR_SPAWNINVUL = 1<<28, // Babysitting deterrent GTR_SPAWNENEMIES = 1<<28, // Spawn enemies
GTR_ALLOWEXIT = 1<<29, // Allow exit sectors
GTR_NOTITLECARD = 1<<30, // Don't show the title card
GTR_CUTSCENES = 1<<31, // Play cutscenes, ending, credits, and evaluation
}; };
// String names for gametypes // String names for gametypes

View File

@ -1617,6 +1617,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver
B_BuildTiccmd(player, cmd); B_BuildTiccmd(player, cmd);
} }
B_HandleFlightIndicator(player);
} }
else if (player->bot == 2) else if (player->bot == 2)
*myangle = localangle; // Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Legacy *myangle = localangle; // Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Legacy
@ -2676,8 +2677,7 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost)
// -- DM/Tag/CTF-spectator/etc -- // -- DM/Tag/CTF-spectator/etc --
// Order: DM->CTF->Coop // Order: DM->CTF->Coop
else if ((gametyperules & GTR_DEATHMATCHSTARTS) || gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF else if ((gametyperules & GTR_DEATHMATCHSTARTS) && !(players[playernum].pflags & PF_TAGIT))
|| ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && !(players[playernum].pflags & PF_TAGIT)))
{ {
if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start
&& !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start && !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start
@ -2878,11 +2878,11 @@ void G_DoReborn(INT32 playernum)
if (countdowntimeup || (!(netgame || multiplayer) && gametype == GT_COOP)) if (countdowntimeup || (!(netgame || multiplayer) && gametype == GT_COOP))
resetlevel = true; resetlevel = true;
else if (gametype == GT_COOP && (netgame || multiplayer) && !G_IsSpecialStage(gamemap)) else if ((G_GametypeUsesCoopLives() || G_GametypeUsesCoopStarposts()) && (netgame || multiplayer) && !G_IsSpecialStage(gamemap))
{ {
boolean notgameover = true; boolean notgameover = true;
if (cv_cooplives.value != 0 && player->lives <= 0) // consider game over first if (G_GametypeUsesCoopLives() && (cv_cooplives.value != 0 && player->lives <= 0)) // consider game over first
{ {
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -2917,7 +2917,7 @@ void G_DoReborn(INT32 playernum)
} }
} }
if (notgameover && cv_coopstarposts.value == 2) if (G_GametypeUsesCoopStarposts() && (notgameover && cv_coopstarposts.value == 2))
{ {
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -2993,7 +2993,7 @@ void G_DoReborn(INT32 playernum)
} }
// restore time in netgame (see also p_setup.c) // restore time in netgame (see also p_setup.c)
if ((netgame || multiplayer) && gametype == GT_COOP && cv_coopstarposts.value == 2) if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
{ {
// is this a hack? maybe // is this a hack? maybe
tic_t maxstarposttime = 0; tic_t maxstarposttime = 0;
@ -3064,7 +3064,7 @@ void G_AddPlayer(INT32 playernum)
if (!players[i].exiting) if (!players[i].exiting)
notexiting++; notexiting++;
if (!(cv_coopstarposts.value && (gametype == GT_COOP) && (p->starpostnum < players[i].starpostnum))) if (!(cv_coopstarposts.value && G_GametypeUsesCoopStarposts() && (p->starpostnum < players[i].starpostnum)))
continue; continue;
p->starpostscale = players[i].starpostscale; p->starpostscale = players[i].starpostscale;
@ -3181,24 +3181,24 @@ const char *Gametype_ConstantNames[NUMGAMETYPES] =
UINT32 gametypedefaultrules[NUMGAMETYPES] = UINT32 gametypedefaultrules[NUMGAMETYPES] =
{ {
// Co-op // Co-op
GTR_CAMPAIGN|GTR_LIVES|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_EMERALDHUNT|GTR_EMERALDTOKENS|GTR_SPECIALSTAGES, GTR_CAMPAIGN|GTR_LIVES|GTR_FRIENDLY|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_EMERALDHUNT|GTR_EMERALDTOKENS|GTR_SPECIALSTAGES|GTR_CUTSCENES,
// Competition // Competition
GTR_RACE|GTR_LIVES|GTR_SPAWNENEMIES|GTR_EMERALDTOKENS|GTR_SPAWNINVUL|GTR_ALLOWEXIT, GTR_RACE|GTR_LIVES|GTR_SPAWNENEMIES|GTR_EMERALDTOKENS|GTR_SPAWNINVUL|GTR_ALLOWEXIT,
// Race // Race
GTR_RACE|GTR_SPAWNENEMIES|GTR_SPAWNINVUL|GTR_ALLOWEXIT, GTR_RACE|GTR_SPAWNENEMIES|GTR_SPAWNINVUL|GTR_ALLOWEXIT,
// Match // Match
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_MATCHEMERALDS|GTR_SPAWNINVUL|GTR_PITYSHIELD|GTR_DEATHPENALTY, GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD|GTR_DEATHPENALTY,
// Team Match // Team Match
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_SPAWNINVUL|GTR_PITYSHIELD, GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD,
// Tag // Tag
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_HIDETIME|GTR_BLINDFOLDED|GTR_SPAWNINVUL, GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
// Hide and Seek // Hide and Seek
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_HIDETIME|GTR_BLINDFOLDED|GTR_SPAWNINVUL, GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
// CTF // CTF
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_MATCHEMERALDS|GTR_SPAWNINVUL|GTR_PITYSHIELD, GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD,
}; };
// //
@ -3239,50 +3239,68 @@ INT16 G_AddGametype(UINT32 rules)
// //
void G_AddGametypeConstant(INT16 gtype, const char *newgtconst) void G_AddGametypeConstant(INT16 gtype, const char *newgtconst)
{ {
char *gtconst = Z_Malloc(strlen(newgtconst) + 3, PU_STATIC, NULL); size_t r = 0; // read
// Copy GT_ and the gametype name. size_t w = 0; // write
strcpy(gtconst, "GT_"); char *gtconst = Z_Calloc(strlen(newgtconst) + 3, PU_STATIC, NULL);
strcat(gtconst, newgtconst); char *tmpconst = Z_Calloc(strlen(newgtconst), PU_STATIC, NULL);
// Copy the gametype name.
strcpy(tmpconst, newgtconst);
// Make uppercase. // Make uppercase.
strupr(gtconst); strupr(tmpconst);
// Remove characters.
#define REMOVECHAR(chr) \ // Prepare to write the new constant string now.
{ \ strcpy(gtconst, "GT_");
char *chrfind = strchr(gtconst, chr); \
while (chrfind) \ // Remove characters that will not be allowed in the constant string.
{ \ for (; r < strlen(tmpconst); r++)
*chrfind = '_'; \ {
chrfind = strchr(chrfind, chr); \ boolean writechar = true;
} \ char rc = tmpconst[r];
switch (rc)
{
// Space, at sign and question mark
case ' ':
case '@':
case '?':
// Used for operations
case '+':
case '-':
case '*':
case '/':
case '%':
case '^':
case '&':
case '!':
// Part of Lua's syntax
case '#':
case '=':
case '~':
case '<':
case '>':
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
case ':':
case ';':
case ',':
case '.':
writechar = false;
break;
}
if (writechar)
{
gtconst[3 + w] = rc;
w++;
}
} }
// Space // Free the temporary string.
REMOVECHAR(' ') Z_Free(tmpconst);
// Used for operations
REMOVECHAR('+')
REMOVECHAR('-')
REMOVECHAR('*')
REMOVECHAR('/')
REMOVECHAR('%')
REMOVECHAR('^')
// Part of Lua's syntax
REMOVECHAR('#')
REMOVECHAR('=')
REMOVECHAR('~')
REMOVECHAR('<')
REMOVECHAR('>')
REMOVECHAR('(')
REMOVECHAR(')')
REMOVECHAR('{')
REMOVECHAR('}')
REMOVECHAR('[')
REMOVECHAR(']')
REMOVECHAR(':')
REMOVECHAR(';')
REMOVECHAR(',')
REMOVECHAR('.')
#undef REMOVECHAR
// Finally, set the constant string. // Finally, set the constant string.
Gametype_ConstantNames[gtype] = gtconst; Gametype_ConstantNames[gtype] = gtconst;
@ -3427,6 +3445,28 @@ boolean G_GametypeUsesLives(void)
return false; return false;
} }
//
// G_GametypeUsesCoopLives
//
// Returns true if the current gametype uses
// the cooplives CVAR. False otherwise.
//
boolean G_GametypeUsesCoopLives(void)
{
return (gametyperules & (GTR_LIVES|GTR_FRIENDLY)) == (GTR_LIVES|GTR_FRIENDLY);
}
//
// G_GametypeUsesCoopStarposts
//
// Returns true if the current gametype uses
// the coopstarposts CVAR. False otherwise.
//
boolean G_GametypeUsesCoopStarposts(void)
{
return (gametyperules & GTR_FRIENDLY);
}
// //
// G_GametypeHasTeams // G_GametypeHasTeams
// //
@ -3480,6 +3520,16 @@ boolean G_TagGametype(void)
return (gametyperules & GTR_TAG); return (gametyperules & GTR_TAG);
} }
//
// G_CompetitionGametype
//
// For gametypes that are race gametypes, and have lives.
//
boolean G_CompetitionGametype(void)
{
return ((gametyperules & GTR_RACE) && (gametyperules & GTR_LIVES));
}
/** Get the typeoflevel flag needed to indicate support of a gametype. /** Get the typeoflevel flag needed to indicate support of a gametype.
* In single-player, this always returns TOL_SP. * In single-player, this always returns TOL_SP.
* \param gametype The gametype for which support is desired. * \param gametype The gametype for which support is desired.
@ -3721,7 +3771,7 @@ void G_AfterIntermission(void)
HU_ClearCEcho(); HU_ClearCEcho();
if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene. if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene.
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false); F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false);
else else
{ {
@ -3831,7 +3881,7 @@ static void G_DoContinued(void)
void G_EndGame(void) void G_EndGame(void)
{ {
// Only do evaluation and credits in coop games. // Only do evaluation and credits in coop games.
if (gametype == GT_COOP) if (gametyperules & GTR_CUTSCENES)
{ {
if (nextmap == 1103-1) // end game with ending if (nextmap == 1103-1) // end game with ending
{ {
@ -4534,7 +4584,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
automapactive = false; automapactive = false;
imcontinuing = false; imcontinuing = false;
if (!skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking) // Start a custom cutscene. if ((gametyperules & GTR_CUTSCENES) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking) // Start a custom cutscene.
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer); F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer);
else else
G_DoLoadLevel(resetplayer); G_DoLoadLevel(resetplayer);

View File

@ -245,11 +245,14 @@ void G_SetGametypeDescription(INT16 gtype, char *descriptiontext, UINT8 leftcolo
INT32 G_GetGametypeByName(const char *gametypestr); INT32 G_GetGametypeByName(const char *gametypestr);
boolean G_IsSpecialStage(INT32 mapnum); boolean G_IsSpecialStage(INT32 mapnum);
boolean G_GametypeUsesLives(void); boolean G_GametypeUsesLives(void);
boolean G_GametypeUsesCoopLives(void);
boolean G_GametypeUsesCoopStarposts(void);
boolean G_GametypeHasTeams(void); boolean G_GametypeHasTeams(void);
boolean G_GametypeHasSpectators(void); boolean G_GametypeHasSpectators(void);
boolean G_RingSlingerGametype(void); boolean G_RingSlingerGametype(void);
boolean G_PlatformGametype(void); boolean G_PlatformGametype(void);
boolean G_TagGametype(void); boolean G_TagGametype(void);
boolean G_CompetitionGametype(void);
boolean G_EnoughPlayersFinished(void); boolean G_EnoughPlayersFinished(void);
void G_ExitLevel(void); void G_ExitLevel(void);
void G_NextLevel(void); void G_NextLevel(void);

View File

@ -2445,7 +2445,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
} }
} }
if (G_GametypeUsesLives() && !(gametyperankings[gametype] == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives if (G_GametypeUsesLives() && !(G_GametypeUsesCoopLives() && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //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)
{ {
@ -2754,7 +2754,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() && !(gametyperankings[gametype] == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives if (G_GametypeUsesLives() && !(G_GametypeUsesCoopLives() && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //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

@ -2854,6 +2854,22 @@ static int lib_gGametypeUsesLives(lua_State *L)
return 1; return 1;
} }
static int lib_gGametypeUsesCoopLives(lua_State *L)
{
//HUDSAFE
INLEVEL
lua_pushboolean(L, G_GametypeUsesCoopLives());
return 1;
}
static int lib_gGametypeUsesCoopStarposts(lua_State *L)
{
//HUDSAFE
INLEVEL
lua_pushboolean(L, G_GametypeUsesCoopStarposts());
return 1;
}
static int lib_gGametypeHasTeams(lua_State *L) static int lib_gGametypeHasTeams(lua_State *L)
{ {
//HUDSAFE //HUDSAFE
@ -2894,6 +2910,14 @@ static int lib_gTagGametype(lua_State *L)
return 1; return 1;
} }
static int lib_gCompetitionGametype(lua_State *L)
{
//HUDSAFE
INLEVEL
lua_pushboolean(L, G_CompetitionGametype());
return 1;
}
static int lib_gTicsToHours(lua_State *L) static int lib_gTicsToHours(lua_State *L)
{ {
tic_t rtic = luaL_checkinteger(L, 1); tic_t rtic = luaL_checkinteger(L, 1);
@ -3139,11 +3163,14 @@ static luaL_Reg lib[] = {
{"G_ExitLevel",lib_gExitLevel}, {"G_ExitLevel",lib_gExitLevel},
{"G_IsSpecialStage",lib_gIsSpecialStage}, {"G_IsSpecialStage",lib_gIsSpecialStage},
{"G_GametypeUsesLives",lib_gGametypeUsesLives}, {"G_GametypeUsesLives",lib_gGametypeUsesLives},
{"G_GametypeUsesCoopLives",lib_gGametypeUsesCoopLives},
{"G_GametypeUsesCoopStarposts",lib_gGametypeUsesCoopStarposts},
{"G_GametypeHasTeams",lib_gGametypeHasTeams}, {"G_GametypeHasTeams",lib_gGametypeHasTeams},
{"G_GametypeHasSpectators",lib_gGametypeHasSpectators}, {"G_GametypeHasSpectators",lib_gGametypeHasSpectators},
{"G_RingSlingerGametype",lib_gRingSlingerGametype}, {"G_RingSlingerGametype",lib_gRingSlingerGametype},
{"G_PlatformGametype",lib_gPlatformGametype}, {"G_PlatformGametype",lib_gPlatformGametype},
{"G_TagGametype",lib_gTagGametype}, {"G_TagGametype",lib_gTagGametype},
{"G_CompetitionGametype",lib_gCompetitionGametype},
{"G_TicsToHours",lib_gTicsToHours}, {"G_TicsToHours",lib_gTicsToHours},
{"G_TicsToMinutes",lib_gTicsToMinutes}, {"G_TicsToMinutes",lib_gTicsToMinutes},
{"G_TicsToSeconds",lib_gTicsToSeconds}, {"G_TicsToSeconds",lib_gTicsToSeconds},

View File

@ -427,6 +427,26 @@ static int lib_cvRegisterVar(lua_State *L)
return 1; return 1;
} }
static int lib_cvFindVar(lua_State *L)
{
consvar_t *cv;
if (( cv = CV_FindVar(luaL_checkstring(L,1)) ))
{
lua_settop(L,1);/* We only want one argument in the stack. */
lua_pushlightuserdata(L, cv);/* Now the second value on stack. */
luaL_getmetatable(L, META_CVAR);
/*
The metatable is the last value on the stack, so this
applies it to the second value, which is the cvar.
*/
lua_setmetatable(L,2);
lua_pushvalue(L,2);
return 1;
}
else
return 0;
}
// CONS_Printf for a single player // CONS_Printf for a single player
// Use 'print' in baselib for a global message. // Use 'print' in baselib for a global message.
static int lib_consPrintf(lua_State *L) static int lib_consPrintf(lua_State *L)
@ -466,6 +486,7 @@ static luaL_Reg lib[] = {
{"COM_BufAddText", lib_comBufAddText}, {"COM_BufAddText", lib_comBufAddText},
{"COM_BufInsertText", lib_comBufInsertText}, {"COM_BufInsertText", lib_comBufInsertText},
{"CV_RegisterVar", lib_cvRegisterVar}, {"CV_RegisterVar", lib_cvRegisterVar},
{"CV_FindVar", lib_cvFindVar},
{"CONS_Printf", lib_consPrintf}, {"CONS_Printf", lib_consPrintf},
{NULL, NULL} {NULL, NULL}
}; };

View File

@ -53,6 +53,7 @@ enum hook {
hook_IntermissionThinker, hook_IntermissionThinker,
hook_TeamSwitch, hook_TeamSwitch,
hook_ViewpointSwitch, hook_ViewpointSwitch,
hook_SeenPlayer,
hook_MAX // last hook hook_MAX // last hook
}; };
@ -97,5 +98,8 @@ void LUAh_PlayerQuit(player_t *plr, int reason); // Hook for player quitting
void LUAh_IntermissionThinker(void); // Hook for Y_Ticker void LUAh_IntermissionThinker(void); // Hook for Y_Ticker
boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh.... boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh....
UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode
#ifdef SEENAMES
boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend); // Hook for MT_NAMECHECK
#endif
#endif #endif

View File

@ -64,6 +64,7 @@ const char *const hookNames[hook_MAX+1] = {
"IntermissionThinker", "IntermissionThinker",
"TeamSwitch", "TeamSwitch",
"ViewpointSwitch", "ViewpointSwitch",
"SeenPlayer",
NULL NULL
}; };
@ -207,6 +208,7 @@ static int lib_addHook(lua_State *L)
case hook_PlayerCanDamage: case hook_PlayerCanDamage:
case hook_TeamSwitch: case hook_TeamSwitch:
case hook_ViewpointSwitch: case hook_ViewpointSwitch:
case hook_SeenPlayer:
case hook_ShieldSpawn: case hook_ShieldSpawn:
case hook_ShieldSpecial: case hook_ShieldSpecial:
lastp = &playerhooks; lastp = &playerhooks;
@ -1412,7 +1414,7 @@ UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean
return 0; return 0;
lua_settop(gL, 0); lua_settop(gL, 0);
hud_running = true; hud_running = true; // local hook
for (hookp = playerhooks; hookp; hookp = hookp->next) for (hookp = playerhooks; hookp; hookp = hookp->next)
{ {
@ -1453,4 +1455,49 @@ UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean
return canSwitchView; return canSwitchView;
} }
// Hook for MT_NAMECHECK
#ifdef SEENAMES
boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend)
{
hook_p hookp;
boolean hasSeenPlayer = true;
if (!gL || !(hooksAvailable[hook_SeenPlayer/8] & (1<<(hook_SeenPlayer%8))))
return 0;
lua_settop(gL, 0);
hud_running = true; // local hook
for (hookp = playerhooks; hookp; hookp = hookp->next)
{
if (hookp->type != hook_SeenPlayer)
continue;
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, player, META_PLAYER);
LUA_PushUserdata(gL, seenfriend, META_PLAYER);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -3);
lua_pushvalue(gL, -3);
if (lua_pcall(gL, 2, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
hookp->error = true;
continue;
}
if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1))
hasSeenPlayer = false; // Hasn't seen player
lua_pop(gL, 1);
}
lua_settop(gL, 0);
hud_running = false;
return hasSeenPlayer;
}
#endif // SEENAMES
#endif #endif

View File

@ -299,9 +299,7 @@ int LUA_PushGlobals(lua_State *L, const char *word)
// See the above. // See the above.
int LUA_CheckGlobals(lua_State *L, const char *word) int LUA_CheckGlobals(lua_State *L, const char *word)
{ {
if (fastcmp(word, "gametyperules")) if (fastcmp(word, "redscore"))
gametyperules = (UINT32)luaL_checkinteger(L, 2);
else if (fastcmp(word, "redscore"))
redscore = (UINT32)luaL_checkinteger(L, 2); redscore = (UINT32)luaL_checkinteger(L, 2);
else if (fastcmp(word, "bluescore")) else if (fastcmp(word, "bluescore"))
bluescore = (UINT32)luaL_checkinteger(L, 2); bluescore = (UINT32)luaL_checkinteger(L, 2);

View File

@ -4966,7 +4966,7 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt)
if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE))
return true; return true;
if (gt > 0 && gt < gametypecount && (mapheaderinfo[mapnum]->typeoflevel & gametypetol[gt])) if (gt >= 0 && gt < gametypecount && (mapheaderinfo[mapnum]->typeoflevel & gametypetol[gt]))
return true; return true;
return false; return false;
@ -10202,7 +10202,7 @@ static void M_DrawRoomMenu(void)
static void M_DrawConnectMenu(void) static void M_DrawConnectMenu(void)
{ {
UINT16 i; UINT16 i;
const char *gt = "Unknown"; char *gt;
INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE; INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE;
for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++) for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++)
@ -10246,14 +10246,17 @@ static void M_DrawConnectMenu(void)
V_DrawSmallString(currentMenu->x, S_LINEY(i)+8, globalflags, V_DrawSmallString(currentMenu->x, S_LINEY(i)+8, globalflags,
va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time))); va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time)));
gt = "Unknown"; gt = serverlist[slindex].info.gametypename;
if (serverlist[slindex].info.gametype < gametypecount)
gt = Gametype_Names[serverlist[slindex].info.gametype];
V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags, V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags,
va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer)); va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer));
V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, va("Gametype: %s", gt)); if (strlen(gt) > 11)
gt = va("Gametype: %.11s...", gt);
else
gt = va("Gametype: %s", gt);
V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, gt);
MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL; MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL;
} }
@ -10294,7 +10297,15 @@ SERVER_LIST_ENTRY_COMPARATOR(time)
SERVER_LIST_ENTRY_COMPARATOR(numberofplayer) SERVER_LIST_ENTRY_COMPARATOR(numberofplayer)
SERVER_LIST_ENTRY_COMPARATOR_REVERSE(numberofplayer) SERVER_LIST_ENTRY_COMPARATOR_REVERSE(numberofplayer)
SERVER_LIST_ENTRY_COMPARATOR_REVERSE(maxplayer) SERVER_LIST_ENTRY_COMPARATOR_REVERSE(maxplayer)
SERVER_LIST_ENTRY_COMPARATOR(gametype)
static int ServerListEntryComparator_gametypename(const void *entry1, const void *entry2)
{
const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2;
int c;
if (( c = strcasecmp(sa->info.gametypename, sb->info.gametypename) ))
return c;
return strcmp(sa->info.servername, sb->info.servername); \
}
// Special one for modified state. // Special one for modified state.
static int ServerListEntryComparator_modified(const void *entry1, const void *entry2) static int ServerListEntryComparator_modified(const void *entry1, const void *entry2)
@ -10334,7 +10345,7 @@ void M_SortServerList(void)
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse); qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse);
break; break;
case 5: // Gametype. case 5: // Gametype.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametype); qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename);
break; break;
} }
#endif #endif

View File

@ -1908,6 +1908,20 @@ void M_UnGetToken(void)
endPos = oldendPos; endPos = oldendPos;
} }
/** Returns the current token's position.
*/
UINT32 M_GetTokenPos(void)
{
return endPos;
}
/** Sets the current token's position.
*/
void M_SetTokenPos(UINT32 newPos)
{
endPos = newPos;
}
/** Count bits in a number. /** Count bits in a number.
*/ */
UINT8 M_CountBits(UINT32 num, UINT8 size) UINT8 M_CountBits(UINT32 num, UINT8 size)

View File

@ -1451,7 +1451,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (player->starpostnum >= special->health) if (player->starpostnum >= special->health)
return; // Already hit this post return; // Already hit this post
if (cv_coopstarposts.value && gametype == GT_COOP && (netgame || multiplayer)) if (cv_coopstarposts.value && G_GametypeUsesCoopStarposts() && (netgame || multiplayer))
{ {
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -1807,7 +1807,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return; return;
case MT_MINECARTSPAWNER: case MT_MINECARTSPAWNER:
if (!player->bot && (special->fuse < TICRATE || player->powers[pw_carry] != CR_MINECART)) if (!player->bot && special->fuse <= TICRATE && player->powers[pw_carry] != CR_MINECART)
{ {
mobj_t *mcart = P_SpawnMobj(special->x, special->y, special->z, MT_MINECART); mobj_t *mcart = P_SpawnMobj(special->x, special->y, special->z, MT_MINECART);
P_SetTarget(&mcart->target, toucher); P_SetTarget(&mcart->target, toucher);
@ -2521,7 +2521,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
target->colorized = false; target->colorized = false;
G_GhostAddColor(GHC_NORMAL); G_GhostAddColor(GHC_NORMAL);
if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0)) if ((target->player->lives <= 1) && (netgame || multiplayer) && G_GametypeUsesCoopLives() && (cv_cooplives.value == 0))
; ;
else if (!target->player->bot && !target->player->spectator && (target->player->lives != INFLIVES) else if (!target->player->bot && !target->player->spectator && (target->player->lives != INFLIVES)
&& G_GametypeUsesLives()) && G_GametypeUsesLives())
@ -2531,7 +2531,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if (target->player->lives <= 0) // Tails 03-14-2000 if (target->player->lives <= 0) // Tails 03-14-2000
{ {
boolean gameovermus = false; boolean gameovermus = false;
if ((netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value != 1)) if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && (cv_cooplives.value != 1))
{ {
INT32 i; INT32 i;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
@ -3199,10 +3199,12 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
player->powers[pw_carry] = CR_NONE; player->powers[pw_carry] = CR_NONE;
// Burst weapons and emeralds in Match/CTF only // Burst weapons and emeralds in Match/CTF only
if (source && (gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF)) if (source)
{ {
P_PlayerRingBurst(player, player->rings); if ((gametyperules & GTR_RINGSLINGER) && !(gametyperules & GTR_TAG))
P_PlayerEmeraldBurst(player, false); P_PlayerRingBurst(player, player->rings);
if (gametyperules & GTR_POWERSTONES)
P_PlayerEmeraldBurst(player, false);
} }
// Get rid of shield // Get rid of shield

View File

@ -745,9 +745,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
// So that NOTHING ELSE can see MT_NAMECHECK because it is client-side. // So that NOTHING ELSE can see MT_NAMECHECK because it is client-side.
if (tmthing->type == MT_NAMECHECK) if (tmthing->type == MT_NAMECHECK)
{ {
// Ignore things that aren't players, ignore spectators, ignore yourself. // Ignore things that aren't players, ignore spectators, ignore yourself.
// (also don't bother to check that tmthing->target->player is non-NULL because we're not actually using it here.) if (!thing->player || !(tmthing->target && tmthing->target->player) || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player))
if (!thing->player || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player))
return true; return true;
// Now check that you actually hit them. // Now check that you actually hit them.
@ -760,6 +759,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->z + tmthing->height < thing->z) if (tmthing->z + tmthing->height < thing->z)
return true; // underneath return true; // underneath
#ifdef HAVE_BLUA
// REX HAS SEEN YOU
if (!LUAh_SeenPlayer(tmthing->target->player, thing->player))
return false;
#endif
seenplayer = thing->player; seenplayer = thing->player;
return false; return false;
} }

View File

@ -11823,7 +11823,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
if (!cv_powerstones.value) if (!cv_powerstones.value)
return false; return false;
if (!(gametyperules & GTR_MATCHEMERALDS)) if (!(gametyperules & GTR_POWERSTONES))
return false; return false;
runemeraldmanager = true; runemeraldmanager = true;

View File

@ -83,6 +83,8 @@
#include "p_slopes.h" #include "p_slopes.h"
#endif #endif
#include "fastcmp.h" // textmap parsing
// //
// Map MD5, calculated on level load. // Map MD5, calculated on level load.
// Sent to clients in PT_SERVERINFO. // Sent to clients in PT_SERVERINFO.
@ -863,11 +865,6 @@ static void P_InitializeSector(sector_t *ss)
ss->lightingdata = NULL; ss->lightingdata = NULL;
ss->fadecolormapdata = NULL; ss->fadecolormapdata = NULL;
ss->floor_xoffs = ss->floor_yoffs = 0;
ss->ceiling_xoffs = ss->ceiling_yoffs = 0;
ss->floorpic_angle = ss->ceilingpic_angle = 0;
ss->heightsec = -1; ss->heightsec = -1;
ss->camsec = -1; ss->camsec = -1;
@ -943,6 +940,11 @@ static void P_LoadSectors(UINT8 *data)
ss->special = SHORT(ms->special); ss->special = SHORT(ms->special);
ss->tag = SHORT(ms->tag); ss->tag = SHORT(ms->tag);
ss->floor_xoffs = ss->floor_yoffs = 0;
ss->ceiling_xoffs = ss->ceiling_yoffs = 0;
ss->floorpic_angle = ss->ceilingpic_angle = 0;
P_InitializeSector(ss); P_InitializeSector(ss);
} }
} }
@ -1012,16 +1014,34 @@ static void P_InitializeLinedef(line_t *ld)
{ {
sides[ld->sidenum[0]].special = ld->special; sides[ld->sidenum[0]].special = ld->special;
sides[ld->sidenum[0]].line = ld; sides[ld->sidenum[0]].line = ld;
} }
if (ld->sidenum[1] != 0xffff) if (ld->sidenum[1] != 0xffff)
{ {
sides[ld->sidenum[1]].special = ld->special; sides[ld->sidenum[1]].special = ld->special;
sides[ld->sidenum[1]].line = ld; sides[ld->sidenum[1]].line = ld;
} }
} }
static void P_SetLinedefV1(size_t i, UINT16 vertex_num)
{
if (vertex_num >= numvertexes)
{
CONS_Debug(DBG_SETUP, "P_SetLinedefV1: linedef %s has out-of-range v1 num %u\n", sizeu1(i), vertex_num);
vertex_num = 0;
}
lines[i].v1 = &vertexes[vertex_num];
}
static void P_SetLinedefV2(size_t i, UINT16 vertex_num)
{
if (vertex_num >= numvertexes)
{
CONS_Debug(DBG_SETUP, "P_SetLinedefV2: linedef %s has out-of-range v2 num %u\n", sizeu1(i), vertex_num);
vertex_num = 0;
}
lines[i].v2 = &vertexes[vertex_num];
}
static void P_LoadLinedefs(UINT8 *data) static void P_LoadLinedefs(UINT8 *data)
{ {
maplinedef_t *mld = (maplinedef_t *)data; maplinedef_t *mld = (maplinedef_t *)data;
@ -1033,8 +1053,8 @@ static void P_LoadLinedefs(UINT8 *data)
ld->flags = SHORT(mld->flags); ld->flags = SHORT(mld->flags);
ld->special = SHORT(mld->special); ld->special = SHORT(mld->special);
ld->tag = SHORT(mld->tag); ld->tag = SHORT(mld->tag);
ld->v1 = &vertexes[SHORT(mld->v1)]; P_SetLinedefV1(i, SHORT(mld->v1));
ld->v2 = &vertexes[SHORT(mld->v2)]; P_SetLinedefV2(i, SHORT(mld->v2));
ld->sidenum[0] = SHORT(mld->sidenum[0]); ld->sidenum[0] = SHORT(mld->sidenum[0]);
ld->sidenum[1] = SHORT(mld->sidenum[1]); ld->sidenum[1] = SHORT(mld->sidenum[1]);
@ -1043,6 +1063,30 @@ static void P_LoadLinedefs(UINT8 *data)
} }
} }
static void P_SetSidedefSector(size_t i, UINT16 sector_num)
{
// cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead
if (sector_num >= numsectors)
{
CONS_Debug(DBG_SETUP, "P_SetSidedefSector: sidedef %s has out-of-range sector num %u\n", sizeu1(i), sector_num);
sector_num = 0;
}
sides[i].sector = &sectors[sector_num];
}
static void P_InitializeSidedef(side_t *sd)
{
if (!sd->line)
{
CONS_Debug(DBG_SETUP, "P_LoadSidedefs: Sidedef %s is not used by any linedef\n", sizeu1((size_t)(sd - sides)));
sd->line = &lines[0];
sd->special = sd->line->special;
}
sd->text = NULL;
sd->colormap_data = NULL;
}
static void P_LoadSidedefs(UINT8 *data) static void P_LoadSidedefs(UINT8 *data)
{ {
mapsidedef_t *msd = (mapsidedef_t*)data; mapsidedef_t *msd = (mapsidedef_t*)data;
@ -1051,22 +1095,28 @@ static void P_LoadSidedefs(UINT8 *data)
for (i = 0; i < numsides; i++, sd++, msd++) for (i = 0; i < numsides; i++, sd++, msd++)
{ {
UINT16 sector_num; INT16 textureoffset = SHORT(msd->textureoffset);
boolean isfrontside = !sd->line || sd->line->sidenum[0] == i; boolean isfrontside;
sd->textureoffset = SHORT(msd->textureoffset)<<FRACBITS; P_InitializeSidedef(sd);
isfrontside = sd->line->sidenum[0] == i;
// Repeat count for midtexture
if (((sd->line->flags & (ML_TWOSIDED|ML_EFFECT5)) == (ML_TWOSIDED|ML_EFFECT5))
&& !(sd->special >= 300 && sd->special < 500)) // exempt linedef exec specials
{
sd->repeatcnt = (INT16)(((unsigned)textureoffset) >> 12);
sd->textureoffset = (((unsigned)textureoffset) & 2047) << FRACBITS;
}
else
{
sd->repeatcnt = 0;
sd->textureoffset = textureoffset << FRACBITS;
}
sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS; sd->rowoffset = SHORT(msd->rowoffset)<<FRACBITS;
// cph 2006/09/30 - catch out-of-range sector numbers; use sector 0 instead P_SetSidedefSector(i, SHORT(msd->sector));
sector_num = SHORT(msd->sector);
if (sector_num >= numsectors)
{
CONS_Debug(DBG_SETUP, "P_LoadSidedefs: sidedef %s has out-of-range sector num %u\n", sizeu1(i), sector_num);
sector_num = 0;
}
sd->sector = &sectors[sector_num];
sd->colormap_data = NULL;
// Special info stored in texture fields! // Special info stored in texture fields!
switch (sd->special) switch (sd->special)
@ -1233,29 +1283,400 @@ static void P_LoadThings(UINT8 *data)
mt->z = mt->options; // NiGHTS Hoops use the full flags bits to set the height. mt->z = mt->options; // NiGHTS Hoops use the full flags bits to set the height.
else else
mt->z = mt->options >> ZSHIFT; mt->z = mt->options >> ZSHIFT;
mt->mobj = NULL;
} }
} }
static void P_LoadMapData(const virtres_t *virt) // Stores positions for relevant map data spread through a TEXTMAP.
UINT32 mapthingsPos[UINT16_MAX];
UINT32 linesPos[UINT16_MAX];
UINT32 sidesPos[UINT16_MAX];
UINT32 vertexesPos[UINT16_MAX];
UINT32 sectorsPos[UINT16_MAX];
// Determine total amount of map data in TEXTMAP.
static boolean TextmapCount(UINT8 *data, size_t size)
{ {
virtlump_t* virtvertexes = NULL, * virtsectors = NULL, * virtsidedefs = NULL, * virtlinedefs = NULL, * virtthings = NULL; char *tkn = M_GetToken((char *)data);
#ifdef UDMF UINT8 brackets = 0;
virtlump_t* textmap = vres_Find(virt, "TEXTMAP");
nummapthings = 0;
numlines = 0;
numsides = 0;
numvertexes = 0;
numsectors = 0;
// Look for namespace at the beginning.
if (!fastcmp(tkn, "namespace"))
{
Z_Free(tkn);
CONS_Alert(CONS_ERROR, "No namespace at beginning of lump!\n");
return false;
}
Z_Free(tkn);
// Check if namespace is valid.
tkn = M_GetToken(NULL);
if (!fastcmp(tkn, "srb2"))
CONS_Alert(CONS_WARNING, "Invalid namespace '%s', only 'srb2' is supported.\n", tkn);
Z_Free(tkn);
tkn = M_GetToken(NULL);
while (tkn && M_GetTokenPos() < size)
{
// Avoid anything inside bracketed stuff, only look for external keywords.
if (brackets)
{
if (fastcmp(tkn, "}"))
brackets--;
}
else if (fastcmp(tkn, "{"))
brackets++;
// Check for valid fields.
else if (fastcmp(tkn, "thing"))
mapthingsPos[nummapthings++] = M_GetTokenPos();
else if (fastcmp(tkn, "linedef"))
linesPos[numlines++] = M_GetTokenPos();
else if (fastcmp(tkn, "sidedef"))
sidesPos[numsides++] = M_GetTokenPos();
else if (fastcmp(tkn, "vertex"))
vertexesPos[numvertexes++] = M_GetTokenPos();
else if (fastcmp(tkn, "sector"))
sectorsPos[numsectors++] = M_GetTokenPos();
else
CONS_Alert(CONS_NOTICE, "Unknown field '%s'.\n", tkn);
Z_Free(tkn);
tkn = M_GetToken(NULL);
}
Z_Free(tkn);
if (brackets)
{
CONS_Alert(CONS_ERROR, "Unclosed brackets detected in textmap lump.\n");
return false;
}
return true;
}
static void ParseTextmapVertexParameter(UINT32 i, char *param, char *val)
{
if (fastcmp(param, "x"))
vertexes[i].x = FLOAT_TO_FIXED(atof(val));
else if (fastcmp(param, "y"))
vertexes[i].y = FLOAT_TO_FIXED(atof(val));
}
static void ParseTextmapSectorParameter(UINT32 i, char *param, char *val)
{
if (fastcmp(param, "heightfloor"))
sectors[i].floorheight = atol(val) << FRACBITS;
else if (fastcmp(param, "heightceiling"))
sectors[i].ceilingheight = atol(val) << FRACBITS;
if (fastcmp(param, "texturefloor"))
sectors[i].floorpic = P_AddLevelFlat(val, foundflats);
else if (fastcmp(param, "textureceiling"))
sectors[i].ceilingpic = P_AddLevelFlat(val, foundflats);
else if (fastcmp(param, "lightlevel"))
sectors[i].lightlevel = atol(val);
else if (fastcmp(param, "special"))
sectors[i].special = atol(val);
else if (fastcmp(param, "id"))
sectors[i].tag = atol(val);
else if (fastcmp(param, "xpanningfloor"))
sectors[i].floor_xoffs = FLOAT_TO_FIXED(atof(val));
else if (fastcmp(param, "ypanningfloor"))
sectors[i].floor_yoffs = FLOAT_TO_FIXED(atof(val));
else if (fastcmp(param, "xpanningceiling"))
sectors[i].ceiling_xoffs = FLOAT_TO_FIXED(atof(val));
else if (fastcmp(param, "ypanningceiling"))
sectors[i].ceiling_yoffs = FLOAT_TO_FIXED(atof(val));
else if (fastcmp(param, "rotationfloor"))
sectors[i].floorpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
else if (fastcmp(param, "rotationceiling"))
sectors[i].ceilingpic_angle = FixedAngle(FLOAT_TO_FIXED(atof(val)));
}
static void ParseTextmapSidedefParameter(UINT32 i, char *param, char *val)
{
if (fastcmp(param, "offsetx"))
sides[i].textureoffset = atol(val)<<FRACBITS;
else if (fastcmp(param, "offsety"))
sides[i].rowoffset = atol(val)<<FRACBITS;
else if (fastcmp(param, "texturetop"))
sides[i].toptexture = R_TextureNumForName(val);
else if (fastcmp(param, "texturebottom"))
sides[i].bottomtexture = R_TextureNumForName(val);
else if (fastcmp(param, "texturemiddle"))
sides[i].midtexture = R_TextureNumForName(val);
else if (fastcmp(param, "sector"))
P_SetSidedefSector(i, atol(val));
else if (fastcmp(param, "repeatcnt"))
sides[i].repeatcnt = atol(val);
}
static void ParseTextmapLinedefParameter(UINT32 i, char *param, char *val)
{
if (fastcmp(param, "id"))
lines[i].tag = atol(val);
else if (fastcmp(param, "special"))
lines[i].special = atol(val);
else if (fastcmp(param, "v1"))
P_SetLinedefV1(i, atol(val));
else if (fastcmp(param, "v2"))
P_SetLinedefV2(i, atol(val));
else if (fastcmp(param, "sidefront"))
lines[i].sidenum[0] = atol(val);
else if (fastcmp(param, "sideback"))
lines[i].sidenum[1] = atol(val);
// Flags
else if (fastcmp(param, "blocking") && fastcmp("true", val))
lines[i].flags |= ML_IMPASSIBLE;
else if (fastcmp(param, "blockmonsters") && fastcmp("true", val))
lines[i].flags |= ML_BLOCKMONSTERS;
else if (fastcmp(param, "twosided") && fastcmp("true", val))
lines[i].flags |= ML_TWOSIDED;
else if (fastcmp(param, "dontpegtop") && fastcmp("true", val))
lines[i].flags |= ML_DONTPEGTOP;
else if (fastcmp(param, "dontpegbottom") && fastcmp("true", val))
lines[i].flags |= ML_DONTPEGBOTTOM;
else if (fastcmp(param, "skewtd") && fastcmp("true", val))
lines[i].flags |= ML_EFFECT1;
else if (fastcmp(param, "noclimb") && fastcmp("true", val))
lines[i].flags |= ML_NOCLIMB;
else if (fastcmp(param, "noskew") && fastcmp("true", val))
lines[i].flags |= ML_EFFECT2;
else if (fastcmp(param, "midpeg") && fastcmp("true", val))
lines[i].flags |= ML_EFFECT3;
else if (fastcmp(param, "midsolid") && fastcmp("true", val))
lines[i].flags |= ML_EFFECT4;
else if (fastcmp(param, "wrapmidtex") && fastcmp("true", val))
lines[i].flags |= ML_EFFECT5;
else if (fastcmp(param, "effect6") && fastcmp("true", val))
lines[i].flags |= ML_EFFECT6;
else if (fastcmp(param, "nonet") && fastcmp("true", val))
lines[i].flags |= ML_NONET;
else if (fastcmp(param, "netonly") && fastcmp("true", val))
lines[i].flags |= ML_NETONLY;
else if (fastcmp(param, "bouncy") && fastcmp("true", val))
lines[i].flags |= ML_BOUNCY;
else if (fastcmp(param, "transfer") && fastcmp("true", val))
lines[i].flags |= ML_TFERLINE;
}
static void ParseTextmapThingParameter(UINT32 i, char *param, char *val)
{
if (fastcmp(param, "x"))
mapthings[i].x = atol(val);
else if (fastcmp(param, "y"))
mapthings[i].y = atol(val);
else if (fastcmp(param, "height"))
mapthings[i].z = atol(val);
else if (fastcmp(param, "angle"))
mapthings[i].angle = atol(val);
else if (fastcmp(param, "type"))
mapthings[i].type = atol(val);
// Flags
else if (fastcmp(param, "extra") && fastcmp("true", val))
mapthings[i].options |= MTF_EXTRA;
else if (fastcmp(param, "flip") && fastcmp("true", val))
mapthings[i].options |= MTF_OBJECTFLIP;
else if (fastcmp(param, "special") && fastcmp("true", val))
mapthings[i].options |= MTF_OBJECTSPECIAL;
else if (fastcmp(param, "ambush") && fastcmp("true", val))
mapthings[i].options |= MTF_AMBUSH;
}
/** From a given position table, run a specified parser function through a {}-encapsuled text.
*
* \param Position of the data to parse, in the textmap.
* \param Structure number (mapthings, sectors, ...).
* \param Parser function pointer.
*/
static void TextmapParse(UINT32 dataPos, size_t num, void (*parser)(UINT32, char *, char *))
{
char *param, *val;
M_SetTokenPos(dataPos);
param = M_GetToken(NULL);
if (!fastcmp(param, "{"))
{
Z_Free(param);
CONS_Alert(CONS_WARNING, "Invalid UDMF data capsule!\n");
return;
}
Z_Free(param);
while (true)
{
param = M_GetToken(NULL);
if (fastcmp(param, "}"))
{
Z_Free(param);
break;
}
val = M_GetToken(NULL);
parser(num, param, val);
Z_Free(param);
Z_Free(val);
}
}
/** Provides a fix to the flat alignment coordinate transform from standard Textmaps.
*/
static void TextmapFixFlatOffsets(sector_t *sec)
{
if (sec->floorpic_angle)
{
fixed_t pc = FINECOSINE(sec->floorpic_angle>>ANGLETOFINESHIFT);
fixed_t ps = FINESINE (sec->floorpic_angle>>ANGLETOFINESHIFT);
fixed_t xoffs = sec->floor_xoffs;
fixed_t yoffs = sec->floor_yoffs;
sec->floor_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
sec->floor_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
}
if (sec->ceilingpic_angle)
{
fixed_t pc = FINECOSINE(sec->ceilingpic_angle>>ANGLETOFINESHIFT);
fixed_t ps = FINESINE (sec->ceilingpic_angle>>ANGLETOFINESHIFT);
fixed_t xoffs = sec->ceiling_xoffs;
fixed_t yoffs = sec->ceiling_yoffs;
sec->ceiling_xoffs = (FixedMul(xoffs, pc) % MAXFLATSIZE) - (FixedMul(yoffs, ps) % MAXFLATSIZE);
sec->ceiling_yoffs = (FixedMul(xoffs, ps) % MAXFLATSIZE) + (FixedMul(yoffs, pc) % MAXFLATSIZE);
}
}
/** Loads the textmap data, after obtaining the elements count and allocating their respective space.
*/
static void P_LoadTextmap(void)
{
UINT32 i;
vertex_t *vt;
sector_t *sc;
line_t *ld;
side_t *sd;
mapthing_t *mt;
CONS_Alert(CONS_NOTICE, "UDMF support is still a work-in-progress; its specs and features are prone to change until it is fully implemented.\n");
/// Given the UDMF specs, some fields are given a default value.
/// If an element's field has a default value set, it is omitted
/// from the textmap, and therefore we have to account for it by
/// preemptively setting that value beforehand.
for (i = 0, vt = vertexes; i < numvertexes; i++, vt++)
{
// Defaults.
vt->x = vt->y = INT32_MAX;
vt->z = 0;
TextmapParse(vertexesPos[i], i, ParseTextmapVertexParameter);
if (vt->x == INT32_MAX)
I_Error("P_LoadTextmap: vertex %s has no x value set!\n", sizeu1(i));
if (vt->y == INT32_MAX)
I_Error("P_LoadTextmap: vertex %s has no y value set!\n", sizeu1(i));
}
for (i = 0, sc = sectors; i < numsectors; i++, sc++)
{
// Defaults.
sc->floorheight = 0;
sc->ceilingheight = 0;
sc->floorpic = 0;
sc->ceilingpic = 0;
sc->lightlevel = 255;
sc->special = 0;
sc->tag = 0;
sc->floor_xoffs = sc->floor_yoffs = 0;
sc->ceiling_xoffs = sc->ceiling_yoffs = 0;
sc->floorpic_angle = sc->ceilingpic_angle = 0;
TextmapParse(sectorsPos[i], i, ParseTextmapSectorParameter);
P_InitializeSector(sc);
TextmapFixFlatOffsets(sc);
}
for (i = 0, ld = lines; i < numlines; i++, ld++)
{
// Defaults.
ld->v1 = ld->v2 = NULL;
ld->flags = 0;
ld->special = 0;
ld->tag = 0;
ld->sidenum[0] = 0xffff;
ld->sidenum[1] = 0xffff;
TextmapParse(linesPos[i], i, ParseTextmapLinedefParameter);
if (!ld->v1)
I_Error("P_LoadTextmap: linedef %s has no v1 value set!\n", sizeu1(i));
if (!ld->v2)
I_Error("P_LoadTextmap: linedef %s has no v2 value set!\n", sizeu1(i));
if (ld->sidenum[0] == 0xffff)
I_Error("P_LoadTextmap: linedef %s has no sidefront value set!\n", sizeu1(i));
P_InitializeLinedef(ld);
}
for (i = 0, sd = sides; i < numsides; i++, sd++)
{
// Defaults.
sd->textureoffset = 0;
sd->rowoffset = 0;
sd->toptexture = R_TextureNumForName("-");
sd->midtexture = R_TextureNumForName("-");
sd->bottomtexture = R_TextureNumForName("-");
sd->sector = NULL;
sd->repeatcnt = 0;
TextmapParse(sidesPos[i], i, ParseTextmapSidedefParameter);
if (!sd->sector)
I_Error("P_LoadTextmap: sidedef %s has no sector value set!\n", sizeu1(i));
P_InitializeSidedef(sd);
}
for (i = 0, mt = mapthings; i < nummapthings; i++, mt++)
{
// Defaults.
mt->x = mt->y = 0;
mt->angle = 0;
mt->type = 0;
mt->options = 0;
mt->z = 0;
mt->extrainfo = 0;
mt->mobj = NULL;
TextmapParse(mapthingsPos[i], i, ParseTextmapThingParameter);
}
}
static boolean P_LoadMapData(const virtres_t *virt)
{
virtlump_t *virtvertexes = NULL, *virtsectors = NULL, *virtsidedefs = NULL, *virtlinedefs = NULL, *virtthings = NULL;
virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
// Count map data. // Count map data.
if (textmap) if (textmap) // Count how many entries for each type we got in textmap.
{ {
nummapthings = 0; if (!TextmapCount(textmap->data, textmap->size))
numlines = 0; return false;
numsides = 0;
numvertexes = 0;
numsectors = 0;
// Count how many entries for each type we got in textmap.
//TextmapCount(vtextmap->data, vtextmap->size);
} }
else else
#endif
{ {
virtthings = vres_Find(virt, "THINGS"); virtthings = vres_Find(virt, "THINGS");
virtvertexes = vres_Find(virt, "VERTEXES"); virtvertexes = vres_Find(virt, "VERTEXES");
@ -1305,15 +1726,11 @@ static void P_LoadMapData(const virtres_t *virt)
numlevelflats = 0; numlevelflats = 0;
#ifdef UDMF // Load map data.
if (textmap) if (textmap)
{ P_LoadTextmap();
}
else else
#endif
{ {
// Strict map data
P_LoadVertices(virtvertexes->data); P_LoadVertices(virtvertexes->data);
P_LoadSectors(virtsectors->data); P_LoadSectors(virtsectors->data);
P_LoadLinedefs(virtlinedefs->data); P_LoadLinedefs(virtlinedefs->data);
@ -1341,6 +1758,8 @@ static void P_LoadMapData(const virtres_t *virt)
memcpy(spawnsectors, sectors, numsectors * sizeof (*sectors)); memcpy(spawnsectors, sectors, numsectors * sizeof (*sectors));
memcpy(spawnlines, lines, numlines * sizeof (*lines)); memcpy(spawnlines, lines, numlines * sizeof (*lines));
memcpy(spawnsides, sides, numsides * sizeof (*sides)); memcpy(spawnsides, sides, numsides * sizeof (*sides));
return true;
} }
static void P_InitializeSubsector(subsector_t *ss) static void P_InitializeSubsector(subsector_t *ss)
@ -1421,10 +1840,13 @@ static inline float P_SegLengthFloat(seg_t *seg)
static void P_InitializeSeg(seg_t *seg) static void P_InitializeSeg(seg_t *seg)
{ {
seg->sidedef = &sides[seg->linedef->sidenum[seg->side]]; if (seg->linedef)
{
seg->sidedef = &sides[seg->linedef->sidenum[seg->side]];
seg->frontsector = seg->sidedef->sector; seg->frontsector = seg->sidedef->sector;
seg->backsector = (seg->linedef->flags & ML_TWOSIDED) ? sides[seg->linedef->sidenum[seg->side ^ 1]].sector : NULL; seg->backsector = (seg->linedef->flags & ML_TWOSIDED) ? sides[seg->linedef->sidenum[seg->side ^ 1]].sector : NULL;
}
#ifdef HWRENDER #ifdef HWRENDER
seg->pv1 = seg->pv2 = NULL; seg->pv1 = seg->pv2 = NULL;
@ -1607,20 +2029,21 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
case NT_XGL3: case NT_XGL3:
for (m = 0; m < subsectors[i].numlines; m++, k++) for (m = 0; m < subsectors[i].numlines; m++, k++)
{ {
UINT32 vertexnum = READUINT32((*data));
UINT16 linenum; UINT16 linenum;
UINT32 vert = READUINT32((*data));
segs[k].v1 = &vertexes[vert]; if (vertexnum >= numvertexes)
if (m == 0) I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid vertex %d!\n", sizeu1(k), m, vertexnum);
segs[k + subsectors[i].numlines - 1].v2 = &vertexes[vert];
else
segs[k - 1].v2 = segs[k].v1;
(*data) += 4; // partner, can be ignored by software renderer segs[k - 1 + ((m == 0) ? subsectors[i].numlines : 0)].v2 = segs[k].v1 = &vertexes[vertexnum];
READUINT32((*data)); // partner, can be ignored by software renderer
if (nodetype == NT_XGL3) if (nodetype == NT_XGL3)
(*data) += 2; // Line number is 32-bit in XGL3, but we're limited to 16 bits. READUINT16((*data)); // Line number is 32-bit in XGL3, but we're limited to 16 bits.
linenum = READUINT16((*data)); linenum = READUINT16((*data));
if (linenum != 0xFFFF && linenum >= numlines)
I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d!\n", sizeu1(k), m, linenum);
segs[k].glseg = (linenum == 0xFFFF); segs[k].glseg = (linenum == 0xFFFF);
segs[k].linedef = (linenum == 0xFFFF) ? NULL : &lines[linenum]; segs[k].linedef = (linenum == 0xFFFF) ? NULL : &lines[linenum];
segs[k].side = READUINT8((*data)); segs[k].side = READUINT8((*data));
@ -1630,9 +2053,20 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
case NT_XNOD: case NT_XNOD:
for (m = 0; m < subsectors[i].numlines; m++, k++) for (m = 0; m < subsectors[i].numlines; m++, k++)
{ {
segs[k].v1 = &vertexes[READUINT32((*data))]; UINT32 v1num = READUINT32((*data));
segs[k].v2 = &vertexes[READUINT32((*data))]; UINT32 v2num = READUINT32((*data));
segs[k].linedef = &lines[READUINT16((*data))]; UINT16 linenum = READUINT16((*data));
if (v1num >= numvertexes)
I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v1 %d!\n", sizeu1(k), m, v1num);
if (v2num >= numvertexes)
I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid v2 %d!\n", sizeu1(k), m, v2num);
if (linenum >= numlines)
I_Error("P_LoadExtendedSubsectorsAndSegs: Seg %s in subsector %d has invalid linedef %d!\n", sizeu1(k), m, linenum);
segs[k].v1 = &vertexes[v1num];
segs[k].v2 = &vertexes[v2num];
segs[k].linedef = &lines[linenum];
segs[k].side = READUINT8((*data)); segs[k].side = READUINT8((*data));
segs[k].glseg = false; segs[k].glseg = false;
} }
@ -1649,7 +2083,8 @@ static boolean P_LoadExtendedSubsectorsAndSegs(UINT8 **data, nodetype_t nodetype
vertex_t *v2 = seg->v2; vertex_t *v2 = seg->v2;
P_InitializeSeg(seg); P_InitializeSeg(seg);
seg->angle = R_PointToAngle2(v1->x, v1->y, v2->x, v2->y); seg->angle = R_PointToAngle2(v1->x, v1->y, v2->x, v2->y);
seg->offset = FixedHypot(v1->x - seg->linedef->v1->x, v1->y - seg->linedef->v1->y); if (seg->linedef)
segs[i].offset = FixedHypot(v1->x - seg->linedef->v1->x, v1->y - seg->linedef->v1->y);
} }
return true; return true;
@ -2091,16 +2526,6 @@ static void P_ProcessLinedefsWithSidedefs(void)
ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here ld->frontsector = sides[ld->sidenum[0]].sector; //e6y: Can't be -1 here
ld->backsector = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0; ld->backsector = ld->sidenum[1] != 0xffff ? sides[ld->sidenum[1]].sector : 0;
// Repeat count for midtexture
if ((ld->flags & ML_EFFECT5) && (ld->sidenum[1] != 0xffff)
&& !(ld->special >= 300 && ld->special < 500)) // exempt linedef exec specials
{
sides[ld->sidenum[0]].repeatcnt = (INT16)(((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) >> 12);
sides[ld->sidenum[0]].textureoffset = (((unsigned)sides[ld->sidenum[0]].textureoffset >> FRACBITS) & 2047) << FRACBITS;
sides[ld->sidenum[1]].repeatcnt = (INT16)(((unsigned)sides[ld->sidenum[1]].textureoffset >> FRACBITS) >> 12);
sides[ld->sidenum[1]].textureoffset = (((unsigned)sides[ld->sidenum[1]].textureoffset >> FRACBITS) & 2047) << FRACBITS;
}
// Compile linedef 'text' from both sidedefs 'text' for appropriate specials. // Compile linedef 'text' from both sidedefs 'text' for appropriate specials.
switch(ld->special) switch(ld->special)
{ {
@ -2123,69 +2548,6 @@ static void P_ProcessLinedefsWithSidedefs(void)
} }
} }
static void P_CompressSidedefs(void)
{
side_t *newsides;
size_t numnewsides = 0;
size_t z;
size_t i;
for (i = 0; i < numsides; i++)
{
size_t j, k;
line_t *ld;
if (!sides[i].sector)
continue;
for (k = numlines, ld = lines; k--; ld++)
{
if (ld->sidenum[0] == i)
ld->sidenum[0] = (UINT16)numnewsides;
if (ld->sidenum[1] == i)
ld->sidenum[1] = (UINT16)numnewsides;
}
for (j = i + 1; j < numsides; j++)
{
if (!sides[j].sector)
continue;
if (!memcmp(&sides[i], &sides[j], sizeof(side_t)))
{
// Find the linedefs that belong to this one
for (k = numlines, ld = lines; k--; ld++)
{
if (ld->sidenum[0] == j)
ld->sidenum[0] = (UINT16)numnewsides;
if (ld->sidenum[1] == j)
ld->sidenum[1] = (UINT16)numnewsides;
}
sides[j].sector = NULL; // Flag for deletion
}
}
numnewsides++;
}
// We're loading crap into this block anyhow, so no point in zeroing it out.
newsides = Z_Malloc(numnewsides*sizeof(*newsides), PU_LEVEL, NULL);
// Copy the sides to their new block of memory.
for (i = 0, z = 0; i < numsides; i++)
{
if (sides[i].sector)
M_Memcpy(&newsides[z++], &sides[i], sizeof(side_t));
}
CONS_Debug(DBG_SETUP, "P_CompressSidedefs: Old sides is %s, new sides is %s\n", sizeu1(numsides), sizeu1(numnewsides));
Z_Free(sides);
sides = newsides;
numsides = numnewsides;
}
// //
// P_LinkMapData // P_LinkMapData
// Builds sector line lists and subsector sector numbers. // Builds sector line lists and subsector sector numbers.
@ -2310,47 +2672,54 @@ static INT32 P_MakeBufferMD5(const char *buffer, size_t len, void *resblock)
static void P_MakeMapMD5(virtres_t *virt, void *dest) static void P_MakeMapMD5(virtres_t *virt, void *dest)
{ {
unsigned char linemd5[16]; virtlump_t *textmap = vres_Find(virt, "TEXTMAP");
unsigned char sectormd5[16];
unsigned char thingmd5[16];
unsigned char sidedefmd5[16];
unsigned char resmd5[16]; unsigned char resmd5[16];
UINT8 i;
// Create a hash for the current map if (textmap)
// get the actual lumps! P_MakeBufferMD5((char*)textmap->data, textmap->size, resmd5);
virtlump_t *virtlines = vres_Find(virt, "LINEDEFS"); else
virtlump_t *virtsectors = vres_Find(virt, "SECTORS"); {
virtlump_t *virtmthings = vres_Find(virt, "THINGS"); unsigned char linemd5[16];
virtlump_t *virtsides = vres_Find(virt, "SIDEDEFS"); unsigned char sectormd5[16];
unsigned char thingmd5[16];
unsigned char sidedefmd5[16];
UINT8 i;
P_MakeBufferMD5((char*)virtlines->data, virtlines->size, linemd5); // Create a hash for the current map
P_MakeBufferMD5((char*)virtsectors->data, virtsectors->size, sectormd5); // get the actual lumps!
P_MakeBufferMD5((char*)virtmthings->data, virtmthings->size, thingmd5); virtlump_t* virtlines = vres_Find(virt, "LINEDEFS");
P_MakeBufferMD5((char*)virtsides->data, virtsides->size, sidedefmd5); virtlump_t* virtsectors = vres_Find(virt, "SECTORS");
virtlump_t* virtmthings = vres_Find(virt, "THINGS");
virtlump_t* virtsides = vres_Find(virt, "SIDEDEFS");
for (i = 0; i < 16; i++) P_MakeBufferMD5((char*)virtlines->data, virtlines->size, linemd5);
resmd5[i] = (linemd5[i] + sectormd5[i] + thingmd5[i] + sidedefmd5[i]) & 0xFF; P_MakeBufferMD5((char*)virtsectors->data, virtsectors->size, sectormd5);
P_MakeBufferMD5((char*)virtmthings->data, virtmthings->size, thingmd5);
P_MakeBufferMD5((char*)virtsides->data, virtsides->size, sidedefmd5);
for (i = 0; i < 16; i++)
resmd5[i] = (linemd5[i] + sectormd5[i] + thingmd5[i] + sidedefmd5[i]) & 0xFF;
}
M_Memcpy(dest, &resmd5, 16); M_Memcpy(dest, &resmd5, 16);
} }
static void P_LoadMapFromFile(void) static boolean P_LoadMapFromFile(void)
{ {
virtres_t *virt = vres_GetMap(lastloadedmaplumpnum); virtres_t *virt = vres_GetMap(lastloadedmaplumpnum);
P_LoadMapData(virt); if (!P_LoadMapData(virt))
return false;
P_LoadMapBSP(virt); P_LoadMapBSP(virt);
P_LoadMapLUT(virt); P_LoadMapLUT(virt);
P_ProcessLinedefsWithSidedefs(); P_ProcessLinedefsWithSidedefs();
if (M_CheckParm("-compress"))
P_CompressSidedefs();
P_LinkMapData(); P_LinkMapData();
P_MakeMapMD5(virt, &mapmd5); P_MakeMapMD5(virt, &mapmd5);
vres_Free(virt); vres_Free(virt);
return true;
} }
// //
@ -2446,7 +2815,7 @@ static void P_InitLevelSettings(void)
// earthquake camera // earthquake camera
memset(&quake,0,sizeof(struct quake)); memset(&quake,0,sizeof(struct quake));
if ((netgame || multiplayer) && gametype == GT_COOP && cv_coopstarposts.value == 2) if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
{ {
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -2464,7 +2833,7 @@ static void P_InitLevelSettings(void)
{ {
G_PlayerReborn(i, true); G_PlayerReborn(i, true);
if (canresetlives && (netgame || multiplayer) && playeringame[i] && (gametype == GT_COMPETITION || players[i].lives <= 0)) if (canresetlives && (netgame || multiplayer) && playeringame[i] && (G_CompetitionGametype() || players[i].lives <= 0))
{ {
// In Co-Op, replenish a user's lives if they are depleted. // In Co-Op, replenish a user's lives if they are depleted.
players[i].lives = cv_startinglives.value; players[i].lives = cv_startinglives.value;
@ -2754,17 +3123,10 @@ static void P_SetupCamera(void)
{ {
mapthing_t *thing; mapthing_t *thing;
switch (gametype) if (gametyperules & GTR_DEATHMATCHSTARTS)
{
case GT_MATCH:
case GT_TAG:
thing = deathmatchstarts[0]; thing = deathmatchstarts[0];
break; else
default:
thing = playerstarts[0]; thing = playerstarts[0];
break;
}
if (thing) if (thing)
{ {
@ -2979,7 +3341,7 @@ static void P_InitGametype(void)
P_InitPlayers(); P_InitPlayers();
// restore time in netgame (see also g_game.c) // restore time in netgame (see also g_game.c)
if ((netgame || multiplayer) && gametype == GT_COOP && cv_coopstarposts.value == 2) if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
{ {
// is this a hack? maybe // is this a hack? maybe
tic_t maxstarposttime = 0; tic_t maxstarposttime = 0;
@ -3023,6 +3385,7 @@ boolean P_LoadLevel(boolean fromnetsave)
// This is needed. Don't touch. // This is needed. Don't touch.
maptol = mapheaderinfo[gamemap-1]->typeoflevel; maptol = mapheaderinfo[gamemap-1]->typeoflevel;
gametyperules = gametypedefaultrules[gametype];
CON_Drawer(); // let the user know what we are going to do CON_Drawer(); // let the user know what we are going to do
I_FinishUpdate(); // page flip or blit buffer I_FinishUpdate(); // page flip or blit buffer
@ -3189,7 +3552,7 @@ boolean P_LoadLevel(boolean fromnetsave)
// internal game map // internal game map
maplumpname = G_BuildMapName(gamemap); maplumpname = G_BuildMapName(gamemap);
lastloadedmaplumpnum = W_CheckNumForName(maplumpname); lastloadedmaplumpnum = W_CheckNumForName(maplumpname);
if (lastloadedmaplumpnum == INT16_MAX) if (lastloadedmaplumpnum == LUMPERROR)
I_Error("Map %s not found.\n", maplumpname); I_Error("Map %s not found.\n", maplumpname);
R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette); R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette);
@ -3202,8 +3565,8 @@ boolean P_LoadLevel(boolean fromnetsave)
P_MapStart(); P_MapStart();
if (lastloadedmaplumpnum) if (!P_LoadMapFromFile())
P_LoadMapFromFile(); return false;
// init gravity, tag lists, // init gravity, tag lists,
// anything that P_ResetDynamicSlopes/P_LoadThings needs to know // anything that P_ResetDynamicSlopes/P_LoadThings needs to know

View File

@ -52,9 +52,6 @@ mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs
// Amount (dx, dy) vector linedef is shifted right to get scroll amount // Amount (dx, dy) vector linedef is shifted right to get scroll amount
#define SCROLL_SHIFT 5 #define SCROLL_SHIFT 5
// This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize.
#define MAXFLATSIZE (2048<<FRACBITS)
/** Animated texture descriptor /** Animated texture descriptor
* This keeps track of an animated texture or an animated flat. * This keeps track of an animated texture or an animated flat.
* \sa P_UpdateSpecials, P_InitPicAnims, animdef_t * \sa P_UpdateSpecials, P_InitPicAnims, animdef_t

View File

@ -27,6 +27,9 @@ extern mobj_t *skyboxcenterpnts[16]; // array of MT_SKYBOX centerpoint mobjs
// //
#define GETSECSPECIAL(i,j) ((i >> ((j-1)*4))&15) #define GETSECSPECIAL(i,j) ((i >> ((j-1)*4))&15)
// This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize.
#define MAXFLATSIZE (2048<<FRACBITS)
// at game start // at game start
void P_InitPicAnims(void); void P_InitPicAnims(void);

View File

@ -349,6 +349,11 @@ void P_GiveEmerald(boolean spawnObj)
continue; continue;
P_SetTarget(&emmo->target, players[i].mo); P_SetTarget(&emmo->target, players[i].mo);
P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em); P_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em);
// Make sure we're not being carried before our tracer is changed
if (players[i].powers[pw_carry] != CR_NIGHTSMODE)
players[i].powers[pw_carry] = CR_NONE;
P_SetTarget(&players[i].mo->tracer, emmo); P_SetTarget(&players[i].mo->tracer, emmo);
if (pnum == 255) if (pnum == 255)
@ -1232,13 +1237,13 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
if (gamestate == GS_LEVEL) if (gamestate == GS_LEVEL)
{ {
if (player->lives == INFLIVES || (gametype != GT_COOP && gametype != GT_COMPETITION)) if (player->lives == INFLIVES || !(gametyperules & GTR_LIVES))
{ {
P_GivePlayerRings(player, 100*numlives); P_GivePlayerRings(player, 100*numlives);
return; return;
} }
if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0)
{ {
P_GivePlayerRings(player, 100*numlives); P_GivePlayerRings(player, 100*numlives);
if (player->lives - prevlives >= numlives) if (player->lives - prevlives >= numlives)
@ -1269,7 +1274,7 @@ docooprespawn:
void P_GiveCoopLives(player_t *player, INT32 numlives, boolean sound) void P_GiveCoopLives(player_t *player, INT32 numlives, boolean sound)
{ {
if (!((netgame || multiplayer) && gametype == GT_COOP)) if (!((netgame || multiplayer) && G_GametypeUsesCoopLives()))
{ {
P_GivePlayerLives(player, numlives); P_GivePlayerLives(player, numlives);
if (sound) if (sound)
@ -1397,7 +1402,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
player->score = MAXSCORE; player->score = MAXSCORE;
// check for extra lives every 50000 pts // check for extra lives every 50000 pts
if (!ultimatemode && !modeattacking && player->score > oldscore && player->score % 50000 < amount && (gametype == GT_COMPETITION || gametype == GT_COOP)) if (!ultimatemode && !modeattacking && player->score > oldscore && player->score % 50000 < amount && (gametyperules & GTR_LIVES))
{ {
P_GivePlayerLives(player, (player->score/50000) - (oldscore/50000)); P_GivePlayerLives(player, (player->score/50000) - (oldscore/50000));
P_PlayLivesJingle(player); P_PlayLivesJingle(player);
@ -7791,6 +7796,7 @@ void P_ElementalFire(player_t *player, boolean cropcircle)
flame->fuse = TICRATE*7; // takes about an extra second to hit the ground flame->fuse = TICRATE*7; // takes about an extra second to hit the ground
flame->destscale = player->mo->scale; flame->destscale = player->mo->scale;
P_SetScale(flame, player->mo->scale); P_SetScale(flame, player->mo->scale);
flame->flags2 = (flame->flags2 & ~MF2_OBJECTFLIP)|(player->mo->flags2 & MF2_OBJECTFLIP);
flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP); flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
P_InstaThrust(flame, flame->angle, FixedMul(3*FRACUNIT, flame->scale)); P_InstaThrust(flame, flame->angle, FixedMul(3*FRACUNIT, flame->scale));
P_SetObjectMomZ(flame, 3*FRACUNIT, false); P_SetObjectMomZ(flame, 3*FRACUNIT, false);
@ -9460,7 +9466,7 @@ 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) || !G_GametypeUsesCoopLives()
|| (player->lives == INFLIVES)) || (player->lives == INFLIVES))
return true; return true;
@ -9609,7 +9615,7 @@ static void P_DeathThink(player_t *player)
player->playerstate = PST_REBORN; player->playerstate = PST_REBORN;
else if ((player->lives > 0 || j != MAXPLAYERS) && !(!(netgame || multiplayer) && G_IsSpecialStage(gamemap))) // Don't allow "click to respawn" in special stages! else if ((player->lives > 0 || j != MAXPLAYERS) && !(!(netgame || multiplayer) && G_IsSpecialStage(gamemap))) // Don't allow "click to respawn" in special stages!
{ {
if (gametype == GT_COOP && (netgame || multiplayer) && cv_coopstarposts.value == 2) if (G_GametypeUsesCoopStarposts() && (netgame || multiplayer) && cv_coopstarposts.value == 2)
{ {
P_ConsiderAllGone(); P_ConsiderAllGone();
if ((player->deadtimer > TICRATE<<1) || ((cmd->buttons & BT_JUMP) && (player->deadtimer > TICRATE))) if ((player->deadtimer > TICRATE<<1) || ((cmd->buttons & BT_JUMP) && (player->deadtimer > TICRATE)))
@ -9624,19 +9630,22 @@ static void P_DeathThink(player_t *player)
// Respawn with jump button, force respawn time (3 second default, cheat protected) in shooter modes. // Respawn with jump button, force respawn time (3 second default, cheat protected) in shooter modes.
if (cmd->buttons & BT_JUMP) if (cmd->buttons & BT_JUMP)
{ {
if (gametype != GT_COOP && player->spectator) // You're a spectator, so respawn right away.
if ((gametyperules & GTR_SPECTATORS) && player->spectator)
player->playerstate = PST_REBORN; player->playerstate = PST_REBORN;
else switch(gametype) { else
case GT_COOP: {
case GT_COMPETITION: // Give me one second.
case GT_RACE: INT32 respawndelay = TICRATE;
if (player->deadtimer > TICRATE)
player->playerstate = PST_REBORN; // Non-platform gametypes
break; if (gametyperules & GTR_RESPAWNDELAY)
default: respawndelay = (cv_respawntime.value*TICRATE);
if (player->deadtimer > cv_respawntime.value*TICRATE)
player->playerstate = PST_REBORN; // You've been dead for enough time.
break; // You may now respawn.
if (player->deadtimer > respawndelay)
player->playerstate = PST_REBORN;
} }
} }
@ -9650,7 +9659,7 @@ static void P_DeathThink(player_t *player)
INT32 i, deadtimercheck = INT32_MAX; INT32 i, deadtimercheck = INT32_MAX;
// In a net/multiplayer game, and out of lives // In a net/multiplayer game, and out of lives
if (gametype == GT_COMPETITION) if (G_CompetitionGametype())
{ {
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -11477,7 +11486,10 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
if (panim == PA_WALK) if (panim == PA_WALK)
{ {
if (stat != fume->info->spawnstate) if (stat != fume->info->spawnstate)
{
fume->threshold = 0;
P_SetMobjState(fume, fume->info->spawnstate); P_SetMobjState(fume, fume->info->spawnstate);
}
return; return;
} }
} }
@ -11508,6 +11520,12 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
if (underwater) if (underwater)
{ {
fume->frame = (fume->frame & FF_FRAMEMASK) | FF_ANIMATE | (P_RandomRange(0, 9) * FF_TRANS10); fume->frame = (fume->frame & FF_FRAMEMASK) | FF_ANIMATE | (P_RandomRange(0, 9) * FF_TRANS10);
fume->threshold = 1;
}
else if (fume->threshold)
{
fume->frame = (fume->frame & FF_FRAMEMASK) | fume->state->frame;
fume->threshold = 0;
} }
} }
@ -11767,7 +11785,7 @@ void P_PlayerThink(player_t *player)
#else #else
if (player->spectator && if (player->spectator &&
#endif #endif
gametype == GT_COOP && (netgame || multiplayer) && cv_coopstarposts.value == 2) G_GametypeUsesCoopStarposts() && (netgame || multiplayer) && cv_coopstarposts.value == 2)
P_ConsiderAllGone(); P_ConsiderAllGone();
if (player->playerstate == PST_DEAD) if (player->playerstate == PST_DEAD)

View File

@ -187,6 +187,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"shield", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pity Shield"}, // generic GET! {"shield", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pity Shield"}, // generic GET!
{"wirlsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Whirlwind Shield"}, // Whirlwind GET! {"wirlsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Whirlwind Shield"}, // Whirlwind GET!
{"forcsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Force Shield"}, // Force GET! {"forcsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Force Shield"}, // Force GET!
{"frcssg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Weak Force Shield"}, // Force GET...? (consider making a custom shield with this instead of a single-hit force shield!)
{"elemsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Elemental Shield"}, // Elemental GET! {"elemsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Elemental Shield"}, // Elemental GET!
{"armasg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Armageddon Shield"}, // Armaggeddon GET! {"armasg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Armageddon Shield"}, // Armaggeddon GET!
{"attrsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Attraction Shield"}, // Attract GET! {"attrsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Attraction Shield"}, // Attract GET!
@ -220,6 +221,9 @@ sfxinfo_t S_sfx[NUMSFX] =
{"sprong", false, 112, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Power spring"}, {"sprong", false, 112, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Power spring"},
{"lvfal1", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Rumble"}, {"lvfal1", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Rumble"},
{"pscree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "SCREE!"}, {"pscree", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "SCREE!"},
{"iceb", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Ice crack"},
{"shattr", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Shattering"},
{"antiri", true, 255, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Depletion"},
// Menu, interface // Menu, interface
{"chchng", false, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Score"}, {"chchng", false, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Score"},
@ -233,6 +237,9 @@ sfxinfo_t S_sfx[NUMSFX] =
{"wepchg", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Weapon change"}, // Weapon switch is identical to menu for now {"wepchg", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Weapon change"}, // Weapon switch is identical to menu for now
{"wtrdng", true, 212, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Aquaphobia"}, // make sure you can hear the DING DING! Tails 03-08-2000 {"wtrdng", true, 212, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Aquaphobia"}, // make sure you can hear the DING DING! Tails 03-08-2000
{"zelda", false, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Discovery"}, {"zelda", false, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Discovery"},
{"adderr", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Error"},
{"notadd", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Reject"},
{"addfil", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Accept"},
// NiGHTS // NiGHTS
{"ideya", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Success"}, {"ideya", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Success"},
@ -427,24 +434,9 @@ sfxinfo_t S_sfx[NUMSFX] =
{"s25e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"s25e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s25f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"s25f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s260", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"s260", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s261", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s262", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s263", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s264", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s265", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s266", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s267", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s268", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s269", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s26a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s26b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s26c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s26d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s26e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s26f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"s270", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
// S3&K sounds // S3&K sounds
{"s3k2b", true, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got Emerald"}, // Got Emerald!
{"s3k33", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Sparkle"}, // stereo in original game, identical to latter {"s3k33", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Sparkle"}, // stereo in original game, identical to latter
{"s3k34", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Sparkle"}, // mono in original game, identical to previous {"s3k34", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Sparkle"}, // mono in original game, identical to previous
{"s3k35", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Hurt"}, {"s3k35", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Hurt"},
@ -566,6 +558,21 @@ sfxinfo_t S_sfx[NUMSFX] =
{"s3ka9", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Aquaphobia"}, {"s3ka9", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Aquaphobia"},
{"s3kaa", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bumper"}, {"s3kaa", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bumper"},
{"s3kab", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"}, {"s3kab", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kab1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kab2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kab3", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kab4", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kab5", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kab6", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kab7", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kab8", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kab9", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kaba", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kabb", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kabc", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kabd", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kabe", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kabf", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spindash"},
{"s3kac", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got Continue"}, {"s3kac", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Got Continue"},
{"s3kad", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "GO!"}, {"s3kad", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "GO!"},
{"s3kae", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pinball flipper"}, {"s3kae", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pinball flipper"},
@ -604,7 +611,8 @@ sfxinfo_t S_sfx[NUMSFX] =
{"s3kc5l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Revving up"}, // ditto {"s3kc5l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Revving up"}, // ditto
{"s3kc6s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Orbiting"}, {"s3kc6s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Orbiting"},
{"s3kc6l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Orbiting"}, // ditto {"s3kc6l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Orbiting"}, // ditto
{"s3kc7", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Aiming"}, {"s3kc7s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Aiming"},
{"s3kc7l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Aiming"}, // ditto
{"s3kc8s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Sliding"}, {"s3kc8s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Sliding"},
{"s3kc8l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Sliding"}, // ditto {"s3kc8l", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Sliding"}, // ditto
{"s3kc9s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Swinging"}, {"s3kc9s", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Swinging"},

View File

@ -236,6 +236,7 @@ typedef enum
sfx_shield, sfx_shield,
sfx_wirlsg, sfx_wirlsg,
sfx_forcsg, sfx_forcsg,
sfx_frcssg,
sfx_elemsg, sfx_elemsg,
sfx_armasg, sfx_armasg,
sfx_attrsg, sfx_attrsg,
@ -269,6 +270,9 @@ typedef enum
sfx_sprong, sfx_sprong,
sfx_lvfal1, sfx_lvfal1,
sfx_pscree, sfx_pscree,
sfx_iceb,
sfx_shattr,
sfx_antiri,
// Menu, interface // Menu, interface
sfx_chchng, sfx_chchng,
@ -282,6 +286,9 @@ typedef enum
sfx_wepchg, sfx_wepchg,
sfx_wtrdng, sfx_wtrdng,
sfx_zelda, sfx_zelda,
sfx_adderr,
sfx_notadd,
sfx_addfil,
// NiGHTS // NiGHTS
sfx_ideya, sfx_ideya,
@ -476,24 +483,9 @@ typedef enum
sfx_s25e, sfx_s25e,
sfx_s25f, sfx_s25f,
sfx_s260, sfx_s260,
sfx_s261,
sfx_s262,
sfx_s263,
sfx_s264,
sfx_s265,
sfx_s266,
sfx_s267,
sfx_s268,
sfx_s269,
sfx_s26a,
sfx_s26b,
sfx_s26c,
sfx_s26d,
sfx_s26e,
sfx_s26f,
sfx_s270,
// S3&K sounds // S3&K sounds
sfx_s3k2b,
sfx_s3k33, sfx_s3k33,
sfx_s3k34, sfx_s3k34,
sfx_s3k35, sfx_s3k35,
@ -615,6 +607,21 @@ typedef enum
sfx_s3ka9, sfx_s3ka9,
sfx_s3kaa, sfx_s3kaa,
sfx_s3kab, sfx_s3kab,
sfx_s3kab1,
sfx_s3kab2,
sfx_s3kab3,
sfx_s3kab4,
sfx_s3kab5,
sfx_s3kab6,
sfx_s3kab7,
sfx_s3kab8,
sfx_s3kab9,
sfx_s3kaba,
sfx_s3kabb,
sfx_s3kabc,
sfx_s3kabd,
sfx_s3kabe,
sfx_s3kabf,
sfx_s3kac, sfx_s3kac,
sfx_s3kad, sfx_s3kad,
sfx_s3kae, sfx_s3kae,
@ -653,7 +660,8 @@ typedef enum
sfx_s3kc5l, sfx_s3kc5l,
sfx_s3kc6s, sfx_s3kc6s,
sfx_s3kc6l, sfx_s3kc6l,
sfx_s3kc7, sfx_s3kc7s,
sfx_s3kc7l,
sfx_s3kc8s, sfx_s3kc8s,
sfx_s3kc8l, sfx_s3kc8l,
sfx_s3kc9s, sfx_s3kc9s,

View File

@ -694,7 +694,7 @@ static void ST_drawTime(void)
else else
{ {
// Counting down the hidetime? // Counting down the hidetime?
if ((gametyperules & GTR_HIDETIME) && (stplyr->realtime <= (hidetime*TICRATE))) if ((gametyperules & GTR_STARTCOUNTDOWN) && (stplyr->realtime <= (hidetime*TICRATE)))
{ {
tics = (hidetime*TICRATE - stplyr->realtime); tics = (hidetime*TICRATE - stplyr->realtime);
if (tics < 3*TICRATE) if (tics < 3*TICRATE)
@ -705,7 +705,7 @@ static void ST_drawTime(void)
else else
{ {
// Hidetime finish! // Hidetime finish!
if ((gametyperules & GTR_HIDETIME) && (stplyr->realtime < ((hidetime+1)*TICRATE))) if ((gametyperules & GTR_STARTCOUNTDOWN) && (stplyr->realtime < ((hidetime+1)*TICRATE)))
ST_drawRaceNum(hidetime*TICRATE - stplyr->realtime); ST_drawRaceNum(hidetime*TICRATE - stplyr->realtime);
// Time limit? // Time limit?
@ -723,7 +723,7 @@ static void ST_drawTime(void)
downwards = true; downwards = true;
} }
// Post-hidetime normal. // Post-hidetime normal.
else if (gametyperules & GTR_TAG) else if (gametyperules & GTR_STARTCOUNTDOWN)
tics = stplyr->realtime - hidetime*TICRATE; tics = stplyr->realtime - hidetime*TICRATE;
// "Shadow! What are you doing? Hurry and get back here // "Shadow! What are you doing? Hurry and get back here
// right now before the island blows up with you on it!" // right now before the island blows up with you on it!"
@ -845,69 +845,13 @@ static void ST_drawLivesArea(void)
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, faceprefix[stplyr->skin], colormap); hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, faceprefix[stplyr->skin], colormap);
} }
// Lives number // Metal Sonic recording
if (metalrecording) if (metalrecording)
{ {
if (((2*leveltime)/TICRATE) & 1) if (((2*leveltime)/TICRATE) & 1)
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_REDMAP|V_HUDTRANS, "REC"); hudinfo[HUD_LIVES].f|V_PERPLAYER|V_REDMAP|V_HUDTRANS, "REC");
} }
else if (G_GametypeUsesLives() || gametype == GT_RACE)
{
// x
V_DrawScaledPatch(hudinfo[HUD_LIVES].x+22, hudinfo[HUD_LIVES].y+10,
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, stlivex);
// lives number
if (gametype == GT_RACE)
{
livescount = INFLIVES;
notgreyedout = true;
}
else if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 3)
{
INT32 i;
livescount = 0;
notgreyedout = (stplyr->lives > 0);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].lives < 1)
continue;
if (players[i].lives > 1)
notgreyedout = true;
if (players[i].lives == INFLIVES)
{
livescount = INFLIVES;
break;
}
else if (livescount < 99)
livescount += (players[i].lives);
}
}
else
{
livescount = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? INFLIVES : stplyr->lives);
notgreyedout = true;
}
if (livescount == INFLIVES)
V_DrawCharacter(hudinfo[HUD_LIVES].x+50, hudinfo[HUD_LIVES].y+8,
'\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false);
else
{
if (stplyr->playerstate == PST_DEAD && !(stplyr->spectator) && (livescount || stplyr->deadtimer < (TICRATE<<1)))
livescount++;
if (livescount > 99)
livescount = 99;
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
hudinfo[HUD_LIVES].f|V_PERPLAYER|(notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF), va("%d",livescount));
}
}
// Spectator // Spectator
else if (stplyr->spectator) else if (stplyr->spectator)
v_colmap = V_GRAYMAP; v_colmap = V_GRAYMAP;
@ -934,11 +878,89 @@ static void ST_drawLivesArea(void)
v_colmap = V_BLUEMAP; v_colmap = V_BLUEMAP;
} }
} }
// Lives number
else
{
boolean candrawlives = true;
// Co-op and Competition, normal life counter
if (G_GametypeUsesLives())
{
// Handle cooplives here
if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 3)
{
INT32 i;
livescount = 0;
notgreyedout = (stplyr->lives > 0);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].lives < 1)
continue;
if (players[i].lives > 1)
notgreyedout = true;
if (players[i].lives == INFLIVES)
{
livescount = INFLIVES;
break;
}
else if (livescount < 99)
livescount += (players[i].lives);
}
}
else
{
livescount = (((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0) ? INFLIVES : stplyr->lives);
notgreyedout = true;
}
}
// Infinity symbol (Race)
else if (G_PlatformGametype() && !(gametyperules & GTR_LIVES))
{
livescount = INFLIVES;
notgreyedout = true;
}
// Otherwise nothing, sorry.
// Special Stages keep not showing lives,
// as G_GametypeUsesLives() returns false in
// Special Stages, and the infinity symbol
// cannot show up because Special Stages
// still have the GTR_LIVES gametype rule
// by default.
else
candrawlives = false;
// Draw the lives counter here.
if (candrawlives)
{
// x
V_DrawScaledPatch(hudinfo[HUD_LIVES].x+22, hudinfo[HUD_LIVES].y+10, hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, stlivex);
if (livescount == INFLIVES)
V_DrawCharacter(hudinfo[HUD_LIVES].x+50, hudinfo[HUD_LIVES].y+8,
'\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false);
else
{
if (stplyr->playerstate == PST_DEAD && !(stplyr->spectator) && (livescount || stplyr->deadtimer < (TICRATE<<1)))
livescount++;
if (livescount > 99)
livescount = 99;
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
hudinfo[HUD_LIVES].f|V_PERPLAYER|(notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF), va("%d",livescount));
}
}
#undef ST_drawLivesX
}
// name // name
v_colmap |= (V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER); v_colmap |= (V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER);
if (strlen(skins[stplyr->skin].hudname) <= 5) if (strlen(skins[stplyr->skin].hudname) <= 5)
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname); V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname);
else if (V_StringWidth(skins[stplyr->skin].hudname, v_colmap) <= 48)
V_DrawString(hudinfo[HUD_LIVES].x+18, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname);
else if (V_ThinStringWidth(skins[stplyr->skin].hudname, v_colmap) <= 40) else if (V_ThinStringWidth(skins[stplyr->skin].hudname, v_colmap) <= 40)
V_DrawRightAlignedThinString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname); V_DrawRightAlignedThinString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y, v_colmap, skins[stplyr->skin].hudname);
else else
@ -2212,7 +2234,7 @@ static void ST_drawTextHUD(void)
donef12 = true; donef12 = true;
} }
} }
else if (!G_PlatformGametype() && stplyr->playerstate == PST_DEAD && stplyr->lives) // Death overrides spectator text. else if ((gametyperules & GTR_RESPAWNDELAY) && stplyr->playerstate == PST_DEAD && stplyr->lives) // Death overrides spectator text.
{ {
INT32 respawntime = cv_respawntime.value - stplyr->deadtimer/TICRATE; INT32 respawntime = cv_respawntime.value - stplyr->deadtimer/TICRATE;
@ -2236,7 +2258,7 @@ static void ST_drawTextHUD(void)
textHUDdraw(M_GetText("\x82""Wait for the stage to end...")) textHUDdraw(M_GetText("\x82""Wait for the stage to end..."))
else if (G_PlatformGametype()) else if (G_PlatformGametype())
{ {
if (gametype == GT_COOP) if (G_GametypeUsesCoopLives())
{ {
if (stplyr->lives <= 0 if (stplyr->lives <= 0
&& cv_cooplives.value == 2 && cv_cooplives.value == 2
@ -2647,7 +2669,7 @@ static void ST_overlayDrawer(void)
INT32 i = MAXPLAYERS; INT32 i = MAXPLAYERS;
INT32 deadtimer = stplyr->spectator ? TICRATE : (stplyr->deadtimer-(TICRATE<<1)); INT32 deadtimer = stplyr->spectator ? TICRATE : (stplyr->deadtimer-(TICRATE<<1));
if ((gametype == GT_COOP) if (G_GametypeUsesCoopLives()
&& (netgame || multiplayer) && (netgame || multiplayer)
&& (cv_cooplives.value != 1)) && (cv_cooplives.value != 1))
{ {

View File

@ -1997,7 +1997,7 @@ static void Y_AwardCoopBonuses(void)
if (i == consoleplayer) if (i == consoleplayer)
{ {
data.coop.gotlife = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? 0 : ptlives); data.coop.gotlife = (((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0) ? 0 : ptlives);
M_Memcpy(&data.coop.bonuses, &localbonuses, sizeof(data.coop.bonuses)); M_Memcpy(&data.coop.bonuses, &localbonuses, sizeof(data.coop.bonuses));
} }
} }
@ -2052,7 +2052,7 @@ static void Y_AwardSpecialStageBonus(void)
if (i == consoleplayer) if (i == consoleplayer)
{ {
data.spec.gotlife = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? 0 : ptlives); data.spec.gotlife = (((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0) ? 0 : ptlives);
M_Memcpy(&data.spec.bonuses, &localbonuses, sizeof(data.spec.bonuses)); M_Memcpy(&data.spec.bonuses, &localbonuses, sizeof(data.spec.bonuses));
// Continues related // Continues related