Merge branch 'master' into spd

This commit is contained in:
toaster 2019-11-29 19:19:55 +00:00
commit de34298bbd
71 changed files with 2949 additions and 1271 deletions

View File

@ -3,6 +3,7 @@
For Sonic Robo Blast 2 Version 2.2 For Sonic Robo Blast 2 Version 2.2
Contributors (alphabetical): Contributors (alphabetical):
* Foxboy * Foxboy
* FuriousFox
* JJames19119 * JJames19119
* Kalaron * Kalaron
* Kristos * Kristos
@ -44,27 +45,32 @@ formatinterface = "SRB2MapSetIO";
//Sky textures for vanilla maps //Sky textures for vanilla maps
defaultskytextures defaultskytextures
{ {
SKY1 = "MAP01,MAP02,MAP03,MAP50,MAPA1,MAPA2,MAPA5,MAPA6,MAPA9,MAPAA,MAPAB,MAPAC,MAPAD,MAPAE,MAPAG,MAPAJ,MAPAK,MAPF0,MAPF1,MAPFA,MAPM0,MAPM8,MAPMA,MAPMB,MAPMC"; SKY1 = "MAP01,MAP02,MAP03,MAP33,MAP50,MAP60,MAPF0,MAPM0";
SKY4 = "MAP04,MAP06,MAP51,MAPF8,MAPM1"; SKY2 = "MAPM7,MAPMB";
SKY6 = "MAP05"; SKY4 = "MAP04,MAP06,MAP61,MAPF6,MAPM1";
SKY7 = "MAP07,MAP08,MAP09,MAP52,MAPM2,MAPM5"; SKY6 = "MAP05,MAP51,MAPMA";
SKY10 = "MAP12,MAP53,MAPM3"; SKY7 = "MAPM2,MAPM5";
SKY11 = "MAP10,MAP11,MAP16,MAP55,MAPF2,MAPF5,MAPF6,MAPF9,MAPM7"; SKY8 = "MAP07,MAP08,MAP09,MAP52,MAP62,MAPF1";
SKY13 = "MAP13,MAP54,MAPAS"; SKY10 = "MAP10,MAP12,MAP53,MAP63,MAPM3";
SKY21 = "MAPAF,MAPF7,MAPM4"; SKY11 = "MAP11,MAPF7";
SKY22 = "MAP22,MAP23,MAP24,MAP25,MAP56,MAPAN,MAPAO,MAPF4,MAPM6"; SKY13 = "MAP13,MAP64";
SKY29 = "MAP58,MAPAV"; SKY14 = "MAP14";
SKY15 = "MAP15,MAP54";
SKY17 = "MAP70";
SKY20 = "MAP32,MAP55,MAP65,MAPF2,MAPF5";
SKY21 = "MAPM4";
SKY22 = "MAP22,MAP23,MAP25,MAP26,MAP27,MAP56,MAP66,MAPF4,MAPM6";
SKY30 = "MAP30"; SKY30 = "MAP30";
SKY35 = "MAP41"; SKY31 = "MAP31";
SKY40 = "MAP40"; SKY35 = "MAP42";
SKY55 = "MAPF3,MAPM9"; SKY40 = "MAP41,MAP71,MAPM9";
SKY66 = "MAPAT"; SKY55 = "MAPF3,MAPM8";
SKY99 = "MAP57"; SKY68 = "MAPF8";
SKY103 = "MAPA3,MAPA4,MAPAU"; SKY99 = "MAP57,MAPZ0";
SKY107 = "MAPA7,MAPA8"; SKY159 = "MAP16";
SKY117 = "MAPAH,MAPAI"; SKY172 = "MAP40";
SKY127 = "MAPAR"; SKY300 = "MAP72";
SKY132 = "MAPAW"; SKY301 = "MAP73";
} }
// Default lump name for new map // Default lump name for new map
@ -90,9 +96,9 @@ skins
Sonic; Sonic;
Tails; Tails;
Knuckles; Knuckles;
Metalsonic;
Fang;
Amy; Amy;
Fang;
Metalsonic;
} }
// Gametypes // Gametypes
@ -3422,7 +3428,7 @@ thingtypes
121 121
{ {
title = "Minus"; title = "Minus";
sprite = "MNUSA1"; sprite = "MNUSA0";
width = 24; width = 24;
height = 32; height = 32;
} }
@ -3457,6 +3463,13 @@ thingtypes
height = 34; height = 34;
flags8text = "[8] Start on fire"; flags8text = "[8] Start on fire";
} }
137
{
title = "Dragonbomber";
sprite = "DRABA1";
width = 28;
height = 48;
}
105 105
{ {
title = "Jetty-Syn Bomber"; title = "Jetty-Syn Bomber";
@ -5726,6 +5739,24 @@ thingtypes
width = 24; width = 24;
height = 32; height = 32;
} }
1505
{
title = "Green Flame";
sprite = "CFLMA0E0";
width = 8;
height = 32;
}
1506
{
arrow = 1;
blocking = 2;
title = "Blue Gargoyle";
sprite = "BGARD1";
width = 16;
height = 40;
flags4text = "[4] Slides when pushed";
flags8text = "[8] Not pushable";
}
} }
dreamhill dreamhill

View File

@ -24,12 +24,47 @@ static boolean lastForward = false;
static boolean lastBlocked = false; static boolean lastBlocked = false;
static boolean blocked = false; static boolean blocked = false;
static boolean jump_last = false;
static boolean spin_last = false;
static UINT8 anxiety = 0;
static boolean panic = false;
static UINT8 flymode = 0;
static boolean spinmode = false;
static boolean thinkfly = false;
static inline void B_ResetAI(void)
{
jump_last = false;
spin_last = false;
anxiety = 0;
panic = false;
flymode = 0;
spinmode = false;
thinkfly = false;
}
static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
{ {
boolean forward=false, backward=false, left=false, right=false, jump=false, spin=false; boolean forward=false, backward=false, left=false, right=false, jump=false, spin=false;
angle_t angle;
INT16 rangle; player_t *player = sonic->player, *bot = tails->player;
fixed_t dist; ticcmd_t *pcmd = &player->cmd;
boolean water = tails->eflags & MFE_UNDERWATER;
SINT8 flip = P_MobjFlip(tails);
boolean _2d = (tails->flags2 & MF2_TWOD) || twodlevel;
fixed_t scale = tails->scale;
fixed_t dist = P_AproxDistance(sonic->x - tails->x, sonic->y - tails->y);
fixed_t zdist = flip * (sonic->z - tails->z);
angle_t ang = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y);
fixed_t pmom = P_AproxDistance(sonic->momx, sonic->momy);
fixed_t bmom = P_AproxDistance(tails->momx, tails->momy);
fixed_t followmax = 128 * 8 * scale; // Max follow distance before AI begins to enter "panic" state
fixed_t followthres = 92 * scale; // Distance that AI will try to reach
fixed_t followmin = 32 * scale;
fixed_t comfortheight = 96 * scale;
fixed_t touchdist = 24 * scale;
boolean stalled = (bmom < scale >> 1) && dist > followthres; // Helps to see if the AI is having trouble catching up
// We can't follow Sonic if he's not around! // We can't follow Sonic if he's not around!
if (!sonic || sonic->health <= 0) if (!sonic || sonic->health <= 0)
@ -58,46 +93,263 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
return; return;
} }
// Gather data about the environment // Adapted from CobaltBW's tails_AI.wad
dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y);
if (tails->player->pflags & PF_STARTDASH) // Check water
angle = sonic->angle; if (water)
{
followmin = 0;
followthres = 16*scale;
followmax >>= 1;
thinkfly = false;
}
// Check anxiety
if (spinmode)
{
anxiety = 0;
panic = false;
}
else if (dist > followmax || zdist > comfortheight || stalled)
{
anxiety = min(anxiety + 2, 70);
if (anxiety >= 70)
panic = true;
}
else else
angle = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y); {
anxiety = max(anxiety - 1, 0);
// Decide which direction to turn panic = false;
angle = (tails->angle - angle);
if (angle < ANGLE_180) {
right = true; // We need to turn right
rangle = AngleFixed(angle)>>FRACBITS;
} else {
left = true; // We need to turn left
rangle = 360-(AngleFixed(angle)>>FRACBITS);
} }
// Decide to move forward if you're finished turning // Orientation
if (abs(rangle) < 10) { // We're facing the right way? if ((bot->pflags & (PF_SPINNING|PF_STARTDASH)) || flymode == 2)
left = right = false; // Stop turning {
forward = true; // and walk forward instead. cmd->angleturn = (sonic->angle - tails->angle) >> FRACBITS;
}
else
{
cmd->angleturn = (ang - tails->angle) >> FRACBITS;
} }
if (dist < (sonic->radius+tails->radius)*3) // We're close enough?
forward = false; // Stop walking.
// Decide when to jump // ********
if (!(tails->player->pflags & (PF_JUMPED|PF_JUMPDOWN))) { // We're not jumping yet... // FLY MODE
if (forward && lastForward && blocked && lastBlocked) // We've been stopped by a wall or something // spinmode check
jump = true; // Try to jump up if (spinmode || player->exiting)
} else if ((tails->player->pflags & (PF_JUMPDOWN|PF_JUMPED)) == (PF_JUMPDOWN|PF_JUMPED)) { // When we're already jumping... thinkfly = false;
if (lastForward && blocked) // We're still stuck on something? else
{
// Activate co-op flight
if (thinkfly && player->pflags & PF_JUMPED)
{
if (!jump_last)
{
jump = true;
flymode = 1;
thinkfly = false;
bot->pflags |= PF_CANCARRY;
}
}
// Check positioning
// Thinker for co-op flight
if (!(water || pmom || bmom)
&& (dist < touchdist)
&& !(pcmd->forwardmove || pcmd->sidemove || player->dashspeed)
&& P_IsObjectOnGround(sonic) && P_IsObjectOnGround(tails)
&& !(player->pflags & PF_STASIS)
&& bot->charability == CA_FLY)
thinkfly = true;
else
thinkfly = false;
// Ready for takeoff
if (flymode == 1)
{
thinkfly = false;
if (zdist < -64*scale || (flip * tails->momz) > scale) // Make sure we're not too high up
spin = true;
else if (!jump_last)
jump = true;
// Abort if the player moves away or spins
if (dist > followthres || player->dashspeed)
flymode = 0;
// Set carried state
if (player->powers[pw_carry] == CR_PLAYER && sonic->tracer == tails)
{
flymode = 2;
}
}
// Read player inputs while carrying
else if (flymode == 2)
{
cmd->forwardmove = pcmd->forwardmove;
cmd->sidemove = pcmd->sidemove;
if (pcmd->buttons & BT_USE)
{
spin = true;
jump = false;
}
else if (!jump_last)
jump = true;
// End flymode
if (player->powers[pw_carry] != CR_PLAYER)
{
flymode = 0;
}
}
}
if (flymode && P_IsObjectOnGround(tails) && !(pcmd->buttons & BT_JUMP))
flymode = 0;
// ********
// SPINNING
if (panic || flymode || !(player->pflags & PF_SPINNING) || (player->pflags & PF_JUMPED))
spinmode = false;
else
{
if (!_2d)
{
// Spindash
if (player->dashspeed)
{
if (dist < followthres && dist > touchdist) // Do positioning
{
cmd->angleturn = (ang - tails->angle) >> FRACBITS;
cmd->forwardmove = 50;
spinmode = true;
}
else if (dist < touchdist)
{
if (!bmom && (!(bot->pflags & PF_SPINNING) || (bot->dashspeed && bot->pflags & PF_SPINNING)))
{
cmd->angleturn = (sonic->angle - tails->angle) >> FRACBITS;
spin = true;
}
spinmode = true;
}
else
spinmode = false;
}
// Spin
else if (player->dashspeed == bot->dashspeed && player->pflags & PF_SPINNING)
{
if (bot->pflags & PF_SPINNING || !spin_last)
{
spin = true;
cmd->angleturn = (sonic->angle - tails->angle) >> FRACBITS;
cmd->forwardmove = MAXPLMOVE;
spinmode = true;
}
else
spinmode = false;
}
}
// 2D mode
else
{
if (((player->dashspeed && !bmom) || (player->dashspeed == bot->dashspeed && (player->pflags & PF_SPINNING)))
&& ((bot->pflags & PF_SPINNING) || !spin_last))
{
spin = true;
spinmode = true;
}
}
}
// ********
// FOLLOW
if (!(flymode || spinmode))
{
// Too far
if (panic || dist > followthres)
{
if (!_2d)
cmd->forwardmove = MAXPLMOVE;
else if (sonic->x > tails->x)
cmd->sidemove = MAXPLMOVE;
else
cmd->sidemove = -MAXPLMOVE;
}
// Within threshold
else if (!panic && dist > followmin && abs(zdist) < 192*scale)
{
if (!_2d)
cmd->forwardmove = FixedHypot(pcmd->forwardmove, pcmd->sidemove);
else
cmd->sidemove = pcmd->sidemove;
}
// Below min
else if (dist < followmin)
{
// Copy inputs
cmd->angleturn = (sonic->angle - tails->angle) >> FRACBITS;
bot->drawangle = ang;
cmd->forwardmove = 8 * pcmd->forwardmove / 10;
cmd->sidemove = 8 * pcmd->sidemove / 10;
}
}
// ********
// JUMP
if (!(flymode || spinmode))
{
// Flying catch-up
if (bot->pflags & PF_THOKKED)
{
cmd->forwardmove = min(MAXPLMOVE, (dist/scale)>>3);
if (zdist < -64*scale)
spin = true;
else if (zdist > 0 && !jump_last)
jump = true;
}
// Just landed
if (tails->eflags & MFE_JUSTHITFLOOR)
jump = false;
// Start jump
else if (!jump_last && !(bot->pflags & PF_JUMPED) //&& !(player->pflags & PF_SPINNING)
&& ((zdist > 32*scale && player->pflags & PF_JUMPED) // Following
|| (zdist > 64*scale && panic) // Vertical catch-up
|| (stalled && anxiety > 20 && bot->powers[pw_carry] == CR_NONE)
//|| (bmom < scale>>3 && dist > followthres && !(bot->powers[pw_carry])) // Stopped & not in carry state
|| (bot->pflags & PF_SPINNING && !(bot->pflags & PF_JUMPED)))) // Spinning
jump = true;
// Hold jump
else if (bot->pflags & PF_JUMPED && jump_last && tails->momz*flip > 0 && (zdist > 0 || panic))
jump = true; jump = true;
if (sonic->floorz > tails->floorz) // He's still above us? Jump HIGHER, then! // Start flying
else if (bot->pflags & PF_JUMPED && panic && !jump_last && bot->charability == CA_FLY)
jump = true; jump = true;
} }
// Decide when to spin // ********
if (sonic->player->pflags & PF_STARTDASH // HISTORY
&& (tails->player->pflags & PF_STARTDASH || (P_AproxDistance(tails->momx, tails->momy) < 2*FRACUNIT && !forward))) jump_last = jump;
spin = true; spin_last = spin;
// ********
// Thinkfly overlay
if (thinkfly)
{
if (!tails->target)
{
P_SetTarget(&tails->target, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
if (tails->target)
{
P_SetTarget(&tails->target->target, tails);
P_SetMobjState(tails->target, S_FLIGHTINDICATOR);
}
}
}
else if (tails->target && tails->target->type == MT_OVERLAY && tails->target->state == states+S_FLIGHTINDICATOR)
{
P_RemoveMobj(tails->target);
P_SetTarget(&tails->target, 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);
@ -141,7 +393,7 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin) void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin)
{ {
// don't try to do stuff if your sonic is in a minecart or something // don't try to do stuff if your sonic is in a minecart or something
if (players[consoleplayer].powers[pw_carry]) if (players[consoleplayer].powers[pw_carry] && players[consoleplayer].powers[pw_carry] != CR_PLAYER)
return; return;
// Turn the virtual keypresses into ticcmd_t. // Turn the virtual keypresses into ticcmd_t.
if (twodlevel || mo->flags2 & MF2_TWOD) { if (twodlevel || mo->flags2 & MF2_TWOD) {
@ -179,6 +431,7 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
cmd->sidemove += MAXPLMOVE<<FRACBITS>>16; cmd->sidemove += MAXPLMOVE<<FRACBITS>>16;
} }
} else { } else {
angle_t angle;
if (forward) if (forward)
cmd->forwardmove += MAXPLMOVE<<FRACBITS>>16; cmd->forwardmove += MAXPLMOVE<<FRACBITS>>16;
if (backward) if (backward)
@ -191,6 +444,15 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
cmd->sidemove -= MAXPLMOVE<<FRACBITS>>16; cmd->sidemove -= MAXPLMOVE<<FRACBITS>>16;
if (straferight) if (straferight)
cmd->sidemove += MAXPLMOVE<<FRACBITS>>16; cmd->sidemove += MAXPLMOVE<<FRACBITS>>16;
// cap inputs so the bot can't accelerate faster diagonally
angle = R_PointToAngle2(0, 0, cmd->sidemove << FRACBITS, cmd->forwardmove << FRACBITS);
{
INT32 maxforward = abs(P_ReturnThrustY(NULL, angle, MAXPLMOVE));
INT32 maxside = abs(P_ReturnThrustX(NULL, angle, MAXPLMOVE));
cmd->forwardmove = max(min(cmd->forwardmove, maxforward), -maxforward);
cmd->sidemove = max(min(cmd->sidemove, maxside), -maxside);
}
} }
if (jump) if (jump)
cmd->buttons |= BT_JUMP; cmd->buttons |= BT_JUMP;
@ -217,7 +479,7 @@ boolean B_CheckRespawn(player_t *player)
// If he's doing any of these things, he probably doesn't want to see us. // If he's doing any of these things, he probably doesn't want to see us.
if (sonic->player->pflags & (PF_GLIDING|PF_SLIDING|PF_BOUNCING) if (sonic->player->pflags & (PF_GLIDING|PF_SLIDING|PF_BOUNCING)
|| (sonic->player->panim != PA_IDLE && sonic->player->panim != PA_WALK) || (sonic->player->panim != PA_IDLE && sonic->player->panim != PA_WALK)
|| (sonic->player->powers[pw_carry])) || (sonic->player->powers[pw_carry] && sonic->player->powers[pw_carry] != CR_PLAYER))
return false; return false;
// Low ceiling, do not want! // Low ceiling, do not want!
@ -252,6 +514,8 @@ void B_RespawnBot(INT32 playernum)
if (!sonic || sonic->health <= 0) if (!sonic || sonic->health <= 0)
return; return;
B_ResetAI();
player->bot = 1; player->bot = 1;
P_SpawnPlayer(playernum); P_SpawnPlayer(playernum);
tails = player->mo; tails = player->mo;

View File

@ -90,6 +90,7 @@ SINT8 nodetoplayer[MAXNETNODES];
SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen
boolean nodeingame[MAXNETNODES]; // set false as nodes leave game boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
tic_t servermaxping = 800; // server's max ping. Defaults to 800
static tic_t nettics[MAXNETNODES]; // what tic the client have received static tic_t nettics[MAXNETNODES]; // what tic the client have received
static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet static tic_t supposedtics[MAXNETNODES]; // nettics prevision for smaller packet
static UINT8 nodewaiting[MAXNETNODES]; static UINT8 nodewaiting[MAXNETNODES];
@ -610,6 +611,11 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
rsp->health = LONG(players[i].mo->health); rsp->health = LONG(players[i].mo->health);
rsp->angle = (angle_t)LONG(players[i].mo->angle); rsp->angle = (angle_t)LONG(players[i].mo->angle);
#ifdef ROTSPRITE
rsp->rollangle = (angle_t)LONG(players[i].mo->rollangle);
#else
rsp->rollangle = 0;
#endif
rsp->x = LONG(players[i].mo->x); rsp->x = LONG(players[i].mo->x);
rsp->y = LONG(players[i].mo->y); rsp->y = LONG(players[i].mo->y);
rsp->z = LONG(players[i].mo->z); rsp->z = LONG(players[i].mo->z);
@ -760,6 +766,9 @@ static void resynch_read_player(resynch_pak *rsp)
//At this point, the player should have a body, whether they were respawned or not. //At this point, the player should have a body, whether they were respawned or not.
P_UnsetThingPosition(players[i].mo); P_UnsetThingPosition(players[i].mo);
players[i].mo->angle = (angle_t)LONG(rsp->angle); players[i].mo->angle = (angle_t)LONG(rsp->angle);
#ifdef ROTSPRITE
players[i].mo->rollangle = (angle_t)LONG(rsp->rollangle);
#endif
players[i].mo->eflags = (UINT16)SHORT(rsp->eflags); players[i].mo->eflags = (UINT16)SHORT(rsp->eflags);
players[i].mo->flags = LONG(rsp->flags); players[i].mo->flags = LONG(rsp->flags);
players[i].mo->flags2 = LONG(rsp->flags2); players[i].mo->flags2 = LONG(rsp->flags2);
@ -1292,10 +1301,23 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16); M_Memcpy(netbuffer->u.serverinfo.mapmd5, mapmd5, 16);
if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, "")) if (*mapheaderinfo[gamemap-1]->lvlttl)
strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33); {
char *read = mapheaderinfo[gamemap-1]->lvlttl, *writ = netbuffer->u.serverinfo.maptitle;
while (writ < (netbuffer->u.serverinfo.maptitle+32) && *read != '\0')
{
if (!(*read & 0x80))
{
*writ = toupper(*read);
writ++;
}
read++;
}
*writ = '\0';
//strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33);
}
else else
strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 33); strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32);
netbuffer->u.serverinfo.maptitle[32] = '\0'; netbuffer->u.serverinfo.maptitle[32] = '\0';
@ -1349,7 +1371,11 @@ static void SV_SendPlayerInfo(INT32 node)
netbuffer->u.playerinfo[i].score = LONG(players[i].score); netbuffer->u.playerinfo[i].score = LONG(players[i].score);
netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE)); netbuffer->u.playerinfo[i].timeinserver = SHORT((UINT16)(players[i].jointime / TICRATE));
netbuffer->u.playerinfo[i].skin = (UINT8)players[i].skin; netbuffer->u.playerinfo[i].skin = (UINT8)(players[i].skin
#ifdef DEVELOP // it's safe to do this only because PLAYERINFO isn't read by the game itself
% 3
#endif
);
// Extra data // Extra data
netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor; netbuffer->u.playerinfo[i].data = 0; //players[i].skincolor;
@ -1607,7 +1633,7 @@ static void CL_LoadReceivedSavegame(void)
{ {
CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl); CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl);
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
CONS_Printf(M_GetText(" ZONE")); CONS_Printf(M_GetText(" Zone"));
if (actnum > 0) if (actnum > 0)
CONS_Printf(" %2d", actnum); CONS_Printf(" %2d", actnum);
} }
@ -4208,10 +4234,12 @@ static void HandlePacketFromPlayer(SINT8 node)
//Update client ping table from the server. //Update client ping table from the server.
if (client) if (client)
{ {
INT32 i; UINT8 i;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i]) if (playeringame[i])
playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i]; playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i];
servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS];
} }
break; break;
@ -4591,7 +4619,7 @@ static void Local_Maketic(INT32 realtics)
{ {
I_OsPolling(); // I_Getevent I_OsPolling(); // I_Getevent
D_ProcessEvents(); // menu responder, cons responder, D_ProcessEvents(); // menu responder, cons responder,
// game responder calls HU_Responder, AM_Responder, F_Responder, // game responder calls HU_Responder, AM_Responder,
// and G_MapEventsToControls // and G_MapEventsToControls
if (!dedicated) rendergametic = gametic; if (!dedicated) rendergametic = gametic;
// translate inputs (keyboard/mouse/joystick) into game controls // translate inputs (keyboard/mouse/joystick) into game controls
@ -4733,6 +4761,14 @@ void TryRunTics(tic_t realtics)
} }
} }
/*
Ping Update except better:
We call this once per second and check for people's pings. If their ping happens to be too high, we increment some timer and kick them out.
If they're not lagging, decrement the timer by 1. Of course, reset all of this if they leave.
*/
static INT32 pingtimeout[MAXPLAYERS];
static inline void PingUpdate(void) static inline void PingUpdate(void)
{ {
INT32 i; INT32 i;
@ -4753,6 +4789,8 @@ static inline void PingUpdate(void)
laggers[i] = true; laggers[i] = true;
numlaggers++; numlaggers++;
} }
else
pingtimeout[i] = 0;
} }
//kick lagging players... unless everyone but the server's ping sucks. //kick lagging players... unless everyone but the server's ping sucks.
@ -4763,12 +4801,27 @@ static inline void PingUpdate(void)
{ {
if (playeringame[i] && laggers[i]) if (playeringame[i] && laggers[i])
{ {
char buf[2]; pingtimeout[i]++;
if (pingtimeout[i] > cv_pingtimeout.value)
// ok your net has been bad for too long, you deserve to die.
{
char buf[2];
buf[0] = (char)i; pingtimeout[i] = 0;
buf[1] = KICK_MSG_PING_HIGH;
SendNetXCmd(XD_KICK, &buf, 2); buf[0] = (char)i;
buf[1] = KICK_MSG_PING_HIGH;
SendNetXCmd(XD_KICK, &buf, 2);
}
} }
/*
you aren't lagging,
but you aren't free yet.
In case you'll keep spiking,
we just make the timer go back down. (Very unstable net must still get kicked).
*/
else
pingtimeout[i] = (pingtimeout[i] == 0 ? 0 : pingtimeout[i]-1);
} }
} }
} }
@ -4783,10 +4836,13 @@ static inline void PingUpdate(void)
realpingtable[i] = 0; //Reset each as we go. realpingtable[i] = 0; //Reset each as we go.
} }
// send the server's maxping as last element of our ping table. This is useful to let us know when we're about to get kicked.
netbuffer->u.pingtable[MAXPLAYERS] = cv_maxping.value;
//send out our ping packets //send out our ping packets
for (i = 0; i < MAXNETNODES; i++) for (i = 0; i < MAXNETNODES; i++)
if (nodeingame[i]) if (nodeingame[i])
HSendPacket(i, true, 0, sizeof(INT32) * MAXPLAYERS); HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1));
pingmeasurecount = 1; //Reset count pingmeasurecount = 1; //Reset count
} }
@ -4816,7 +4872,7 @@ void NetUpdate(void)
if (server) if (server)
{ {
if (netgame && !(gametime % 255)) if (netgame && !(gametime % 35)) // update once per second.
PingUpdate(); PingUpdate();
// update node latency values so we can take an average later. // update node latency values so we can take an average later.
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)

View File

@ -255,6 +255,7 @@ typedef struct
INT32 health; INT32 health;
angle_t angle; angle_t angle;
angle_t rollangle;
fixed_t x; fixed_t x;
fixed_t y; fixed_t y;
fixed_t z; fixed_t z;
@ -423,9 +424,9 @@ typedef struct
serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...) serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...)
askinfo_pak askinfo; // 61 bytes askinfo_pak askinfo; // 61 bytes
msaskinfo_pak msaskinfo; // 22 bytes msaskinfo_pak msaskinfo; // 22 bytes
plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes (I'd say 36~38) plrinfo playerinfo[MAXPLAYERS]; // 576 bytes(?)
plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes (welp they ARE) plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?)
UINT32 pingtable[MAXPLAYERS]; // 128 bytes UINT32 pingtable[MAXPLAYERS+1]; // 68 bytes
} u; // This is needed to pack diff packet types data together } u; // This is needed to pack diff packet types data together
} ATTRPACK doomdata_t; } ATTRPACK doomdata_t;
@ -487,6 +488,7 @@ extern tic_t jointimeout;
extern UINT16 pingmeasurecount; extern UINT16 pingmeasurecount;
extern UINT32 realpingtable[MAXPLAYERS]; extern UINT32 realpingtable[MAXPLAYERS];
extern UINT32 playerpingtable[MAXPLAYERS]; extern UINT32 playerpingtable[MAXPLAYERS];
extern tic_t servermaxping;
extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed; extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed;

View File

@ -274,7 +274,7 @@ static void D_Display(void)
&& wipetypepre != UINT8_MAX) && wipetypepre != UINT8_MAX)
{ {
F_WipeStartScreen(); F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeColorFill(31);
F_WipeEndScreen(); F_WipeEndScreen();
F_RunWipe(wipetypepre, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN); F_RunWipe(wipetypepre, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN);
} }
@ -291,8 +291,11 @@ static void D_Display(void)
switch (gamestate) switch (gamestate)
{ {
case GS_TITLESCREEN: case GS_TITLESCREEN:
F_TitleScreenDrawer(); if (!titlemapinaction || !curbghide) {
break; F_TitleScreenDrawer();
break;
}
/* FALLTHRU */
case GS_LEVEL: case GS_LEVEL:
if (!gametic) if (!gametic)
break; break;
@ -363,11 +366,56 @@ static void D_Display(void)
// clean up border stuff // clean up border stuff
// see if the border needs to be initially drawn // see if the border needs to be initially drawn
if (gamestate == GS_LEVEL) if (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction && curbghide && (!hidetitlemap)))
{ {
// draw the view directly // draw the view directly
D_Render(); if (!automapactive && !dedicated && cv_renderview.value)
{
if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
{
topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
objectsdrawn = 0;
#ifdef HWRENDER
if (rendermode != render_soft)
HWR_RenderPlayerView(0, &players[displayplayer]);
else
#endif
if (rendermode != render_none)
R_RenderPlayerView(&players[displayplayer]);
}
// render the second screen
if (splitscreen && players[secondarydisplayplayer].mo)
{
#ifdef HWRENDER
if (rendermode != render_soft)
HWR_RenderPlayerView(1, &players[secondarydisplayplayer]);
else
#endif
if (rendermode != render_none)
{
viewwindowy = vid.height / 2;
M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));
topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
R_RenderPlayerView(&players[secondarydisplayplayer]);
viewwindowy = 0;
M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
}
}
// Image postprocessing effect
if (rendermode == render_soft)
{
if (postimgtype)
V_DoPostProcessor(0, postimgtype, postimgparam);
if (postimgtype2)
V_DoPostProcessor(1, postimgtype2, postimgparam2);
}
}
if (lastdraw) if (lastdraw)
{ {
@ -380,9 +428,14 @@ static void D_Display(void)
lastdraw = false; lastdraw = false;
} }
ST_Drawer(); if (gamestate == GS_LEVEL)
F_TextPromptDrawer(); {
HU_Drawer(); ST_Drawer();
F_TextPromptDrawer();
HU_Drawer();
}
else
F_TitleScreenDrawer();
} }
} }
@ -435,6 +488,15 @@ static void D_Display(void)
if (rendermode != render_none) if (rendermode != render_none)
{ {
F_WipeEndScreen(); F_WipeEndScreen();
// Funny.
if (WipeStageTitle && st_overlay)
{
lt_ticker--;
lt_lasttic = lt_ticker;
ST_preLevelTitleCardDrawer(0, false);
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
F_WipeStartScreen();
}
F_RunWipe(wipetypepost, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN); F_RunWipe(wipetypepost, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN);
} }
@ -444,7 +506,7 @@ static void D_Display(void)
framecount = 0; framecount = 0;
demostarttime = I_GetTime(); demostarttime = I_GetTime();
} }
wipetypepost = -1; wipetypepost = -1;
} }
else else
@ -485,56 +547,6 @@ static void D_Display(void)
} }
} }
void D_Render(void)
{
if (!automapactive && !dedicated && cv_renderview.value)
{
if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
{
topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
objectsdrawn = 0;
#ifdef HWRENDER
if (rendermode != render_soft)
HWR_RenderPlayerView(0, &players[displayplayer]);
else
#endif
if (rendermode != render_none)
R_RenderPlayerView(&players[displayplayer]);
}
// render the second screen
if (splitscreen && players[secondarydisplayplayer].mo)
{
#ifdef HWRENDER
if (rendermode != render_soft)
HWR_RenderPlayerView(1, &players[secondarydisplayplayer]);
else
#endif
if (rendermode != render_none)
{
viewwindowy = vid.height / 2;
M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));
topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
R_RenderPlayerView(&players[secondarydisplayplayer]);
viewwindowy = 0;
M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
}
}
// Image postprocessing effect
if (rendermode == render_soft)
{
if (postimgtype)
V_DoPostProcessor(0, postimgtype, postimgparam);
if (postimgtype2)
V_DoPostProcessor(1, postimgtype2, postimgparam2);
}
}
}
// ========================================================================= // =========================================================================
// D_SRB2Loop // D_SRB2Loop
// ========================================================================= // =========================================================================
@ -655,6 +667,7 @@ void D_SRB2Loop(void)
// consoleplayer -> displayplayer (hear sounds from viewpoint) // consoleplayer -> displayplayer (hear sounds from viewpoint)
S_UpdateSounds(); // move positional sounds S_UpdateSounds(); // move positional sounds
S_UpdateClosedCaptions();
// check for media change, loop music.. // check for media change, loop music..
I_UpdateCD(); I_UpdateCD();
@ -720,6 +733,8 @@ void D_StartTitle(void)
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
CL_ClearPlayer(i); CL_ClearPlayer(i);
players[consoleplayer].availabilities = players[1].availabilities = R_GetSkinAvailabilities(); // players[1] is supposed to be for 2p
splitscreen = false; splitscreen = false;
SplitScreen_OnChange(); SplitScreen_OnChange();
botingame = false; botingame = false;

View File

@ -54,7 +54,4 @@ const char *D_Home(void);
void D_AdvanceDemo(void); void D_AdvanceDemo(void);
void D_StartTitle(void); void D_StartTitle(void);
/* Here for title maps */
void D_Render(void);
#endif //__D_MAIN__ #endif //__D_MAIN__

View File

@ -87,6 +87,7 @@ static void JoinTimeout_OnChange(void);
static void CoopStarposts_OnChange(void); static void CoopStarposts_OnChange(void);
static void CoopLives_OnChange(void); static void CoopLives_OnChange(void);
static void ExitMove_OnChange(void);
static void Ringslinger_OnChange(void); static void Ringslinger_OnChange(void);
static void Gravity_OnChange(void); static void Gravity_OnChange(void);
@ -341,21 +342,32 @@ consvar_t cv_nettimeout = {"nettimeout", "350", CV_CALL|CV_SAVE, nettimeout_cons
static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}}; static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
consvar_t cv_jointimeout = {"jointimeout", "350", CV_CALL|CV_SAVE, jointimeout_cons_t, JoinTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_jointimeout = {"jointimeout", "350", CV_CALL|CV_SAVE, jointimeout_cons_t, JoinTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_maxping = {"maxping", "0", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_maxping = {"maxping", "0", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t pingtimeout_cons_t[] = {{8, "MIN"}, {120, "MAX"}, {0, NULL}};
consvar_t cv_pingtimeout = {"pingtimeout", "10", CV_SAVE, pingtimeout_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
// show your ping on the HUD next to framerate. Defaults to warning only (shows up if your ping is > maxping)
static CV_PossibleValue_t showping_cons_t[] = {{0, "Off"}, {1, "Always"}, {2, "Warning"}, {0, NULL}};
consvar_t cv_showping = {"showping", "Warning", CV_SAVE, showping_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
// Intermission time Tails 04-19-2002 // Intermission time Tails 04-19-2002
static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}}; static CV_PossibleValue_t inttime_cons_t[] = {{0, "MIN"}, {3600, "MAX"}, {0, NULL}};
consvar_t cv_inttime = {"inttime", "10", CV_NETVAR, inttime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_inttime = {"inttime", "10", CV_NETVAR, inttime_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t coopstarposts_cons_t[] = {{0, "Per-player"}, {1, "Shared"}, {2, "Teamwork"}, {0, NULL}}; static CV_PossibleValue_t coopstarposts_cons_t[] = {{0, "Per-player"}, {1, "Shared"}, {2, "Teamwork"}, {0, NULL}};
consvar_t cv_coopstarposts = {"coopstarposts", "Teamwork", CV_NETVAR|CV_CALL|CV_CHEAT, coopstarposts_cons_t, CoopStarposts_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_coopstarposts = {"coopstarposts", "Per-player", CV_NETVAR|CV_CALL, coopstarposts_cons_t, CoopStarposts_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t cooplives_cons_t[] = {{0, "Infinite"}, {1, "Per-player"}, {2, "Avoid Game Over"}, {3, "Single pool"}, {0, NULL}}; static CV_PossibleValue_t cooplives_cons_t[] = {{0, "Infinite"}, {1, "Per-player"}, {2, "Avoid Game Over"}, {3, "Single pool"}, {0, NULL}};
consvar_t cv_cooplives = {"cooplives", "Avoid Game Over", CV_NETVAR|CV_CALL|CV_CHEAT, cooplives_cons_t, CoopLives_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_cooplives = {"cooplives", "Avoid Game Over", CV_NETVAR|CV_CALL|CV_CHEAT, cooplives_cons_t, CoopLives_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {0, NULL}}; static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {0, NULL}};
consvar_t cv_advancemap = {"advancemap", "Next", CV_NETVAR, advancemap_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_advancemap = {"advancemap", "Next", CV_NETVAR, advancemap_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "1/4"}, {2, "Half"}, {3, "3/4"}, {4, "All"}, {0, NULL}}; static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "1/4"}, {2, "Half"}, {3, "3/4"}, {4, "All"}, {0, NULL}};
consvar_t cv_playersforexit = {"playersforexit", "All", CV_NETVAR, playersforexit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_playersforexit = {"playersforexit", "All", CV_NETVAR, playersforexit_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_exitmove = {"exitmove", "Off", CV_NETVAR|CV_CALL, CV_OnOff, ExitMove_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_runscripts = {"runscripts", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_runscripts = {"runscripts", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_pause = {"pausepermission", "Server", CV_NETVAR, pause_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_pause = {"pausepermission", "Server", CV_NETVAR, pause_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@ -514,6 +526,7 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_inttime); CV_RegisterVar(&cv_inttime);
CV_RegisterVar(&cv_advancemap); CV_RegisterVar(&cv_advancemap);
CV_RegisterVar(&cv_playersforexit); CV_RegisterVar(&cv_playersforexit);
CV_RegisterVar(&cv_exitmove);
CV_RegisterVar(&cv_timelimit); CV_RegisterVar(&cv_timelimit);
CV_RegisterVar(&cv_playbackspeed); CV_RegisterVar(&cv_playbackspeed);
CV_RegisterVar(&cv_forceskin); CV_RegisterVar(&cv_forceskin);
@ -577,6 +590,8 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_skipmapcheck); CV_RegisterVar(&cv_skipmapcheck);
CV_RegisterVar(&cv_sleep); CV_RegisterVar(&cv_sleep);
CV_RegisterVar(&cv_maxping); CV_RegisterVar(&cv_maxping);
CV_RegisterVar(&cv_pingtimeout);
CV_RegisterVar(&cv_showping);
#ifdef SEENAMES #ifdef SEENAMES
CV_RegisterVar(&cv_allowseenames); CV_RegisterVar(&cv_allowseenames);
@ -1213,16 +1228,16 @@ static void SendNameAndColor(void)
} }
else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUsable(consoleplayer, foundskin)) else if ((foundskin = R_SkinAvailable(cv_skin.string)) != -1 && R_SkinUsable(consoleplayer, foundskin))
{ {
boolean notsame; //boolean notsame;
cv_skin.value = foundskin; cv_skin.value = foundskin;
notsame = (cv_skin.value != players[consoleplayer].skin); //notsame = (cv_skin.value != players[consoleplayer].skin);
SetPlayerSkin(consoleplayer, cv_skin.string); SetPlayerSkin(consoleplayer, cv_skin.string);
CV_StealthSet(&cv_skin, skins[cv_skin.value].name); CV_StealthSet(&cv_skin, skins[cv_skin.value].name);
if (notsame) /*if (notsame)
{ {
CV_StealthSetValue(&cv_playercolor, skins[cv_skin.value].prefcolor); CV_StealthSetValue(&cv_playercolor, skins[cv_skin.value].prefcolor);
@ -1230,7 +1245,7 @@ static void SendNameAndColor(void)
if (players[consoleplayer].mo) if (players[consoleplayer].mo)
players[consoleplayer].mo->color = (UINT8)players[consoleplayer].skincolor; players[consoleplayer].mo->color = (UINT8)players[consoleplayer].skincolor;
} }*/
} }
else else
{ {
@ -1341,15 +1356,16 @@ static void SendNameAndColor2(void)
} }
else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUsable(secondplaya, foundskin)) else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUsable(secondplaya, foundskin))
{ {
boolean notsame; //boolean notsame;
cv_skin2.value = foundskin; cv_skin2.value = foundskin;
notsame = (cv_skin2.value != players[secondplaya].skin); //notsame = (cv_skin2.value != players[secondplaya].skin);
SetPlayerSkin(secondplaya, cv_skin2.string); SetPlayerSkin(secondplaya, cv_skin2.string);
CV_StealthSet(&cv_skin2, skins[cv_skin2.value].name);
if (notsame) /*if (notsame)
{ {
CV_StealthSetValue(&cv_playercolor2, skins[players[secondplaya].skin].prefcolor); CV_StealthSetValue(&cv_playercolor2, skins[players[secondplaya].skin].prefcolor);
@ -1357,7 +1373,7 @@ static void SendNameAndColor2(void)
if (players[secondplaya].mo) if (players[secondplaya].mo)
players[secondplaya].mo->color = players[secondplaya].skincolor; players[secondplaya].mo->color = players[secondplaya].skincolor;
} }*/
} }
else else
{ {
@ -1924,6 +1940,17 @@ static void Command_Map_f(void)
d = atoi(gametypename); d = atoi(gametypename);
if (d >= 0 && d < NUMGAMETYPES) if (d >= 0 && d < NUMGAMETYPES)
newgametype = d; newgametype = d;
else
{
CONS_Alert(CONS_ERROR,
"Gametype number %d is out of range. Use a number between"
" 0 and %d inclusive. ...Or just use the name. :v\n",
d,
NUMGAMETYPES-1);
Z_Free(realmapname);
Z_Free(mapname);
return;
}
} }
else else
{ {
@ -2039,7 +2066,9 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
lastgametype = gametype; lastgametype = gametype;
gametype = READUINT8(*cp); gametype = READUINT8(*cp);
if (gametype != lastgametype) if (gametype < 0 || gametype >= NUMGAMETYPES)
gametype = lastgametype;
else if (gametype != lastgametype)
D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype D_GameTypeChanged(lastgametype); // emulate consvar_t behavior for gametype
skipprecutscene = ((flags & (1<<2)) != 0); skipprecutscene = ((flags & (1<<2)) != 0);
@ -3778,6 +3807,17 @@ static void CoopLives_OnChange(void)
} }
} }
static void ExitMove_OnChange(void)
{
if (!(netgame || multiplayer) || gametype != GT_COOP)
return;
if (cv_exitmove.value)
CONS_Printf(M_GetText("Players can now move after completing the level.\n"));
else
CONS_Printf(M_GetText("Players can no longer move after completing the level.\n"));
}
UINT32 timelimitintics = 0; UINT32 timelimitintics = 0;
/** Deals with a timelimit change by printing the change to the console. /** Deals with a timelimit change by printing the change to the console.
@ -4250,6 +4290,8 @@ void Command_ExitGame_f(void)
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
CL_ClearPlayer(i); CL_ClearPlayer(i);
players[consoleplayer].availabilities = players[1].availabilities = R_GetSkinAvailabilities(); // players[1] is supposed to be for 2p
splitscreen = false; splitscreen = false;
SplitScreen_OnChange(); SplitScreen_OnChange();
botingame = false; botingame = false;
@ -4310,9 +4352,9 @@ static void Command_Isgamemodified_f(void)
if (savemoddata) if (savemoddata)
CONS_Printf(M_GetText("modifiedgame is true, but you can save emblem and time data in this mod.\n")); CONS_Printf(M_GetText("modifiedgame is true, but you can save emblem and time data in this mod.\n"));
else if (modifiedgame) else if (modifiedgame)
CONS_Printf(M_GetText("modifiedgame is true, secrets will not be unlocked\n")); CONS_Printf(M_GetText("modifiedgame is true, extras will not be unlocked\n"));
else else
CONS_Printf(M_GetText("modifiedgame is false, you can unlock secrets\n")); CONS_Printf(M_GetText("modifiedgame is false, you can unlock extras\n"));
} }
static void Command_Cheats_f(void) static void Command_Cheats_f(void)

View File

@ -94,7 +94,7 @@ extern consvar_t cv_recycler;
extern consvar_t cv_itemfinder; extern consvar_t cv_itemfinder;
extern consvar_t cv_inttime, cv_coopstarposts, cv_cooplives, cv_advancemap, cv_playersforexit; extern consvar_t cv_inttime, cv_coopstarposts, cv_cooplives, cv_advancemap, cv_playersforexit, cv_exitmove;
extern consvar_t cv_overtime; extern consvar_t cv_overtime;
extern consvar_t cv_startinglives; extern consvar_t cv_startinglives;
@ -106,6 +106,9 @@ extern consvar_t cv_ringslinger, cv_soundtest;
extern consvar_t cv_specialrings, cv_powerstones, cv_matchboxes, cv_competitionboxes; extern consvar_t cv_specialrings, cv_powerstones, cv_matchboxes, cv_competitionboxes;
extern consvar_t cv_maxping; extern consvar_t cv_maxping;
extern consvar_t cv_pingtimeout;
extern consvar_t cv_showping;
extern consvar_t cv_skipmapcheck; extern consvar_t cv_skipmapcheck;

View File

@ -716,7 +716,7 @@ void SV_FileSendTicker(void)
if (ram) if (ram)
M_Memcpy(p->data, &f->id.ram[transfer[i].position], size); M_Memcpy(p->data, &f->id.ram[transfer[i].position], size);
else if (fread(p->data, 1, size, transfer[i].currentfile) != size) else if (fread(p->data, 1, size, transfer[i].currentfile) != size)
I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, strerror(ferror(transfer[i].currentfile))); I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, M_FileError(transfer[i].currentfile));
p->position = LONG(transfer[i].position); p->position = LONG(transfer[i].position);
// Put flag so receiver knows the total size // Put flag so receiver knows the total size
if (transfer[i].position + size == f->size) if (transfer[i].position + size == f->size)
@ -794,7 +794,7 @@ void Got_Filetxpak(void)
// We can receive packet in the wrong order, anyway all os support gaped file // We can receive packet in the wrong order, anyway all os support gaped file
fseek(file->file, pos, SEEK_SET); fseek(file->file, pos, SEEK_SET);
if (fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1) if (fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1)
I_Error("Can't write to %s: %s\n",filename, strerror(ferror(file->file))); I_Error("Can't write to %s: %s\n",filename, M_FileError(file->file));
file->currentsize += size; file->currentsize += size;
// Finished? // Finished?

View File

@ -151,6 +151,7 @@ typedef enum
/*** misc ***/ /*** misc ***/
PF_FORCESTRAFE = 1<<28, // Turning inputs are translated into strafing inputs PF_FORCESTRAFE = 1<<28, // Turning inputs are translated into strafing inputs
PF_CANCARRY = 1<<29, // Can carry another player? PF_CANCARRY = 1<<29, // Can carry another player?
PF_FINISHED = 1<<30, // The player finished the level. NOT the same as exiting
// up to 1<<31 is free // up to 1<<31 is free
} pflags_t; } pflags_t;

View File

@ -1201,6 +1201,14 @@ static void readlevelheader(MYFILE *f, INT32 num)
word2 = tmp += 2; word2 = tmp += 2;
i = atoi(word2); // used for numerical settings i = atoi(word2); // used for numerical settings
if (fastcmp(word, "LEVELNAME"))
{
deh_strlcpy(mapheaderinfo[num-1]->lvlttl, word2,
sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num));
strlcpy(mapheaderinfo[num-1]->selectheading, word2, sizeof(mapheaderinfo[num-1]->selectheading)); // not deh_ so only complains once
continue;
}
// CHEAP HACK: move this over here for lowercase subtitles // CHEAP HACK: move this over here for lowercase subtitles
if (fastcmp(word, "SUBTITLE")) if (fastcmp(word, "SUBTITLE"))
{ {
@ -1344,12 +1352,6 @@ static void readlevelheader(MYFILE *f, INT32 num)
} }
// Strings that can be truncated // Strings that can be truncated
else if (fastcmp(word, "LEVELNAME"))
{
deh_strlcpy(mapheaderinfo[num-1]->lvlttl, word2,
sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num));
strlcpy(mapheaderinfo[num-1]->selectheading, word2, sizeof(mapheaderinfo[num-1]->selectheading)); // not deh_ so only complains once
}
else if (fastcmp(word, "SELECTHEADING")) else if (fastcmp(word, "SELECTHEADING"))
{ {
deh_strlcpy(mapheaderinfo[num-1]->selectheading, word2, deh_strlcpy(mapheaderinfo[num-1]->selectheading, word2,
@ -1573,6 +1575,20 @@ static void readlevelheader(MYFILE *f, INT32 num)
else else
mapheaderinfo[num-1]->levelflags &= ~LF_MIXNIGHTSCOUNTDOWN; mapheaderinfo[num-1]->levelflags &= ~LF_MIXNIGHTSCOUNTDOWN;
} }
else if (fastcmp(word, "WARNINGTITLE"))
{
if (i || word2[0] == 'T' || word2[0] == 'Y')
mapheaderinfo[num-1]->levelflags |= LF_WARNINGTITLE;
else
mapheaderinfo[num-1]->levelflags &= ~LF_WARNINGTITLE;
}
else if (fastcmp(word, "NOTITLECARD"))
{
if (i || word2[0] == 'T' || word2[0] == 'Y')
mapheaderinfo[num-1]->levelflags |= LF_NOTITLECARD;
else
mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARD;
}
// Individual triggers for menu flags // Individual triggers for menu flags
else if (fastcmp(word, "HIDDEN")) else if (fastcmp(word, "HIDDEN"))
@ -2781,6 +2797,9 @@ static actionpointer_t actionpointers[] =
{{A_PterabyteHover}, "A_PTERABYTEHOVER"}, {{A_PterabyteHover}, "A_PTERABYTEHOVER"},
{{A_RolloutSpawn}, "A_ROLLOUTSPAWN"}, {{A_RolloutSpawn}, "A_ROLLOUTSPAWN"},
{{A_RolloutRock}, "A_ROLLOUTROCK"}, {{A_RolloutRock}, "A_ROLLOUTROCK"},
{{A_DragonbomberSpawn}, "A_DRAGONBOMERSPAWN"},
{{A_DragonWing}, "A_DRAGONWING"},
{{A_DragonSegment}, "A_DRAGONSEGMENT"},
{{NULL}, "NONE"}, {{NULL}, "NONE"},
// This NULL entry must be the last in the list // This NULL entry must be the last in the list
@ -3165,7 +3184,6 @@ static void readextraemblemdata(MYFILE *f, INT32 num)
// Now get the part after // Now get the part after
word2 = tmp += 2; word2 = tmp += 2;
strupr(word2);
value = atoi(word2); // used for numerical settings value = atoi(word2); // used for numerical settings
@ -3177,22 +3195,26 @@ static void readextraemblemdata(MYFILE *f, INT32 num)
sizeof (extraemblems[num-1].description), va("Extra emblem %d: objective", num)); sizeof (extraemblems[num-1].description), va("Extra emblem %d: objective", num));
else if (fastcmp(word, "CONDITIONSET")) else if (fastcmp(word, "CONDITIONSET"))
extraemblems[num-1].conditionset = (UINT8)value; extraemblems[num-1].conditionset = (UINT8)value;
else if (fastcmp(word, "SPRITE"))
{
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = word2[0];
else
value += 'A'-1;
if (value < 'A' || value > 'Z')
deh_warning("Emblem %d: sprite must be from A - Z (1 - 26)", num);
else
extraemblems[num-1].sprite = (UINT8)value;
}
else if (fastcmp(word, "COLOR"))
extraemblems[num-1].color = get_number(word2);
else else
deh_warning("Extra emblem %d: unknown word '%s'", num, word); {
strupr(word2);
if (fastcmp(word, "SPRITE"))
{
if (word2[0] >= 'A' && word2[0] <= 'Z')
value = word2[0];
else
value += 'A'-1;
if (value < 'A' || value > 'Z')
deh_warning("Emblem %d: sprite must be from A - Z (1 - 26)", num);
else
extraemblems[num-1].sprite = (UINT8)value;
}
else if (fastcmp(word, "COLOR"))
extraemblems[num-1].color = get_number(word2);
else
deh_warning("Extra emblem %d: unknown word '%s'", num, word);
}
} }
} while (!myfeof(f)); } while (!myfeof(f));
@ -3243,7 +3265,6 @@ static void readunlockable(MYFILE *f, INT32 num)
// Now get the part after // Now get the part after
word2 = tmp += 2; word2 = tmp += 2;
strupr(word2);
i = atoi(word2); // used for numerical settings i = atoi(word2); // used for numerical settings
@ -3253,54 +3274,58 @@ static void readunlockable(MYFILE *f, INT32 num)
else if (fastcmp(word, "OBJECTIVE")) else if (fastcmp(word, "OBJECTIVE"))
deh_strlcpy(unlockables[num].objective, word2, deh_strlcpy(unlockables[num].objective, word2,
sizeof (unlockables[num].objective), va("Unlockable %d: objective", num)); sizeof (unlockables[num].objective), va("Unlockable %d: objective", num));
else if (fastcmp(word, "HEIGHT"))
unlockables[num].height = (UINT16)i;
else if (fastcmp(word, "CONDITIONSET"))
unlockables[num].conditionset = (UINT8)i;
else if (fastcmp(word, "NOCECHO"))
unlockables[num].nocecho = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
else if (fastcmp(word, "NOCHECKLIST"))
unlockables[num].nochecklist = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
else if (fastcmp(word, "TYPE"))
{
if (fastcmp(word2, "NONE"))
unlockables[num].type = SECRET_NONE;
else if (fastcmp(word2, "ITEMFINDER"))
unlockables[num].type = SECRET_ITEMFINDER;
else if (fastcmp(word2, "EMBLEMHINTS"))
unlockables[num].type = SECRET_EMBLEMHINTS;
else if (fastcmp(word2, "PANDORA"))
unlockables[num].type = SECRET_PANDORA;
else if (fastcmp(word2, "CREDITS"))
unlockables[num].type = SECRET_CREDITS;
else if (fastcmp(word2, "RECORDATTACK"))
unlockables[num].type = SECRET_RECORDATTACK;
else if (fastcmp(word2, "NIGHTSMODE"))
unlockables[num].type = SECRET_NIGHTSMODE;
else if (fastcmp(word2, "HEADER"))
unlockables[num].type = SECRET_HEADER;
else if (fastcmp(word2, "LEVELSELECT"))
unlockables[num].type = SECRET_LEVELSELECT;
else if (fastcmp(word2, "WARP"))
unlockables[num].type = SECRET_WARP;
else if (fastcmp(word2, "SOUNDTEST"))
unlockables[num].type = SECRET_SOUNDTEST;
else
unlockables[num].type = (INT16)i;
}
else if (fastcmp(word, "VAR"))
{
// Support using the actual map name,
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
i = M_MapNumber(word2[0], word2[1]);
unlockables[num].variable = (INT16)i;
}
else else
deh_warning("Unlockable %d: unknown word '%s'", num+1, word); {
strupr(word2);
if (fastcmp(word, "HEIGHT"))
unlockables[num].height = (UINT16)i;
else if (fastcmp(word, "CONDITIONSET"))
unlockables[num].conditionset = (UINT8)i;
else if (fastcmp(word, "NOCECHO"))
unlockables[num].nocecho = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
else if (fastcmp(word, "NOCHECKLIST"))
unlockables[num].nochecklist = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y');
else if (fastcmp(word, "TYPE"))
{
if (fastcmp(word2, "NONE"))
unlockables[num].type = SECRET_NONE;
else if (fastcmp(word2, "ITEMFINDER"))
unlockables[num].type = SECRET_ITEMFINDER;
else if (fastcmp(word2, "EMBLEMHINTS"))
unlockables[num].type = SECRET_EMBLEMHINTS;
else if (fastcmp(word2, "PANDORA"))
unlockables[num].type = SECRET_PANDORA;
else if (fastcmp(word2, "CREDITS"))
unlockables[num].type = SECRET_CREDITS;
else if (fastcmp(word2, "RECORDATTACK"))
unlockables[num].type = SECRET_RECORDATTACK;
else if (fastcmp(word2, "NIGHTSMODE"))
unlockables[num].type = SECRET_NIGHTSMODE;
else if (fastcmp(word2, "HEADER"))
unlockables[num].type = SECRET_HEADER;
else if (fastcmp(word2, "LEVELSELECT"))
unlockables[num].type = SECRET_LEVELSELECT;
else if (fastcmp(word2, "WARP"))
unlockables[num].type = SECRET_WARP;
else if (fastcmp(word2, "SOUNDTEST"))
unlockables[num].type = SECRET_SOUNDTEST;
else
unlockables[num].type = (INT16)i;
}
else if (fastcmp(word, "VAR"))
{
// Support using the actual map name,
// i.e., Level AB, Level FZ, etc.
// Convert to map number
if (word2[0] >= 'A' && word2[0] <= 'Z')
i = M_MapNumber(word2[0], word2[1]);
unlockables[num].variable = (INT16)i;
}
else
deh_warning("Unlockable %d: unknown word '%s'", num+1, word);
}
} }
} while (!myfeof(f)); // finish when the line is empty } while (!myfeof(f)); // finish when the line is empty
@ -4950,22 +4975,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_MINUS_BURST4", "S_MINUS_BURST4",
"S_MINUS_BURST5", "S_MINUS_BURST5",
"S_MINUS_POPUP", "S_MINUS_POPUP",
"S_MINUS_UPWARD1", "S_MINUS_AERIAL1",
"S_MINUS_UPWARD2", "S_MINUS_AERIAL2",
"S_MINUS_UPWARD3", "S_MINUS_AERIAL3",
"S_MINUS_UPWARD4", "S_MINUS_AERIAL4",
"S_MINUS_UPWARD5",
"S_MINUS_UPWARD6",
"S_MINUS_UPWARD7",
"S_MINUS_UPWARD8",
"S_MINUS_DOWNWARD1",
"S_MINUS_DOWNWARD2",
"S_MINUS_DOWNWARD3",
"S_MINUS_DOWNWARD4",
"S_MINUS_DOWNWARD5",
"S_MINUS_DOWNWARD6",
"S_MINUS_DOWNWARD7",
"S_MINUS_DOWNWARD8",
// Minus dirt // Minus dirt
"S_MINUSDIRT1", "S_MINUSDIRT1",
@ -5041,6 +5054,26 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_PTERABYTE_SWOOPDOWN", "S_PTERABYTE_SWOOPDOWN",
"S_PTERABYTE_SWOOPUP", "S_PTERABYTE_SWOOPUP",
// Dragonbomber
"S_DRAGONBOMBER",
"S_DRAGONWING1",
"S_DRAGONWING2",
"S_DRAGONWING3",
"S_DRAGONWING4",
"S_DRAGONTAIL_LOADED",
"S_DRAGONTAIL_EMPTY",
"S_DRAGONTAIL_EMPTYLOOP",
"S_DRAGONTAIL_RELOAD",
"S_DRAGONMINE",
"S_DRAGONMINE_LAND1",
"S_DRAGONMINE_LAND2",
"S_DRAGONMINE_SLOWFLASH1",
"S_DRAGONMINE_SLOWFLASH2",
"S_DRAGONMINE_SLOWLOOP",
"S_DRAGONMINE_FASTFLASH1",
"S_DRAGONMINE_FASTFLASH2",
"S_DRAGONMINE_FASTLOOP",
// Boss Explosion // Boss Explosion
"S_BOSSEXPLODE", "S_BOSSEXPLODE",
@ -5343,6 +5376,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FSGNA", "S_FSGNA",
"S_FSGNB", "S_FSGNB",
"S_FSGNC", "S_FSGNC",
"S_FSGND",
// Black Eggman (Boss 7) // Black Eggman (Boss 7)
"S_BLACKEGG_STND", "S_BLACKEGG_STND",
@ -7139,6 +7173,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FOUR2", "S_FOUR2",
"S_FIVE2", "S_FIVE2",
"S_FLIGHTINDICATOR",
"S_LOCKON1", "S_LOCKON1",
"S_LOCKON2", "S_LOCKON2",
"S_LOCKON3", "S_LOCKON3",
@ -7322,13 +7358,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FIREFLOWER2", "S_FIREFLOWER2",
"S_FIREFLOWER3", "S_FIREFLOWER3",
"S_FIREFLOWER4", "S_FIREFLOWER4",
"S_FIREBALL1", "S_FIREBALL",
"S_FIREBALL2", "S_FIREBALLTRAIL1",
"S_FIREBALL3", "S_FIREBALLTRAIL2",
"S_FIREBALL4",
"S_FIREBALLEXP1",
"S_FIREBALLEXP2",
"S_FIREBALLEXP3",
"S_SHELL", "S_SHELL",
"S_PUMA_START1", "S_PUMA_START1",
"S_PUMA_START2", "S_PUMA_START2",
@ -7497,6 +7529,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_POPHAT_SHOOT1", "S_POPHAT_SHOOT1",
"S_POPHAT_SHOOT2", "S_POPHAT_SHOOT2",
"S_POPHAT_SHOOT3", "S_POPHAT_SHOOT3",
"S_POPHAT_SHOOT4",
"S_POPSHOT",
"S_POPSHOT_TRAIL",
"S_HIVEELEMENTAL_LOOK", "S_HIVEELEMENTAL_LOOK",
"S_HIVEELEMENTAL_PREPARE1", "S_HIVEELEMENTAL_PREPARE1",
@ -7628,8 +7663,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_DUST3", "S_DUST3",
"S_DUST4", "S_DUST4",
"S_WOODDEBRIS",
"S_ROCKSPAWN", "S_ROCKSPAWN",
"S_ROCKCRUMBLEA", "S_ROCKCRUMBLEA",
@ -7648,7 +7681,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_ROCKCRUMBLEN", "S_ROCKCRUMBLEN",
"S_ROCKCRUMBLEO", "S_ROCKCRUMBLEO",
"S_ROCKCRUMBLEP", "S_ROCKCRUMBLEP",
"S_GFZDEBRIS",
"S_BRICKDEBRIS", "S_BRICKDEBRIS",
"S_WOODDEBRIS",
#ifdef SEENAMES #ifdef SEENAMES
"S_NAMECHECK", "S_NAMECHECK",
@ -7712,6 +7747,10 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_PTERABYTESPAWNER", // Pterabyte spawner "MT_PTERABYTESPAWNER", // Pterabyte spawner
"MT_PTERABYTEWAYPOINT", // Pterabyte waypoint "MT_PTERABYTEWAYPOINT", // Pterabyte waypoint
"MT_PTERABYTE", // Pterabyte "MT_PTERABYTE", // Pterabyte
"MT_DRAGONBOMBER", // Dragonbomber
"MT_DRAGONWING", // Dragonbomber wing
"MT_DRAGONTAIL", // Dragonbomber tail segment
"MT_DRAGONMINE", // Dragonbomber mine
// Generic Boss Items // Generic Boss Items
"MT_BOSSEXPLODE", "MT_BOSSEXPLODE",
@ -8317,6 +8356,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_BLUEGOOMBA", "MT_BLUEGOOMBA",
"MT_FIREFLOWER", "MT_FIREFLOWER",
"MT_FIREBALL", "MT_FIREBALL",
"MT_FIREBALLTRAIL",
"MT_SHELL", "MT_SHELL",
"MT_PUMA", "MT_PUMA",
"MT_PUMATRAIL", "MT_PUMATRAIL",
@ -8361,6 +8401,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_PENGUINATOR", "MT_PENGUINATOR",
"MT_POPHAT", "MT_POPHAT",
"MT_POPSHOT", "MT_POPSHOT",
"MT_POPSHOT_TRAIL",
"MT_HIVEELEMENTAL", "MT_HIVEELEMENTAL",
"MT_BUMBLEBORE", "MT_BUMBLEBORE",
@ -8398,7 +8439,6 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_EXPLODE", // Robot Explosion "MT_EXPLODE", // Robot Explosion
"MT_UWEXPLODE", // Underwater Explosion "MT_UWEXPLODE", // Underwater Explosion
"MT_DUST", "MT_DUST",
"MT_WOODDEBRIS",
"MT_ROCKSPAWNER", "MT_ROCKSPAWNER",
"MT_FALLINGROCK", "MT_FALLINGROCK",
"MT_ROCKCRUMBLE1", "MT_ROCKCRUMBLE1",
@ -8417,7 +8457,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_ROCKCRUMBLE14", "MT_ROCKCRUMBLE14",
"MT_ROCKCRUMBLE15", "MT_ROCKCRUMBLE15",
"MT_ROCKCRUMBLE16", "MT_ROCKCRUMBLE16",
"MT_GFZDEBRIS",
"MT_BRICKDEBRIS", "MT_BRICKDEBRIS",
"MT_WOODDEBRIS",
#ifdef SEENAMES #ifdef SEENAMES
"MT_NAMECHECK", "MT_NAMECHECK",
@ -8570,6 +8612,7 @@ static const char *const PLAYERFLAG_LIST[] = {
/*** misc ***/ /*** misc ***/
"FORCESTRAFE", // Translate turn inputs into strafe inputs "FORCESTRAFE", // Translate turn inputs into strafe inputs
"CANCARRY", // Can carry? "CANCARRY", // Can carry?
"FINISHED",
NULL // stop loop here. NULL // stop loop here.
}; };
@ -8856,7 +8899,7 @@ static const char *const MENUTYPES_LIST[] = {
"OP_SCREENSHOTS", "OP_SCREENSHOTS",
"OP_ERASEDATA", "OP_ERASEDATA",
// Secrets // Extras
"SR_MAIN", "SR_MAIN",
"SR_PANDORA", "SR_PANDORA",
"SR_LEVELSELECT", "SR_LEVELSELECT",
@ -9012,6 +9055,8 @@ struct {
{"LF_NOZONE",LF_NOZONE}, {"LF_NOZONE",LF_NOZONE},
{"LF_SAVEGAME",LF_SAVEGAME}, {"LF_SAVEGAME",LF_SAVEGAME},
{"LF_MIXNIGHTSCOUNTDOWN",LF_MIXNIGHTSCOUNTDOWN}, {"LF_MIXNIGHTSCOUNTDOWN",LF_MIXNIGHTSCOUNTDOWN},
{"LF_NOTITLECARD",LF_NOTITLECARD},
{"LF_WARNINGTITLE",LF_WARNINGTITLE},
// And map flags // And map flags
{"LF2_HIDEINMENU",LF2_HIDEINMENU}, {"LF2_HIDEINMENU",LF2_HIDEINMENU},
{"LF2_HIDEINSTATS",LF2_HIDEINSTATS}, {"LF2_HIDEINSTATS",LF2_HIDEINSTATS},

View File

@ -98,6 +98,9 @@ void I_FinishUpdate (void)
if (cv_ticrate.value) if (cv_ticrate.value)
SCR_DisplayTicRate(); SCR_DisplayTicRate();
if (cv_showping.value && netgame && consoleplayer != serverplayer)
SCR_DisplayLocalPing();
//blast it to the screen //blast it to the screen
// this code sucks // this code sucks
//memcpy(dascreen,screens[0],screenwidth*screenheight); //memcpy(dascreen,screens[0],screenwidth*screenheight);

View File

@ -349,6 +349,8 @@ typedef struct
#define LF_NOZONE 16 ///< Don't include "ZONE" on level title #define LF_NOZONE 16 ///< Don't include "ZONE" on level title
#define LF_SAVEGAME 32 ///< Save the game upon loading this level #define LF_SAVEGAME 32 ///< Save the game upon loading this level
#define LF_MIXNIGHTSCOUNTDOWN 64 ///< Play sfx_timeup instead of music change for NiGHTS countdown #define LF_MIXNIGHTSCOUNTDOWN 64 ///< Play sfx_timeup instead of music change for NiGHTS countdown
#define LF_WARNINGTITLE 128 ///< WARNING! WARNING! WARNING! WARNING!
#define LF_NOTITLECARD 256 ///< Don't start the title card
#define LF2_HIDEINMENU 1 ///< Hide in the multiplayer menu #define LF2_HIDEINMENU 1 ///< Hide in the multiplayer menu
#define LF2_HIDEINSTATS 2 ///< Hide in the statistics screen #define LF2_HIDEINSTATS 2 ///< Hide in the statistics screen
@ -433,7 +435,6 @@ typedef struct
tic_t time; ///< Time in which the level was finished. tic_t time; ///< Time in which the level was finished.
UINT32 score; ///< Score when the level was finished. UINT32 score; ///< Score when the level was finished.
UINT16 rings; ///< Rings when the level was finished. UINT16 rings; ///< Rings when the level was finished.
boolean gotperfect; ///< Got perfect bonus?
} recorddata_t; } recorddata_t;
/** Setup for one NiGHTS map. /** Setup for one NiGHTS map.

View File

@ -856,7 +856,7 @@ void F_IntroDrawer(void)
if (rendermode != render_none) if (rendermode != render_none)
{ {
F_WipeStartScreen(); F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeColorFill(31);
F_WipeEndScreen(); F_WipeEndScreen();
F_RunWipe(99,true); F_RunWipe(99,true);
} }
@ -866,10 +866,11 @@ void F_IntroDrawer(void)
else if (intro_scenenum == 10) else if (intro_scenenum == 10)
{ {
// The only fade to white in the entire damn game. // The only fade to white in the entire damn game.
// (not true)
if (rendermode != render_none) if (rendermode != render_none)
{ {
F_WipeStartScreen(); F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0); F_WipeColorFill(0);
F_WipeEndScreen(); F_WipeEndScreen();
F_RunWipe(99,true); F_RunWipe(99,true);
} }
@ -879,7 +880,7 @@ void F_IntroDrawer(void)
if (rendermode != render_none) if (rendermode != render_none)
{ {
F_WipeStartScreen(); F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeColorFill(31);
F_WipeEndScreen(); F_WipeEndScreen();
F_RunWipe(99,true); F_RunWipe(99,true);
} }
@ -926,7 +927,7 @@ void F_IntroDrawer(void)
patch_t *radar = W_CachePatchName("RADAR", PU_CACHE); patch_t *radar = W_CachePatchName("RADAR", PU_CACHE);
F_WipeStartScreen(); F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeColorFill(31);
V_DrawScaledPatch(0, 0, 0, radar); V_DrawScaledPatch(0, 0, 0, radar);
W_UnlockCachedPatch(radar); W_UnlockCachedPatch(radar);
V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext); V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext);
@ -939,7 +940,7 @@ void F_IntroDrawer(void)
patch_t *grass = W_CachePatchName("SGRASS5", PU_CACHE); patch_t *grass = W_CachePatchName("SGRASS5", PU_CACHE);
F_WipeStartScreen(); F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeColorFill(31);
V_DrawScaledPatch(0, 0, 0, grass); V_DrawScaledPatch(0, 0, 0, grass);
W_UnlockCachedPatch(grass); W_UnlockCachedPatch(grass);
V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext); V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext);
@ -952,7 +953,7 @@ void F_IntroDrawer(void)
patch_t *confront = W_CachePatchName("CONFRONT", PU_CACHE); patch_t *confront = W_CachePatchName("CONFRONT", PU_CACHE);
F_WipeStartScreen(); F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeColorFill(31);
V_DrawSmallScaledPatch(0, 0, 0, confront); V_DrawSmallScaledPatch(0, 0, 0, confront);
W_UnlockCachedPatch(confront); W_UnlockCachedPatch(confront);
V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext); V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext);
@ -965,7 +966,7 @@ void F_IntroDrawer(void)
patch_t *sdo = W_CachePatchName("SONICDO2", PU_CACHE); patch_t *sdo = W_CachePatchName("SONICDO2", PU_CACHE);
F_WipeStartScreen(); F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); F_WipeColorFill(31);
V_DrawSmallScaledPatch(0, 0, 0, sdo); V_DrawSmallScaledPatch(0, 0, 0, sdo);
W_UnlockCachedPatch(sdo); W_UnlockCachedPatch(sdo);
V_DrawString(224, 8, V_ALLOWLOWERCASE, cutscene_disptext); V_DrawString(224, 8, V_ALLOWLOWERCASE, cutscene_disptext);
@ -1385,13 +1386,16 @@ boolean F_CreditResponder(event_t *event)
void F_StartGameEvaluation(void) void F_StartGameEvaluation(void)
{ {
// Credits option in secrets menu // Credits option in extras menu
if (cursaveslot == -1) if (cursaveslot == -1)
{ {
S_FadeOutStopMusic(2*MUSICRATE);
F_StartGameEnd(); F_StartGameEnd();
return; return;
} }
S_FadeOutStopMusic(5*MUSICRATE);
G_SetGamestate(GS_EVALUATION); G_SetGamestate(GS_EVALUATION);
// Just in case they're open ... somehow // Just in case they're open ... somehow
@ -1537,9 +1541,9 @@ void F_GameEvaluationDrawer(void)
} }
} }
else if (netgame) else if (netgame)
V_DrawString(8, 96, V_YELLOWMAP, "Prizes only\nawarded in\nsingle player!"); V_DrawString(8, 96, V_YELLOWMAP, "Multiplayer games\ncan't unlock\nextras!");
else else
V_DrawString(8, 96, V_YELLOWMAP, "Prizes not\nawarded in\nmodified games!"); V_DrawString(8, 96, V_YELLOWMAP, "Modified games\ncan't unlock\nextras!");
} }
#endif #endif
} }
@ -1586,7 +1590,7 @@ void F_GameEvaluationTicker(void)
{ {
HU_SetCEchoFlags(V_YELLOWMAP|V_RETURN8); HU_SetCEchoFlags(V_YELLOWMAP|V_RETURN8);
HU_SetCEchoDuration(6); HU_SetCEchoDuration(6);
HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Prizes only awarded in singleplayer!"); HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Multiplayer games can't unlock extras!");
S_StartSound(NULL, sfx_s3k68); S_StartSound(NULL, sfx_s3k68);
} }
else if (!modifiedgame || savemoddata) else if (!modifiedgame || savemoddata)
@ -1608,7 +1612,7 @@ void F_GameEvaluationTicker(void)
{ {
HU_SetCEchoFlags(V_YELLOWMAP|V_RETURN8); HU_SetCEchoFlags(V_YELLOWMAP|V_RETURN8);
HU_SetCEchoDuration(6); HU_SetCEchoDuration(6);
HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Prizes not awarded in modified games!"); HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Modified games can't unlock extras!");
S_StartSound(NULL, sfx_s3k68); S_StartSound(NULL, sfx_s3k68);
} }
} }
@ -1621,6 +1625,7 @@ void F_GameEvaluationTicker(void)
// ========== // ==========
#define INFLECTIONPOINT (6*TICRATE) #define INFLECTIONPOINT (6*TICRATE)
#define STOPPINGPOINT (14*TICRATE)
#define SPARKLLOOPTIME 15 // must be odd #define SPARKLLOOPTIME 15 // must be odd
void F_StartEnding(void) void F_StartEnding(void)
@ -1638,7 +1643,7 @@ void F_StartEnding(void)
gameaction = ga_nothing; gameaction = ga_nothing;
paused = false; paused = false;
CON_ToggleOff(); CON_ToggleOff();
S_StopMusic(); // todo: placeholder S_StopMusic();
S_StopSounds(); S_StopSounds();
finalecount = -10; // what? this totally isn't a hack. why are you asking? finalecount = -10; // what? this totally isn't a hack. why are you asking?
@ -1679,7 +1684,7 @@ void F_StartEnding(void)
UINT8 skinnum = players[consoleplayer].skin; UINT8 skinnum = players[consoleplayer].skin;
spritedef_t *sprdef; spritedef_t *sprdef;
spriteframe_t *sprframe; spriteframe_t *sprframe;
if (skins[skinnum].sprites[SPR2_XTRA].numframes >= (XTRA_ENDING+2)+1) if (skins[skinnum].sprites[SPR2_XTRA].numframes > (XTRA_ENDING+2))
{ {
sprdef = &skins[skinnum].sprites[SPR2_XTRA]; sprdef = &skins[skinnum].sprites[SPR2_XTRA];
// character head, skin specific // character head, skin specific
@ -1712,13 +1717,16 @@ void F_StartEnding(void)
void F_EndingTicker(void) void F_EndingTicker(void)
{ {
if (++finalecount > INFLECTIONPOINT*2) if (++finalecount > STOPPINGPOINT)
{ {
F_StartCredits(); F_StartCredits();
wipetypepre = INT16_MAX; wipetypepre = INT16_MAX;
return; return;
} }
if (finalecount == -8)
S_ChangeMusicInternal((goodending ? "_endg" : "_endb"), false);
if (goodending && finalecount == INFLECTIONPOINT) // time to swap some assets if (goodending && finalecount == INFLECTIONPOINT) // time to swap some assets
{ {
endegrk[0] = W_CachePatchName("ENDEGRK2", PU_LEVEL); endegrk[0] = W_CachePatchName("ENDEGRK2", PU_LEVEL);
@ -2105,26 +2113,26 @@ void F_EndingDrawer(void)
if (finalecount < 10) if (finalecount < 10)
trans = (10-finalecount)/2; trans = (10-finalecount)/2;
else if (finalecount > (2*INFLECTIONPOINT) - 20) else if (finalecount > STOPPINGPOINT - 20)
{ {
trans = 10 + (finalecount/2) - INFLECTIONPOINT; trans = 10 + (finalecount - STOPPINGPOINT)/2;
donttouch = true; donttouch = true;
} }
if (trans != 10) if (trans < 10)
{ {
//colset(linkmap, 164, 165, 169); -- the ideal purple colour to represent a clicked in-game link, but not worth it just for a soundtest-controlled secret //colset(linkmap, 164, 165, 169); -- the ideal purple colour to represent a clicked in-game link, but not worth it just for a soundtest-controlled secret
V_DrawCenteredString(BASEVIDWIDTH/2, 8, V_ALLOWLOWERCASE|(trans<<V_ALPHASHIFT), str); V_DrawCenteredString(BASEVIDWIDTH/2, 8, V_ALLOWLOWERCASE|(trans<<V_ALPHASHIFT), str);
V_DrawCharacter(32, BASEVIDHEIGHT-16, '>'|(trans<<V_ALPHASHIFT), false); V_DrawCharacter(32, BASEVIDHEIGHT-16, '>'|(trans<<V_ALPHASHIFT), false);
V_DrawString(40, ((finalecount == (2*INFLECTIONPOINT)-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((timesBeaten || finalecount >= (2*INFLECTIONPOINT)-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>"); V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<<V_ALPHASHIFT), " [S] ===>");
} }
if (finalecount > (2*INFLECTIONPOINT)-(20+(2*TICRATE))) if (finalecount > STOPPINGPOINT-(20+(2*TICRATE)))
{ {
INT32 trans2 = abs((5*FINECOSINE((FixedAngle((finalecount*5)<<FRACBITS)>>ANGLETOFINESHIFT & FINEMASK)))>>FRACBITS)+2; INT32 trans2 = abs((5*FINECOSINE((FixedAngle((finalecount*5)<<FRACBITS)>>ANGLETOFINESHIFT & FINEMASK)))>>FRACBITS)+2;
if (!donttouch) if (!donttouch)
{ {
trans = 10 + ((2*INFLECTIONPOINT)-(20+(2*TICRATE))) - finalecount; trans = 10 + (STOPPINGPOINT-(20+(2*TICRATE))) - finalecount;
if (trans > trans2) if (trans > trans2)
trans2 = trans; trans2 = trans;
} }
@ -2148,7 +2156,6 @@ void F_StartGameEnd(void)
gameaction = ga_nothing; gameaction = ga_nothing;
paused = false; paused = false;
CON_ToggleOff(); CON_ToggleOff();
S_StopMusic();
S_StopSounds(); S_StopSounds();
// In case menus are still up?!! // In case menus are still up?!!
@ -2582,9 +2589,7 @@ void F_TitleScreenDrawer(void)
// Draw that sky! // Draw that sky!
if (curbgcolor >= 0) if (curbgcolor >= 0)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, curbgcolor); V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, curbgcolor);
else if (titlemapinaction && curbghide && ! hidetitlemap) else if (!curbghide || !titlemapinaction || gamestate == GS_WAITINGPLAYERS)
D_Render();
else
F_SkyScroll(curbgxspeed, curbgyspeed, curbgname); F_SkyScroll(curbgxspeed, curbgyspeed, curbgname);
// Don't draw outside of the title screen, or if the patch isn't there. // Don't draw outside of the title screen, or if the patch isn't there.
@ -3344,6 +3349,10 @@ void F_TitleScreenTicker(boolean run)
if (run) if (run)
finalecount++; finalecount++;
// don't trigger if doing anything besides idling on title
if (gameaction != ga_nothing || gamestate != GS_TITLESCREEN)
return;
// Execute the titlemap camera settings // Execute the titlemap camera settings
if (titlemapinaction) if (titlemapinaction)
{ {
@ -3390,10 +3399,6 @@ void F_TitleScreenTicker(boolean run)
} }
} }
// don't trigger if doing anything besides idling on title
if (gameaction != ga_nothing || gamestate != GS_TITLESCREEN)
return;
// no demos to play? or, are they disabled? // no demos to play? or, are they disabled?
if (!cv_rollingdemos.value || !numDemos) if (!cv_rollingdemos.value || !numDemos)
return; return;
@ -3546,7 +3551,7 @@ void F_ContinueDrawer(void)
if (timetonext >= (11*TICRATE)+10) if (timetonext >= (11*TICRATE)+10)
return; return;
V_DrawLevelTitle(x - (V_LevelNameWidth("CONTINUE")>>1), 16, 0, "CONTINUE"); V_DrawLevelTitle(x - (V_LevelNameWidth("Continue?")>>1), 16, 0, "Continue?");
// Two stars... // Two stars...
patch = W_CachePatchName("CONTSTAR", PU_CACHE); patch = W_CachePatchName("CONTSTAR", PU_CACHE);

View File

@ -141,11 +141,42 @@ void F_MenuPresTicker(boolean run);
#define FORCEWIPEOFF -2 #define FORCEWIPEOFF -2
extern boolean WipeInAction; extern boolean WipeInAction;
extern boolean WipeStageTitle;
typedef enum
{
WIPESTYLE_NORMAL,
WIPESTYLE_LEVEL
} wipestyle_t;
extern wipestyle_t wipestyle;
typedef enum
{
WSF_FADEOUT = 1,
WSF_FADEIN = 1<<1,
WSF_TOWHITE = 1<<2,
WSF_CROSSFADE = 1<<3,
} wipestyleflags_t;
extern wipestyleflags_t wipestyleflags;
#define FADECOLORMAPDIV 8
#define FADECOLORMAPROWS (256/FADECOLORMAPDIV)
#define FADEREDFACTOR 15
#define FADEGREENFACTOR 15
#define FADEBLUEFACTOR 10
extern INT32 lastwipetic; extern INT32 lastwipetic;
// Don't know where else to place this constant
// But this file seems appropriate
#define PRELEVELTIME 24 // frames in tics
void F_WipeStartScreen(void); void F_WipeStartScreen(void);
void F_WipeEndScreen(void); void F_WipeEndScreen(void);
void F_RunWipe(UINT8 wipetype, boolean drawMenu); void F_RunWipe(UINT8 wipetype, boolean drawMenu);
void F_WipeStageTitle(void);
#define F_WipeColorFill(c) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, c)
tic_t F_GetWipeLength(UINT8 wipetype); tic_t F_GetWipeLength(UINT8 wipetype);
boolean F_WipeExists(UINT8 wipetype); boolean F_WipeExists(UINT8 wipetype);

View File

@ -18,6 +18,8 @@
#include "r_draw.h" // transtable #include "r_draw.h" // transtable
#include "p_pspr.h" // tr_transxxx #include "p_pspr.h" // tr_transxxx
#include "p_local.h"
#include "st_stuff.h"
#include "w_wad.h" #include "w_wad.h"
#include "z_zone.h" #include "z_zone.h"
@ -25,8 +27,15 @@
#include "m_menu.h" #include "m_menu.h"
#include "console.h" #include "console.h"
#include "d_main.h" #include "d_main.h"
#include "g_game.h"
#include "m_misc.h" // movie mode #include "m_misc.h" // movie mode
#include "doomstat.h"
#ifdef HAVE_BLUA
#include "lua_hud.h" // level title
#endif
#ifdef HWRENDER #ifdef HWRENDER
#include "hardware/hw_main.h" #include "hardware/hw_main.h"
#endif #endif
@ -82,8 +91,12 @@ UINT8 wipedefs[NUMWIPEDEFS] = {
//-------------------------------------------------------------------------- //--------------------------------------------------------------------------
boolean WipeInAction = false; boolean WipeInAction = false;
boolean WipeStageTitle = false;
INT32 lastwipetic = 0; INT32 lastwipetic = 0;
wipestyle_t wipestyle = WIPESTYLE_NORMAL;
wipestyleflags_t wipestyleflags = WSF_CROSSFADE;
#ifndef NOWIPE #ifndef NOWIPE
static UINT8 *wipe_scr_start; //screen 3 static UINT8 *wipe_scr_start; //screen 3
static UINT8 *wipe_scr_end; //screen 4 static UINT8 *wipe_scr_end; //screen 4
@ -148,7 +161,10 @@ static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) {
{ {
// Determine pixel to use from fademask // Determine pixel to use from fademask
pcolor = &pMasterPalette[*lump++]; pcolor = &pMasterPalette[*lump++];
*mask++ = FixedDiv((pcolor->s.red+1)<<FRACBITS, paldiv)>>FRACBITS; if (wipestyle == WIPESTYLE_LEVEL)
*mask++ = pcolor->s.red / FADECOLORMAPDIV;
else
*mask++ = FixedDiv((pcolor->s.red+1)<<FRACBITS, paldiv)>>FRACBITS;
} }
fm.xscale = FixedDiv(vid.width<<FRACBITS, fm.width<<FRACBITS); fm.xscale = FixedDiv(vid.width<<FRACBITS, fm.width<<FRACBITS);
@ -169,6 +185,21 @@ static fademask_t *F_GetFadeMask(UINT8 masknum, UINT8 scrnnum) {
return NULL; return NULL;
} }
/** Draw the stage title.
*/
void F_WipeStageTitle(void)
{
// draw level title
if ((WipeStageTitle && st_overlay)
&& (wipestyle == WIPESTYLE_LEVEL)
&& !(mapheaderinfo[gamemap-1]->levelflags & LF_NOTITLECARD)
&& *mapheaderinfo[gamemap-1]->lvlttl != '\0')
{
ST_runTitleCard();
ST_drawWipeTitleCard();
}
}
/** Wipe ticker /** Wipe ticker
* *
* \param fademask pixels to change * \param fademask pixels to change
@ -251,7 +282,7 @@ static void F_DoWipe(fademask_t *fademask)
relativepos += vid.width; relativepos += vid.width;
} }
} }
else if (*mask == 10) else if (*mask >= ((wipestyle == WIPESTYLE_LEVEL) ? FADECOLORMAPROWS : 10))
{ {
// shortcut - memcpy target to work // shortcut - memcpy target to work
while (draw_linestogo--) while (draw_linestogo--)
@ -262,8 +293,25 @@ static void F_DoWipe(fademask_t *fademask)
} }
else else
{ {
// pointer to transtable that this mask would use if (wipestyle == WIPESTYLE_LEVEL)
transtbl = transtables + ((9 - *mask)<<FF_TRANSSHIFT); {
int nmask;
UINT8 *fade = fadecolormap;
if (wipestyleflags & WSF_TOWHITE)
fade = fadecolormap + (FADECOLORMAPROWS * 256);
nmask = *mask;
if (wipestyleflags & WSF_FADEIN)
nmask = (FADECOLORMAPROWS-1) - nmask;
transtbl = fade + (nmask * 256);
}
else
{
// pointer to transtable that this mask would use
transtbl = transtables + ((9 - *mask)<<FF_TRANSSHIFT);
}
// DRAWING LOOP // DRAWING LOOP
while (draw_linestogo--) while (draw_linestogo--)
@ -273,8 +321,16 @@ static void F_DoWipe(fademask_t *fademask)
e = e_base + relativepos; e = e_base + relativepos;
draw_rowstogo = draw_rowend - draw_rowstart; draw_rowstogo = draw_rowend - draw_rowstart;
while (draw_rowstogo--) if (wipestyle == WIPESTYLE_LEVEL)
*w++ = transtbl[ ( *e++ << 8 ) + *s++ ]; {
while (draw_rowstogo--)
*w++ = transtbl[*e++];
}
else
{
while (draw_rowstogo--)
*w++ = transtbl[ ( *e++ << 8 ) + *s++ ];
}
relativepos += vid.width; relativepos += vid.width;
} }
@ -346,6 +402,15 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
WipeInAction = true; WipeInAction = true;
wipe_scr = screens[0]; wipe_scr = screens[0];
// don't know where else to put this.
// this any good?
if ((gamestate == GS_LEVEL || gamestate == GS_TITLESCREEN)
&& (wipestyleflags & (WSF_FADEIN|WSF_FADEOUT)) // only if one of those wipestyleflags are actually set
&& !(wipestyleflags & WSF_CROSSFADE)) // and if not crossfading
wipestyle = WIPESTYLE_LEVEL;
else
wipestyle = WIPESTYLE_NORMAL;
// lastwipetic should either be 0 or the tic we last wiped // lastwipetic should either be 0 or the tic we last wiped
// on for fade-to-black // on for fade-to-black
for (;;) for (;;)
@ -362,10 +427,20 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
#ifdef HWRENDER #ifdef HWRENDER
if (rendermode == render_opengl) if (rendermode == render_opengl)
HWR_DoWipe(wipetype, wipeframe-1); // send in the wipe type and wipeframe because we need to cache the graphic {
// send in the wipe type and wipe frame because we need to cache the graphic
if (wipestyle == WIPESTYLE_LEVEL)
HWR_DoTintedWipe(wipetype, wipeframe-1);
else
HWR_DoWipe(wipetype, wipeframe-1);
}
else else
#endif #endif
F_DoWipe(fmask); F_DoWipe(fmask);
if (wipestyle == WIPESTYLE_LEVEL)
F_WipeStageTitle();
I_OsPolling(); I_OsPolling();
I_UpdateNoBlit(); I_UpdateNoBlit();
@ -377,7 +452,9 @@ void F_RunWipe(UINT8 wipetype, boolean drawMenu)
if (moviemode) if (moviemode)
M_SaveFrame(); M_SaveFrame();
} }
WipeInAction = false; WipeInAction = false;
WipeStageTitle = false;
#endif #endif
} }
@ -408,6 +485,8 @@ tic_t F_GetWipeLength(UINT8 wipetype)
#endif #endif
} }
/** Does the specified wipe exist?
*/
boolean F_WipeExists(UINT8 wipetype) boolean F_WipeExists(UINT8 wipetype)
{ {
#ifdef NOWIPE #ifdef NOWIPE

View File

@ -47,6 +47,10 @@
#include "m_cond.h" // condition sets #include "m_cond.h" // condition sets
#include "md5.h" // demo checksums #include "md5.h" // demo checksums
#ifdef HAVE_BLUA
#include "lua_hud.h"
#endif
gameaction_t gameaction; gameaction_t gameaction;
gamestate_t gamestate = GS_NULL; gamestate_t gamestate = GS_NULL;
UINT8 ultimatemode = false; UINT8 ultimatemode = false;
@ -168,6 +172,7 @@ mapheader_t* mapheaderinfo[NUMMAPS] = {NULL};
static boolean exitgame = false; static boolean exitgame = false;
static boolean retrying = false; static boolean retrying = false;
static boolean retryingmodeattack = false;
UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage. UINT8 stagefailed; // Used for GEMS BONUS? Also to see if you beat the stage.
@ -536,11 +541,99 @@ void G_AddTempNightsRecords(UINT32 pscore, tic_t ptime, UINT8 mare)
ntemprecords.nummares = mare; ntemprecords.nummares = mare;
} }
//
// G_UpdateRecordReplays
//
// Update replay files/data, etc. for Record Attack
// See G_SetNightsRecords for NiGHTS Attack.
//
static void G_UpdateRecordReplays(void)
{
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
char *gpath;
char lastdemo[256], bestdemo[256];
UINT8 earnedEmblems;
// Record new best time
if (!mainrecords[gamemap-1])
G_AllocMainRecordData(gamemap-1);
if (players[consoleplayer].score > mainrecords[gamemap-1]->score)
mainrecords[gamemap-1]->score = players[consoleplayer].score;
if ((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time))
mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
if ((UINT16)(players[consoleplayer].rings) > mainrecords[gamemap-1]->rings)
mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
// Save demo!
bestdemo[255] = '\0';
lastdemo[255] = '\0';
G_SetDemoTime(players[consoleplayer].realtime, players[consoleplayer].score, (UINT16)(players[consoleplayer].rings));
G_CheckDemoStatus();
I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (FIL_FileExists(lastdemo))
{
UINT8 *buf;
size_t len = FIL_ReadFile(lastdemo, &buf);
snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
{ // Better time, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
}
snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
{ // Better score, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
}
snprintf(bestdemo, 255, "%s-%s-rings-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<2)))
{ // Better rings, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW MOST RINGS!"), M_GetText("Saved replay as"), bestdemo);
}
//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
Z_Free(buf);
}
free(gpath);
// Check emblems when level data is updated
if ((earnedEmblems = M_CheckLevelEmblems()))
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
// Update timeattack menu's replay availability.
Nextmap_OnChange();
}
void G_SetNightsRecords(void) void G_SetNightsRecords(void)
{ {
INT32 i; INT32 i;
UINT32 totalscore = 0; UINT32 totalscore = 0;
tic_t totaltime = 0; tic_t totaltime = 0;
UINT8 earnedEmblems;
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
char *gpath; char *gpath;
@ -640,6 +733,9 @@ void G_SetNightsRecords(void)
} }
free(gpath); free(gpath);
if ((earnedEmblems = M_CheckLevelEmblems()))
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for NiGHTS records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
// If the mare count changed, this will update the score display // If the mare count changed, this will update the score display
Nextmap_OnChange(); Nextmap_OnChange();
} }
@ -923,11 +1019,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
movebkey = PLAYER1INPUTDOWN(gc_backward); movebkey = PLAYER1INPUTDOWN(gc_backward);
mouseaiming = (PLAYER1INPUTDOWN(gc_mouseaiming)) ^ mouseaiming = (PLAYER1INPUTDOWN(gc_mouseaiming)) ^
(cv_chasecam.value ? cv_chasefreelook.value : cv_alwaysfreelook.value); ((cv_chasecam.value && !player->spectator) ? cv_chasefreelook.value : cv_alwaysfreelook.value);
analogjoystickmove = cv_usejoystick.value && !Joystick.bGamepadStyle; analogjoystickmove = cv_usejoystick.value && !Joystick.bGamepadStyle;
gamepadjoystickmove = cv_usejoystick.value && Joystick.bGamepadStyle; gamepadjoystickmove = cv_usejoystick.value && Joystick.bGamepadStyle;
thisjoyaiming = (cv_chasecam.value) ? cv_chasefreelook.value : cv_alwaysfreelook.value; thisjoyaiming = (cv_chasecam.value && !player->spectator) ? cv_chasefreelook.value : cv_alwaysfreelook.value;
// Reset the vertical look if we're no longer joyaiming // Reset the vertical look if we're no longer joyaiming
if (!thisjoyaiming && joyaiming) if (!thisjoyaiming && joyaiming)
@ -1252,11 +1348,11 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics)
movebkey = PLAYER2INPUTDOWN(gc_backward); movebkey = PLAYER2INPUTDOWN(gc_backward);
mouseaiming = (PLAYER2INPUTDOWN(gc_mouseaiming)) ^ mouseaiming = (PLAYER2INPUTDOWN(gc_mouseaiming)) ^
(cv_chasecam2.value ? cv_chasefreelook2.value : cv_alwaysfreelook2.value); ((cv_chasecam2.value && !player->spectator) ? cv_chasefreelook2.value : cv_alwaysfreelook2.value);
analogjoystickmove = cv_usejoystick2.value && !Joystick2.bGamepadStyle; analogjoystickmove = cv_usejoystick2.value && !Joystick2.bGamepadStyle;
gamepadjoystickmove = cv_usejoystick2.value && Joystick2.bGamepadStyle; gamepadjoystickmove = cv_usejoystick2.value && Joystick2.bGamepadStyle;
thisjoyaiming = (cv_chasecam2.value) ? cv_chasefreelook2.value : cv_alwaysfreelook2.value; thisjoyaiming = (cv_chasecam2.value && !player->spectator) ? cv_chasefreelook2.value : cv_alwaysfreelook2.value;
// Reset the vertical look if we're no longer joyaiming // Reset the vertical look if we're no longer joyaiming
if (!thisjoyaiming && joyaiming) if (!thisjoyaiming && joyaiming)
@ -1703,6 +1799,58 @@ void G_DoLoadLevel(boolean resetplayer)
CON_ClearHUD(); CON_ClearHUD();
} }
//
// Start the title card.
//
void G_StartTitleCard(void)
{
wipestyleflags |= WSF_FADEIN;
wipestyleflags &= ~WSF_FADEOUT;
// The title card has been disabled for this map.
// Oh well.
if (mapheaderinfo[gamemap-1]->levelflags & LF_NOTITLECARD)
{
WipeStageTitle = false;
return;
}
// clear the hud
CON_ClearHUD();
// prepare status bar
ST_startTitleCard();
// start the title card
WipeStageTitle = (!titlemapinaction);
}
//
// Run the title card before fading in to the level.
//
void G_PreLevelTitleCard(tic_t ticker, boolean update)
{
tic_t starttime = I_GetTime();
tic_t endtime = starttime + (PRELEVELTIME*NEWTICRATERATIO);
tic_t nowtime = starttime;
tic_t lasttime = starttime;
while (nowtime < endtime)
{
// draw loop
while (!((nowtime = I_GetTime()) - lasttime))
I_Sleep();
lasttime = nowtime;
ST_runTitleCard();
ST_preLevelTitleCardDrawer(ticker, update);
if (moviemode)
M_SaveFrame();
if (takescreenshot) // Only take screenshots after drawing.
M_DoScreenShot();
}
}
INT32 pausedelay = 0; INT32 pausedelay = 0;
boolean pausebreakkey = false; boolean pausebreakkey = false;
static INT32 camtoggledelay, camtoggledelay2 = 0; static INT32 camtoggledelay, camtoggledelay2 = 0;
@ -1868,7 +2016,7 @@ boolean G_Responder(event_t *ev)
pausedelay = 1+(NEWTICRATE/2); pausedelay = 1+(NEWTICRATE/2);
else if (++pausedelay > 1+(NEWTICRATE/2)+(NEWTICRATE/3)) else if (++pausedelay > 1+(NEWTICRATE/2)+(NEWTICRATE/3))
{ {
G_SetRetryFlag(); G_SetModeAttackRetryFlag();
return true; return true;
} }
pausedelay++; // counteract subsequent subtraction this frame pausedelay++; // counteract subsequent subtraction this frame
@ -1992,7 +2140,7 @@ void G_Ticker(boolean run)
if (titledemo) if (titledemo)
F_TitleDemoTicker(); F_TitleDemoTicker();
P_Ticker(run); // tic the game P_Ticker(run); // tic the game
ST_Ticker(); ST_Ticker(run);
F_TextPromptTicker(); F_TextPromptTicker();
AM_Ticker(); AM_Ticker();
HU_Ticker(); HU_Ticker();
@ -2175,6 +2323,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
outofcoop = players[player].outofcoop; outofcoop = players[player].outofcoop;
pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER)); pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER));
if (!betweenmaps)
pflags |= (players[player].pflags & PF_FINISHED);
// As long as we're not in multiplayer, carry over cheatcodes from map to map // As long as we're not in multiplayer, carry over cheatcodes from map to map
if (!(netgame || multiplayer)) if (!(netgame || multiplayer))
pflags |= (players[player].pflags & (PF_GODMODE|PF_NOCLIP|PF_INVIS)); pflags |= (players[player].pflags & (PF_GODMODE|PF_NOCLIP|PF_INVIS));
@ -2709,6 +2860,7 @@ void G_DoReborn(INT32 playernum)
// Do a wipe // Do a wipe
wipegamestate = -1; wipegamestate = -1;
wipestyleflags = WSF_CROSSFADE;
if (camera.chase) if (camera.chase)
P_ResetCamera(&players[displayplayer], &camera); P_ResetCamera(&players[displayplayer], &camera);
@ -2829,10 +2981,35 @@ void G_AddPlayer(INT32 playernum)
if (G_GametypeUsesLives() || ((netgame || multiplayer) && gametype == GT_COOP)) if (G_GametypeUsesLives() || ((netgame || multiplayer) && gametype == GT_COOP))
p->lives = cv_startinglives.value; p->lives = cv_startinglives.value;
if (countplayers && !notexiting) if ((countplayers && !notexiting) || G_IsSpecialStage(gamemap))
P_DoPlayerExit(p); P_DoPlayerExit(p);
} }
boolean G_EnoughPlayersFinished(void)
{
UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value);
INT32 total = 0;
INT32 exiting = 0;
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || players[i].bot)
continue;
if (players[i].lives <= 0)
continue;
total++;
if ((players[i].pflags & PF_FINISHED) || players[i].exiting)
exiting++;
}
if (exiting)
return exiting * 4 / total >= numneeded;
else
return false;
}
void G_ExitLevel(void) void G_ExitLevel(void)
{ {
if (gamestate == GS_LEVEL) if (gamestate == GS_LEVEL)
@ -2925,7 +3102,7 @@ boolean G_GametypeUsesLives(void)
// Coop, Competitive // Coop, Competitive
if ((gametype == GT_COOP || gametype == GT_COMPETITION) if ((gametype == GT_COOP || gametype == GT_COMPETITION)
&& !(modeattacking || metalrecording) // No lives in Time Attack && !(modeattacking || metalrecording) // No lives in Time Attack
//&& !G_IsSpecialStage(gamemap) && !G_IsSpecialStage(gamemap)
&& !(maptol & TOL_NIGHTS)) // No lives in NiGHTS && !(maptol & TOL_NIGHTS)) // No lives in NiGHTS
return true; return true;
return false; return false;
@ -3038,12 +3215,51 @@ static INT16 RandMap(INT16 tolflags, INT16 pprevmap)
return ix; return ix;
} }
//
// G_UpdateVisited
//
static void G_UpdateVisited(void)
{
boolean spec = G_IsSpecialStage(gamemap);
// Update visitation flags?
if ((!modifiedgame || savemoddata) // Not modified
&& !multiplayer && !demoplayback && (gametype == GT_COOP) // SP/RA/NiGHTS mode
&& !(spec && stagefailed)) // Not failed the special stage
{
UINT8 earnedEmblems;
// Update visitation flags
mapvisited[gamemap-1] |= MV_BEATEN;
// eh, what the hell
if (ultimatemode)
mapvisited[gamemap-1] |= MV_ULTIMATE;
// may seem incorrect but IS possible in what the main game uses as special stages, and nummaprings will be -1 in NiGHTS
if (nummaprings > 0 && players[consoleplayer].rings >= nummaprings)
mapvisited[gamemap-1] |= MV_PERFECT;
if (!spec)
{
// not available to special stages because they can only really be done in one order in an unmodified game, so impossible for first six and trivial for seventh
if (ALL7EMERALDS(emeralds))
mapvisited[gamemap-1] |= MV_ALLEMERALDS;
}
if (modeattacking == ATTACKING_RECORD)
G_UpdateRecordReplays();
else if (modeattacking == ATTACKING_NIGHTS)
G_SetNightsRecords();
if ((earnedEmblems = M_CompletionEmblems()))
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
}
}
// //
// G_DoCompleted // G_DoCompleted
// //
static void G_DoCompleted(void) static void G_DoCompleted(void)
{ {
INT32 i; INT32 i;
boolean spec = G_IsSpecialStage(gamemap);
tokenlist = 0; // Reset the list tokenlist = 0; // Reset the list
@ -3076,14 +3292,14 @@ static void G_DoCompleted(void)
nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1); nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1);
// Remember last map for when you come out of the special stage. // Remember last map for when you come out of the special stage.
if (!G_IsSpecialStage(gamemap)) if (!spec)
lastmap = nextmap; lastmap = nextmap;
// If nextmap is actually going to get used, make sure it points to // If nextmap is actually going to get used, make sure it points to
// a map of the proper gametype -- skip levels that don't support // a map of the proper gametype -- skip levels that don't support
// the current gametype. (Helps avoid playing boss levels in Race, // the current gametype. (Helps avoid playing boss levels in Race,
// for instance). // for instance).
if (!token && !G_IsSpecialStage(gamemap) if (!token && !spec
&& (nextmap >= 0 && nextmap < NUMMAPS)) && (nextmap >= 0 && nextmap < NUMMAPS))
{ {
register INT16 cm = nextmap; register INT16 cm = nextmap;
@ -3147,7 +3363,7 @@ static void G_DoCompleted(void)
gottoken = false; gottoken = false;
} }
if (G_IsSpecialStage(gamemap) && !gottoken) if (spec && !gottoken)
nextmap = lastmap; // Exiting from a special stage? Go back to the game. Tails 08-11-2001 nextmap = lastmap; // Exiting from a special stage? Go back to the game. Tails 08-11-2001
automapactive = false; automapactive = false;
@ -3166,17 +3382,29 @@ static void G_DoCompleted(void)
if (nextmap < NUMMAPS && !mapheaderinfo[nextmap]) if (nextmap < NUMMAPS && !mapheaderinfo[nextmap])
P_AllocMapHeader(nextmap); P_AllocMapHeader(nextmap);
if (skipstats && !modeattacking) // Don't skip stats if we're in record attack if ((skipstats && !modeattacking) || (spec && modeattacking && stagefailed))
{
G_UpdateVisited();
G_AfterIntermission(); G_AfterIntermission();
}
else else
{ {
G_SetGamestate(GS_INTERMISSION); G_SetGamestate(GS_INTERMISSION);
Y_StartIntermission(); Y_StartIntermission();
G_UpdateVisited();
} }
} }
void G_AfterIntermission(void) void G_AfterIntermission(void)
{ {
Y_CleanupScreenBuffer();
if (modeattacking)
{
M_EndModeAttackRun();
return;
}
HU_ClearCEcho(); HU_ClearCEcho();
if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene. if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene.
@ -3343,7 +3571,6 @@ void G_LoadGameData(void)
UINT32 recscore; UINT32 recscore;
tic_t rectime; tic_t rectime;
UINT16 recrings; UINT16 recrings;
boolean gotperf;
UINT8 recmares; UINT8 recmares;
INT32 curmare; INT32 curmare;
@ -3441,7 +3668,7 @@ void G_LoadGameData(void)
recscore = READUINT32(save_p); recscore = READUINT32(save_p);
rectime = (tic_t)READUINT32(save_p); rectime = (tic_t)READUINT32(save_p);
recrings = READUINT16(save_p); recrings = READUINT16(save_p);
gotperf = (boolean)READUINT8(save_p); save_p++; // compat
if (recrings > 10000 || recscore > MAXSCORE) if (recrings > 10000 || recscore > MAXSCORE)
goto datacorrupt; goto datacorrupt;
@ -3453,9 +3680,6 @@ void G_LoadGameData(void)
mainrecords[i]->time = rectime; mainrecords[i]->time = rectime;
mainrecords[i]->rings = recrings; mainrecords[i]->rings = recrings;
} }
if (gotperf)
mainrecords[i]->gotperfect = gotperf;
} }
// Nights records // Nights records
@ -3587,15 +3811,14 @@ void G_SaveGameData(void)
WRITEUINT32(save_p, mainrecords[i]->score); WRITEUINT32(save_p, mainrecords[i]->score);
WRITEUINT32(save_p, mainrecords[i]->time); WRITEUINT32(save_p, mainrecords[i]->time);
WRITEUINT16(save_p, mainrecords[i]->rings); WRITEUINT16(save_p, mainrecords[i]->rings);
WRITEUINT8(save_p, mainrecords[i]->gotperfect);
} }
else else
{ {
WRITEUINT32(save_p, 0); WRITEUINT32(save_p, 0);
WRITEUINT32(save_p, 0); WRITEUINT32(save_p, 0);
WRITEUINT16(save_p, 0); WRITEUINT16(save_p, 0);
WRITEUINT8(save_p, 0);
} }
WRITEUINT8(save_p, 0); // compat
} }
// NiGHTS records // NiGHTS records
@ -4031,7 +4254,7 @@ char *G_BuildMapTitle(INT32 mapnum)
len += strlen(mapheaderinfo[mapnum-1]->lvlttl); len += strlen(mapheaderinfo[mapnum-1]->lvlttl);
if (!(mapheaderinfo[mapnum-1]->levelflags & LF_NOZONE)) if (!(mapheaderinfo[mapnum-1]->levelflags & LF_NOZONE))
{ {
zonetext = M_GetText("ZONE"); zonetext = M_GetText("Zone");
len += strlen(zonetext) + 1; // ' ' + zonetext len += strlen(zonetext) + 1; // ' ' + zonetext
} }
if (actnum > 0) if (actnum > 0)
@ -4642,6 +4865,12 @@ void G_WriteGhostTic(mobj_t *ghost)
oldghost.flags2 |= MF2_AMBUSH; oldghost.flags2 |= MF2_AMBUSH;
} }
if (ghost->player->followmobj->scale != ghost->scale)
{
followtic |= FZT_SCALE;
WRITEFIXED(demo_p,ghost->player->followmobj->scale);
}
temp = (INT16)((ghost->player->followmobj->x-ghost->x)>>8); temp = (INT16)((ghost->player->followmobj->x-ghost->x)>>8);
WRITEINT16(demo_p,temp); WRITEINT16(demo_p,temp);
temp = (INT16)((ghost->player->followmobj->y-ghost->y)>>8); temp = (INT16)((ghost->player->followmobj->y-ghost->y)>>8);
@ -4653,11 +4882,6 @@ void G_WriteGhostTic(mobj_t *ghost)
WRITEUINT16(demo_p,ghost->player->followmobj->sprite); WRITEUINT16(demo_p,ghost->player->followmobj->sprite);
WRITEUINT8(demo_p,(ghost->player->followmobj->frame & FF_FRAMEMASK)); WRITEUINT8(demo_p,(ghost->player->followmobj->frame & FF_FRAMEMASK));
WRITEUINT8(demo_p,ghost->player->followmobj->color); WRITEUINT8(demo_p,ghost->player->followmobj->color);
if (ghost->player->followmobj->scale != ghost->scale)
{
followtic |= FZT_SCALE;
WRITEFIXED(demo_p,ghost->player->followmobj->scale);
}
*followtic_p = followtic; *followtic_p = followtic;
} }
@ -5194,7 +5418,10 @@ void G_ReadMetalTic(mobj_t *metal)
{ // But wait, there's more! { // But wait, there's more!
xziptic = READUINT8(metal_p); xziptic = READUINT8(metal_p);
if (xziptic & EZT_FLIP) if (xziptic & EZT_FLIP)
{
metal->eflags ^= MFE_VERTICALFLIP; metal->eflags ^= MFE_VERTICALFLIP;
metal->flags2 ^= MF2_OBJECTFLIP;
}
if (xziptic & EZT_SCALE) if (xziptic & EZT_SCALE)
{ {
metal->destscale = READFIXED(metal_p); metal->destscale = READFIXED(metal_p);
@ -6702,6 +6929,22 @@ boolean G_GetRetryFlag(void)
return retrying; return retrying;
} }
void G_SetModeAttackRetryFlag(void)
{
retryingmodeattack = true;
G_SetRetryFlag();
}
void G_ClearModeAttackRetryFlag(void)
{
retryingmodeattack = false;
}
boolean G_GetModeAttackRetryFlag(void)
{
return retryingmodeattack;
}
// Time utility functions // Time utility functions
INT32 G_TicsToHours(tic_t tics) INT32 G_TicsToHours(tic_t tics)
{ {

View File

@ -140,7 +140,8 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost);
void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar,
boolean SSSG, boolean FLS); boolean SSSG, boolean FLS);
void G_DoLoadLevel(boolean resetplayer); void G_DoLoadLevel(boolean resetplayer);
void G_StartTitleCard(void);
void G_PreLevelTitleCard(tic_t ticker, boolean update);
void G_DeferedPlayDemo(const char *demo); void G_DeferedPlayDemo(const char *demo);
// Can be called by the startup code or M_Responder, calls P_SetupLevel. // Can be called by the startup code or M_Responder, calls P_SetupLevel.
@ -208,6 +209,7 @@ 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_EnoughPlayersFinished(void);
void G_ExitLevel(void); void G_ExitLevel(void);
void G_NextLevel(void); void G_NextLevel(void);
void G_Continue(void); void G_Continue(void);
@ -223,10 +225,14 @@ void G_AddPlayer(INT32 playernum);
void G_SetExitGameFlag(void); void G_SetExitGameFlag(void);
void G_ClearExitGameFlag(void); void G_ClearExitGameFlag(void);
boolean G_GetExitGameFlag(void); boolean G_GetExitGameFlag(void);
void G_SetRetryFlag(void); void G_SetRetryFlag(void);
void G_ClearRetryFlag(void); void G_ClearRetryFlag(void);
boolean G_GetRetryFlag(void); boolean G_GetRetryFlag(void);
void G_SetModeAttackRetryFlag(void);
void G_ClearModeAttackRetryFlag(void);
boolean G_GetModeAttackRetryFlag(void);
void G_LoadGameData(void); void G_LoadGameData(void);
void G_LoadGameSettings(void); void G_LoadGameSettings(void);

View File

@ -771,18 +771,25 @@ void HWR_InitTextureCache(void)
gr_textures2 = NULL; gr_textures2 = NULL;
} }
// Callback function for HWR_FreeTextureCache. // Callback function for HWR_FreeTextureCache.
static void FreeMipmapColormap(INT32 patchnum, void *patch) static void FreeMipmapColormap(INT32 patchnum, void *patch)
{ {
GLPatch_t* const grpatch = patch; GLPatch_t* const pat = patch;
(void)patchnum; //unused (void)patchnum; //unused
while (grpatch->mipmap->nextcolormap) while (pat->mipmap && pat->mipmap->nextcolormap) // The mipmap must be valid, obviously
{ {
GLMipmap_t *grmip = grpatch->mipmap->nextcolormap; // Confusing at first, but pat->mipmap->nextcolormap
grpatch->mipmap->nextcolormap = grmip->nextcolormap; // at the beginning of the loop is the first colormap
if (grmip->grInfo.data) Z_Free(grmip->grInfo.data); // from the linked list of colormaps
free(grmip); GLMipmap_t *next = pat->mipmap->nextcolormap;
// Set the first colormap
// to the one that comes after it
pat->mipmap->nextcolormap = next->nextcolormap;
// Free image data from memory
if (next->grInfo.data)
Z_Free(next->grInfo.data);
// Free the old colormap from memory
free(next);
} }
} }
@ -799,7 +806,7 @@ void HWR_FreeTextureCache(void)
// Alam: free the Z_Blocks before freeing it's users // Alam: free the Z_Blocks before freeing it's users
// free all skin after each level: must be done after pfnClearMipMapCache! // free all patch colormaps after each level: must be done after ClearMipMapCache!
for (i = 0; i < numwadfiles; i++) for (i = 0; i < numwadfiles; i++)
M_AATreeIterate(wadfiles[i]->hwrcache, FreeMipmapColormap); M_AATreeIterate(wadfiles[i]->hwrcache, FreeMipmapColormap);

View File

@ -380,9 +380,9 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t
{ {
FSurfaceInfo Surf; FSurfaceInfo Surf;
Surf.FlatColor.s.red = Surf.FlatColor.s.green = Surf.FlatColor.s.blue = 0xff; Surf.FlatColor.s.red = Surf.FlatColor.s.green = Surf.FlatColor.s.blue = 0xff;
if (alphalevel == 13) Surf.FlatColor.s.alpha = softwaretranstogl_lo[cv_translucenthud.value]; if (alphalevel == 13) Surf.FlatColor.s.alpha = softwaretranstogl_lo[st_translucency];
else if (alphalevel == 14) Surf.FlatColor.s.alpha = softwaretranstogl[cv_translucenthud.value]; else if (alphalevel == 14) Surf.FlatColor.s.alpha = softwaretranstogl[st_translucency];
else if (alphalevel == 15) Surf.FlatColor.s.alpha = softwaretranstogl_hi[cv_translucenthud.value]; else if (alphalevel == 15) Surf.FlatColor.s.alpha = softwaretranstogl_hi[st_translucency];
else Surf.FlatColor.s.alpha = softwaretranstogl[10-alphalevel]; else Surf.FlatColor.s.alpha = softwaretranstogl[10-alphalevel];
flags |= PF_Modulated; flags |= PF_Modulated;
HWD.pfnDrawPolygon(&Surf, v, 4, flags); HWD.pfnDrawPolygon(&Surf, v, 4, flags);
@ -538,9 +538,9 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
{ {
FSurfaceInfo Surf; FSurfaceInfo Surf;
Surf.FlatColor.s.red = Surf.FlatColor.s.green = Surf.FlatColor.s.blue = 0xff; Surf.FlatColor.s.red = Surf.FlatColor.s.green = Surf.FlatColor.s.blue = 0xff;
if (alphalevel == 13) Surf.FlatColor.s.alpha = softwaretranstogl_lo[cv_translucenthud.value]; if (alphalevel == 13) Surf.FlatColor.s.alpha = softwaretranstogl_lo[st_translucency];
else if (alphalevel == 14) Surf.FlatColor.s.alpha = softwaretranstogl[cv_translucenthud.value]; else if (alphalevel == 14) Surf.FlatColor.s.alpha = softwaretranstogl[st_translucency];
else if (alphalevel == 15) Surf.FlatColor.s.alpha = softwaretranstogl_hi[cv_translucenthud.value]; else if (alphalevel == 15) Surf.FlatColor.s.alpha = softwaretranstogl_hi[st_translucency];
else Surf.FlatColor.s.alpha = softwaretranstogl[10-alphalevel]; else Surf.FlatColor.s.alpha = softwaretranstogl[10-alphalevel];
flags |= PF_Modulated; flags |= PF_Modulated;
HWD.pfnDrawPolygon(&Surf, v, 4, flags); HWD.pfnDrawPolygon(&Surf, v, 4, flags);

View File

@ -184,6 +184,7 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_CANG &lspr[NOLIGHT], // SPR_CANG
&lspr[NOLIGHT], // SPR_PYRE &lspr[NOLIGHT], // SPR_PYRE
&lspr[NOLIGHT], // SPR_PTER &lspr[NOLIGHT], // SPR_PTER
&lspr[NOLIGHT], // SPR_DRAB
// Generic Boos Items // Generic Boos Items
&lspr[JETLIGHT_L], // SPR_JETF // Boss jet fumes &lspr[JETLIGHT_L], // SPR_JETF // Boss jet fumes
@ -505,6 +506,7 @@ light_t *t_lspr[NUMSPRITES] =
// Game Indicators // Game Indicators
&lspr[NOLIGHT], // SPR_SCOR &lspr[NOLIGHT], // SPR_SCOR
&lspr[NOLIGHT], // SPR_DRWN &lspr[NOLIGHT], // SPR_DRWN
&lspr[NOLIGHT], // SPR_FLII
&lspr[NOLIGHT], // SPR_LCKN &lspr[NOLIGHT], // SPR_LCKN
&lspr[NOLIGHT], // SPR_TTAG &lspr[NOLIGHT], // SPR_TTAG
&lspr[NOLIGHT], // SPR_GFLG &lspr[NOLIGHT], // SPR_GFLG
@ -584,7 +586,6 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[SUPERSPARK_L], // SPR_BOM3 &lspr[SUPERSPARK_L], // SPR_BOM3
&lspr[NOLIGHT], // SPR_BOM4 &lspr[NOLIGHT], // SPR_BOM4
&lspr[REDBALL_L], // SPR_BMNB &lspr[REDBALL_L], // SPR_BMNB
&lspr[NOLIGHT], // SPR_WDDB
// Crumbly rocks // Crumbly rocks
&lspr[NOLIGHT], // SPR_ROIA &lspr[NOLIGHT], // SPR_ROIA
@ -604,8 +605,10 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_ROIO &lspr[NOLIGHT], // SPR_ROIO
&lspr[NOLIGHT], // SPR_ROIP &lspr[NOLIGHT], // SPR_ROIP
// Bricks // Level debris
&lspr[NOLIGHT], // SPR_GFZD
&lspr[NOLIGHT], // SPR_BRIC &lspr[NOLIGHT], // SPR_BRIC
&lspr[NOLIGHT], // SPR_WDDB
// Gravity Well Objects // Gravity Well Objects
&lspr[NOLIGHT], // SPR_GWLG &lspr[NOLIGHT], // SPR_GWLG

View File

@ -40,6 +40,7 @@
#include "../st_stuff.h" #include "../st_stuff.h"
#include "../i_system.h" #include "../i_system.h"
#include "../m_cheat.h" #include "../m_cheat.h"
#include "../f_finale.h"
#ifdef ESLOPE #ifdef ESLOPE
#include "../p_slopes.h" #include "../p_slopes.h"
#endif #endif
@ -111,7 +112,6 @@ static consvar_t cv_grclipwalls = {"gr_clipwalls", "Off", 0, CV_OnOff, NULL, 0,
static consvar_t cv_gralpha = {"gr_alpha", "160", 0, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_gralpha = {"gr_alpha", "160", 0, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
static consvar_t cv_grbeta = {"gr_beta", "0", 0, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL}; static consvar_t cv_grbeta = {"gr_beta", "0", 0, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
static float HWRWipeCounter = 1.0f;
consvar_t cv_grrounddown = {"gr_rounddown", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grrounddown = {"gr_rounddown", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grfov = {"gr_fov", "90", CV_FLOAT|CV_CALL, grfov_cons_t, CV_grFov_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_grfov = {"gr_fov", "90", CV_FLOAT|CV_CALL, grfov_cons_t, CV_grFov_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_grfogdensity = {"gr_fogdensity", "150", CV_CALL|CV_NOINIT, CV_Unsigned, consvar_t cv_grfogdensity = {"gr_fogdensity", "150", CV_CALL|CV_NOINIT, CV_Unsigned,
@ -1973,7 +1973,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
{ {
// Single sided line... Deal only with the middletexture (if one exists) // Single sided line... Deal only with the middletexture (if one exists)
gr_midtexture = R_GetTextureNum(gr_sidedef->midtexture); gr_midtexture = R_GetTextureNum(gr_sidedef->midtexture);
if (gr_midtexture && gr_linedef->special != 41) // Ignore horizon line for OGL if (gr_midtexture && gr_linedef->special != HORIZONSPECIAL) // Ignore horizon line for OGL
{ {
{ {
fixed_t texturevpeg; fixed_t texturevpeg;
@ -4068,6 +4068,7 @@ static gr_vissprite_t *HWR_NewVisSprite(void)
return HWR_GetVisSprite(gr_visspritecount++); return HWR_GetVisSprite(gr_visspritecount++);
} }
#ifdef GLBADSHADOWS
// Finds a floor through which light does not pass. // Finds a floor through which light does not pass.
static fixed_t HWR_OpaqueFloorAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height) static fixed_t HWR_OpaqueFloorAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t height)
{ {
@ -4098,6 +4099,7 @@ static fixed_t HWR_OpaqueFloorAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t hei
return floorz; return floorz;
} }
#endif //#ifdef GLBADSHADOWS
// //
// HWR_DoCulling // HWR_DoCulling
@ -4139,6 +4141,7 @@ static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float v
return false; return false;
} }
#ifdef GLBADSHADOWS
static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float this_scale) static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float this_scale)
{ {
FOutVector swallVerts[4]; FOutVector swallVerts[4];
@ -4311,6 +4314,7 @@ static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float t
HWD.pfnDrawPolygon(&sSurf, swallVerts, 4, PF_Translucent|PF_Modulated|PF_Clip); HWD.pfnDrawPolygon(&sSurf, swallVerts, 4, PF_Translucent|PF_Modulated|PF_Clip);
} }
} }
#endif //#ifdef GLBADSHADOWS
// This is expecting a pointer to an array containing 4 wallVerts for a sprite // This is expecting a pointer to an array containing 4 wallVerts for a sprite
static void HWR_RotateSpritePolyToAim(gr_vissprite_t *spr, FOutVector *wallVerts) static void HWR_RotateSpritePolyToAim(gr_vissprite_t *spr, FOutVector *wallVerts)
@ -4386,6 +4390,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr)
//Hurdler: 25/04/2000: now support colormap in hardware mode //Hurdler: 25/04/2000: now support colormap in hardware mode
HWR_GetMappedPatch(gpatch, spr->colormap); HWR_GetMappedPatch(gpatch, spr->colormap);
#ifdef GLBADSHADOWS
// Draw shadow BEFORE sprite // Draw shadow BEFORE sprite
if (cv_shadow.value // Shadows enabled if (cv_shadow.value // Shadows enabled
&& (spr->mobj->flags & (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY)) != (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY) // Ceiling scenery have no shadow. && (spr->mobj->flags & (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY)) != (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY) // Ceiling scenery have no shadow.
@ -4401,6 +4406,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr)
//////////////////// ////////////////////
HWR_DrawSpriteShadow(spr, gpatch, this_scale); HWR_DrawSpriteShadow(spr, gpatch, this_scale);
} }
#endif //#ifdef GLBADSHADOWS
baseWallVerts[0].x = baseWallVerts[3].x = spr->x1; baseWallVerts[0].x = baseWallVerts[3].x = spr->x1;
baseWallVerts[2].x = baseWallVerts[1].x = spr->x2; baseWallVerts[2].x = baseWallVerts[1].x = spr->x2;
@ -4788,6 +4794,7 @@ static void HWR_DrawSprite(gr_vissprite_t *spr)
//Hurdler: 25/04/2000: now support colormap in hardware mode //Hurdler: 25/04/2000: now support colormap in hardware mode
HWR_GetMappedPatch(gpatch, spr->colormap); HWR_GetMappedPatch(gpatch, spr->colormap);
#ifdef GLBADSHADOWS
// Draw shadow BEFORE sprite // Draw shadow BEFORE sprite
if (cv_shadow.value // Shadows enabled if (cv_shadow.value // Shadows enabled
&& (spr->mobj->flags & (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY)) != (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY) // Ceiling scenery have no shadow. && (spr->mobj->flags & (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY)) != (MF_SCENERY|MF_SPAWNCEILING|MF_NOGRAVITY) // Ceiling scenery have no shadow.
@ -4803,6 +4810,7 @@ static void HWR_DrawSprite(gr_vissprite_t *spr)
//////////////////// ////////////////////
HWR_DrawSpriteShadow(spr, gpatch, this_scale); HWR_DrawSpriteShadow(spr, gpatch, this_scale);
} }
#endif //#ifdef GLBADSHADOWS
// if it has a dispoffset, push it a little towards the camera // if it has a dispoffset, push it a little towards the camera
if (spr->dispoffset) { if (spr->dispoffset) {
@ -7017,7 +7025,6 @@ void HWR_StartScreenWipe(void)
void HWR_EndScreenWipe(void) void HWR_EndScreenWipe(void)
{ {
HWRWipeCounter = 0.0f;
//CONS_Debug(DBG_RENDER, "In HWR_EndScreenWipe()\n"); //CONS_Debug(DBG_RENDER, "In HWR_EndScreenWipe()\n");
HWD.pfnEndScreenWipe(); HWD.pfnEndScreenWipe();
} }
@ -7027,38 +7034,55 @@ void HWR_DrawIntermissionBG(void)
HWD.pfnDrawIntermissionBG(); HWD.pfnDrawIntermissionBG();
} }
void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum) //
// hwr mode wipes
//
static lumpnum_t wipelumpnum;
// puts wipe lumpname in wipename[9]
static boolean HWR_WipeCheck(UINT8 wipenum, UINT8 scrnnum)
{ {
static char lumpname[9] = "FADEmmss"; static char lumpname[9] = "FADEmmss";
lumpnum_t lumpnum;
size_t lsize; size_t lsize;
if (wipenum > 99 || scrnnum > 99) // not a valid wipe number // not a valid wipe number
return; // shouldn't end up here really, the loop should've stopped running beforehand if (wipenum > 99 || scrnnum > 99)
return false; // shouldn't end up here really, the loop should've stopped running beforehand
// puts the numbers into the lumpname // puts the numbers into the wipename
sprintf(&lumpname[4], "%.2hu%.2hu", (UINT16)wipenum, (UINT16)scrnnum); lumpname[4] = '0'+(wipenum/10);
lumpnum = W_CheckNumForName(lumpname); lumpname[5] = '0'+(wipenum%10);
lumpname[6] = '0'+(scrnnum/10);
lumpname[7] = '0'+(scrnnum%10);
wipelumpnum = W_CheckNumForName(lumpname);
if (lumpnum == LUMPERROR) // again, shouldn't be here really // again, shouldn't be here really
return; if (wipelumpnum == LUMPERROR)
return false;
lsize = W_LumpLength(lumpnum);
lsize = W_LumpLength(wipelumpnum);
if (!(lsize == 256000 || lsize == 64000 || lsize == 16000 || lsize == 4000)) if (!(lsize == 256000 || lsize == 64000 || lsize == 16000 || lsize == 4000))
{ {
CONS_Alert(CONS_WARNING, "Fade mask lump %s of incorrect size, ignored\n", lumpname); CONS_Alert(CONS_WARNING, "Fade mask lump %s of incorrect size, ignored\n", lumpname);
return; // again, shouldn't get here if it is a bad size return false; // again, shouldn't get here if it is a bad size
} }
HWR_GetFadeMask(lumpnum); return true;
}
void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum)
{
if (!HWR_WipeCheck(wipenum, scrnnum))
return;
HWR_GetFadeMask(wipelumpnum);
HWD.pfnDoScreenWipe(); HWD.pfnDoScreenWipe();
}
HWRWipeCounter += 0.05f; // increase opacity of end screen void HWR_DoTintedWipe(UINT8 wipenum, UINT8 scrnnum)
{
if (HWRWipeCounter > 1.0f) // It does the same thing
HWRWipeCounter = 1.0f; HWR_DoWipe(wipenum, scrnnum);
} }
void HWR_MakeScreenFinalTexture(void) void HWR_MakeScreenFinalTexture(void)

View File

@ -66,6 +66,7 @@ void HWR_StartScreenWipe(void);
void HWR_EndScreenWipe(void); void HWR_EndScreenWipe(void);
void HWR_DrawIntermissionBG(void); void HWR_DrawIntermissionBG(void);
void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum); void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum);
void HWR_DoTintedWipe(UINT8 wipenum, UINT8 scrnnum);
void HWR_MakeScreenFinalTexture(void); void HWR_MakeScreenFinalTexture(void);
void HWR_DrawScreenFinalTexture(int width, int height); void HWR_DrawScreenFinalTexture(int width, int height);

View File

@ -2587,7 +2587,6 @@ EXPORT void HWRAPI(DoScreenWipe)(void)
tex_downloaded = endScreenWipe; tex_downloaded = endScreenWipe;
} }
// Create a texture from the screen. // Create a texture from the screen.
EXPORT void HWRAPI(MakeScreenTexture) (void) EXPORT void HWRAPI(MakeScreenTexture) (void)
{ {

View File

@ -2322,13 +2322,14 @@ void HU_Erase(void)
// //
// HU_drawPing // HU_drawPing
// //
void HU_drawPing(INT32 x, INT32 y, INT32 ping, boolean notext) void HU_drawPing(INT32 x, INT32 y, UINT32 ping, boolean notext, INT32 flags)
{ {
UINT8 numbars = 1; // how many ping bars do we draw? UINT8 numbars = 1; // how many ping bars do we draw?
UINT8 barcolor = 35; // color we use for the bars (green, yellow or red) UINT8 barcolor = 35; // color we use for the bars (green, yellow or red)
SINT8 i = 0; SINT8 i = 0;
SINT8 yoffset = 6; SINT8 yoffset = 6;
INT32 dx = x+1 - (V_SmallStringWidth(va("%dms", ping), V_ALLOWLOWERCASE)/2); INT32 dx = x+1 - (V_SmallStringWidth(va("%dms", ping),
V_ALLOWLOWERCASE|flags)/2);
if (ping < 128) if (ping < 128)
{ {
@ -2342,13 +2343,13 @@ void HU_drawPing(INT32 x, INT32 y, INT32 ping, boolean notext)
} }
if (!notext || vid.width >= 640) // how sad, we're using a shit resolution. if (!notext || vid.width >= 640) // how sad, we're using a shit resolution.
V_DrawSmallString(dx, y+4, V_ALLOWLOWERCASE, va("%dms", ping)); V_DrawSmallString(dx, y+4, V_ALLOWLOWERCASE|flags, va("%dms", ping));
for (i=0; (i<3); i++) // Draw the ping bar for (i=0; (i<3); i++) // Draw the ping bar
{ {
V_DrawFill(x+2 *(i-1), y+yoffset-4, 2, 8-yoffset, 31); V_DrawFill(x+2 *(i-1), y+yoffset-4, 2, 8-yoffset, 31|flags);
if (i < numbars) if (i < numbars)
V_DrawFill(x+2 *(i-1), y+yoffset-3, 1, 8-yoffset-1, barcolor); V_DrawFill(x+2 *(i-1), y+yoffset-3, 1, 8-yoffset-1, barcolor|flags);
yoffset -= 2; yoffset -= 2;
} }
@ -2379,7 +2380,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
if (!splitscreen) // don't draw it on splitscreen, if (!splitscreen) // don't draw it on splitscreen,
{ {
if (!(tab[i].num == serverplayer)) if (!(tab[i].num == serverplayer))
HU_drawPing(x+ 253, y+2, playerpingtable[tab[i].num], false); HU_drawPing(x+ 253, y, playerpingtable[tab[i].num], false, 0);
//else //else
// V_DrawSmallString(x+ 246, y+4, V_YELLOWMAP, "SERVER"); // V_DrawSmallString(x+ 246, y+4, V_YELLOWMAP, "SERVER");
} }
@ -2443,7 +2444,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
V_DrawSmallScaledPatch(x-32, y-4, 0, tagico); V_DrawSmallScaledPatch(x-32, y-4, 0, tagico);
} }
if (players[tab[i].num].exiting) if (players[tab[i].num].exiting || (players[tab[i].num].pflags & PF_FINISHED))
V_DrawSmallScaledPatch(x - SHORT(exiticon->width)/2 - 1, y-3, 0, exiticon); V_DrawSmallScaledPatch(x - SHORT(exiticon->width)/2 - 1, y-3, 0, exiticon);
if (gametype == GT_RACE) if (gametype == GT_RACE)
@ -2578,7 +2579,7 @@ static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer)
if (!splitscreen) if (!splitscreen)
{ {
if (!(tab[i].num == serverplayer)) if (!(tab[i].num == serverplayer))
HU_drawPing(x+ 135, y+3, playerpingtable[tab[i].num], true); HU_drawPing(x+ 135, y+1, playerpingtable[tab[i].num], true, 0);
//else //else
//V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); //V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST");
} }
@ -2702,7 +2703,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
if (!splitscreen) if (!splitscreen)
{ {
if (!(tab[i].num == serverplayer)) if (!(tab[i].num == serverplayer))
HU_drawPing(x+ 113, y+2, playerpingtable[tab[i].num], false); HU_drawPing(x+ 113, y, playerpingtable[tab[i].num], false, 0);
//else //else
// V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER"); // V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER");
} }
@ -2733,7 +2734,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
strlcpy(name, tab[i].name, 7); strlcpy(name, tab[i].name, 7);
if (!(tab[i].num == serverplayer)) if (!(tab[i].num == serverplayer))
HU_drawPing(x+ 113, y+2, playerpingtable[tab[i].num], false); HU_drawPing(x+ 113, y, playerpingtable[tab[i].num], false, 0);
//else //else
// V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER"); // V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER");
@ -2747,7 +2748,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
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);
if (players[tab[i].num].exiting) if (players[tab[i].num].exiting || (players[tab[i].num].pflags & PF_FINISHED))
V_DrawSmallScaledPatch(x - SHORT(exiticon->width)/2 - 1, y-3, 0, exiticon); V_DrawSmallScaledPatch(x - SHORT(exiticon->width)/2 - 1, y-3, 0, exiticon);
// Draw emeralds // Draw emeralds
@ -2841,7 +2842,7 @@ static void HU_Draw32TabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scor
if (!splitscreen) // don't draw it on splitscreen, if (!splitscreen) // don't draw it on splitscreen,
{ {
if (!(tab[i].num == serverplayer)) if (!(tab[i].num == serverplayer))
HU_drawPing(x+ 135, y+3, playerpingtable[tab[i].num], true); HU_drawPing(x+ 135, y+1, playerpingtable[tab[i].num], true, 0);
//else //else
// V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); // V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST");
} }

View File

@ -28,7 +28,7 @@
// Level title font // Level title font
#define LT_FONTSTART '!' // the first font characters #define LT_FONTSTART '!' // the first font characters
#define LT_FONTEND 'Z' // the last font characters #define LT_FONTEND 'z' // the last font characters
#define LT_FONTSIZE (LT_FONTEND - LT_FONTSTART + 1) #define LT_FONTSIZE (LT_FONTEND - LT_FONTSTART + 1)
#define CRED_FONTSTART '!' // the first font character #define CRED_FONTSTART '!' // the first font character
@ -113,7 +113,7 @@ void HU_Drawer(void);
char HU_dequeueChatChar(void); char HU_dequeueChatChar(void);
void HU_Erase(void); void HU_Erase(void);
void HU_clearChatChars(void); void HU_clearChatChars(void);
void HU_drawPing(INT32 x, INT32 y, INT32 ping, boolean notext); // Lat': Ping drawer for scoreboard. void HU_drawPing(INT32 x, INT32 y, UINT32 ping, boolean notext, INT32 flags); // Lat': Ping drawer for scoreboard.
void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer); void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer);
void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer); void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer);
void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer); void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer);

View File

@ -72,6 +72,7 @@ char sprnames[NUMSPRITES + 1][5] =
"CANG", // Canarivore gas "CANG", // Canarivore gas
"PYRE", // Pyre Fly "PYRE", // Pyre Fly
"PTER", // Pterabyte "PTER", // Pterabyte
"DRAB", // Dragonbomber
// Generic Boss Items // Generic Boss Items
"JETF", // Boss jet fumes "JETF", // Boss jet fumes
@ -402,6 +403,7 @@ char sprnames[NUMSPRITES + 1][5] =
// Game Indicators // Game Indicators
"SCOR", // Score logo "SCOR", // Score logo
"DRWN", // Drowning Timer "DRWN", // Drowning Timer
"FLII", // Flight indicator
"LCKN", // Target "LCKN", // Target
"TTAG", // Tag Sign "TTAG", // Tag Sign
"GFLG", // Got Flag sign "GFLG", // Got Flag sign
@ -481,7 +483,6 @@ char sprnames[NUMSPRITES + 1][5] =
"BOM3", // Boss Explosion 2 "BOM3", // Boss Explosion 2
"BOM4", // Underwater Explosion "BOM4", // Underwater Explosion
"BMNB", // Mine Explosion "BMNB", // Mine Explosion
"WDDB", // Wood Debris
// Crumbly rocks // Crumbly rocks
"ROIA", "ROIA",
@ -501,8 +502,10 @@ char sprnames[NUMSPRITES + 1][5] =
"ROIO", "ROIO",
"ROIP", "ROIP",
// Bricks // Level debris
"BRIC", "GFZD", // GFZ debris
"BRIC", // Bricks
"WDDB", // Wood Debris
// Gravity Well Objects // Gravity Well Objects
"GWLG", "GWLG",
@ -632,7 +635,7 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = {
0, // SPR2_TRNS, 0, // SPR2_TRNS,
FF_SPR2SUPER|SPR2_STND, // SPR2_NSTD, FF_SPR2SUPER|SPR2_STND, // SPR2_NSTD,
FF_SPR2SUPER|SPR2_FLT , // SPR2_NFLT, FF_SPR2SUPER|SPR2_FALL, // SPR2_NFLT,
0, // SPR2_NFLY, (will never be referenced unless skin 0 lacks this) 0, // SPR2_NFLY, (will never be referenced unless skin 0 lacks this)
SPR2_NFLY, // SPR2_NDRL, SPR2_NFLY, // SPR2_NDRL,
FF_SPR2SUPER|SPR2_STUN, // SPR2_NSTN, FF_SPR2SUPER|SPR2_STUN, // SPR2_NSTN,
@ -1071,23 +1074,11 @@ state_t states[NUMSTATES] =
{SPR_MNUD, 2|FF_ANIMATE, 5, {NULL}, 1, 2, S_MINUS_BURST4}, // S_MINUS_BURST3 {SPR_MNUD, 2|FF_ANIMATE, 5, {NULL}, 1, 2, S_MINUS_BURST4}, // S_MINUS_BURST3
{SPR_MNUD, 3|FF_ANIMATE, 5, {NULL}, 1, 2, S_MINUS_BURST5}, // S_MINUS_BURST4 {SPR_MNUD, 3|FF_ANIMATE, 5, {NULL}, 1, 2, S_MINUS_BURST5}, // S_MINUS_BURST4
{SPR_MNUD, 4|FF_ANIMATE, 5, {NULL}, 1, 2, S_MINUSDIRT2}, // S_MINUS_BURST5 {SPR_MNUD, 4|FF_ANIMATE, 5, {NULL}, 1, 2, S_MINUSDIRT2}, // S_MINUS_BURST5
{SPR_MNUS, 0, 1, {A_MinusPopup}, 0, 0, S_MINUS_UPWARD1}, // S_MINUS_POPUP {SPR_MNUS, 3, 1, {A_MinusPopup}, 0, 0, S_MINUS_AERIAL1}, // S_MINUS_POPUP
{SPR_MNUS, 0, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD2}, // S_MINUS_UPWARD1 {SPR_MNUS, 0, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL2}, // S_MINUS_AERIAL1
{SPR_MNUS, 1, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD3}, // S_MINUS_UPWARD2 {SPR_MNUS, 1, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL3}, // S_MINUS_AERIAL2
{SPR_MNUS, 2, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD4}, // S_MINUS_UPWARD3 {SPR_MNUS, 2, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL4}, // S_MINUS_AERIAL3
{SPR_MNUS, 3, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD5}, // S_MINUS_UPWARD4 {SPR_MNUS, 3, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL1}, // S_MINUS_AERIAL4
{SPR_MNUS, 4, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD6}, // S_MINUS_UPWARD5
{SPR_MNUS, 5, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD7}, // S_MINUS_UPWARD6
{SPR_MNUS, 6, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD8}, // S_MINUS_UPWARD7
{SPR_MNUS, 7, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD1}, // S_MINUS_UPWARD8
{SPR_MNUS, 8, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD2}, // S_MINUS_DOWNWARD1
{SPR_MNUS, 9, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD3}, // S_MINUS_DOWNWARD2
{SPR_MNUS, 10, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD4}, // S_MINUS_DOWNWARD3
{SPR_MNUS, 11, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD5}, // S_MINUS_DOWNWARD4
{SPR_MNUS, 12, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD6}, // S_MINUS_DOWNWARD5
{SPR_MNUS, 13, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD7}, // S_MINUS_DOWNWARD6
{SPR_MNUS, 14, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD8}, // S_MINUS_DOWNWARD7
{SPR_MNUS, 15, 1, {A_MinusCheck}, 0, 0, S_MINUS_DOWNWARD1}, // S_MINUS_DOWNWARD8
{SPR_MNUD, FF_ANIMATE, 6, {NULL}, 1, 5, S_MINUSDIRT2}, // S_MINUSDIRT1 {SPR_MNUD, FF_ANIMATE, 6, {NULL}, 1, 5, S_MINUSDIRT2}, // S_MINUSDIRT1
{SPR_MNUD, 5, 8, {NULL}, 3, 5, S_MINUSDIRT3}, // S_MINUSDIRT2 {SPR_MNUD, 5, 8, {NULL}, 3, 5, S_MINUSDIRT3}, // S_MINUSDIRT2
@ -1163,6 +1154,26 @@ state_t states[NUMSTATES] =
{SPR_PTER, 4, 1, {NULL}, 0, 0, S_PTERABYTE_SWOOPDOWN}, // S_PTERABYTE_SWOOPDOWN {SPR_PTER, 4, 1, {NULL}, 0, 0, S_PTERABYTE_SWOOPDOWN}, // S_PTERABYTE_SWOOPDOWN
{SPR_PTER, 0, 1, {NULL}, 0, 0, S_PTERABYTE_SWOOPUP}, // S_PTERABYTE_SWOOPUP {SPR_PTER, 0, 1, {NULL}, 0, 0, S_PTERABYTE_SWOOPUP}, // S_PTERABYTE_SWOOPUP
// Dragonbomber
{SPR_DRAB, 0, -1, {A_DragonbomberSpawn}, 6, 0, S_NULL}, // S_DRAGONBOMBER
{SPR_DRAB, FF_PAPERSPRITE|7, 1, {A_DragonWing}, 0, 0, S_DRAGONWING2}, // S_DRAGONWING1
{SPR_DRAB, FF_PAPERSPRITE|8, 1, {A_DragonWing}, 0, 0, S_DRAGONWING3}, // S_DRAGONWING2
{SPR_DRAB, FF_PAPERSPRITE|9, 1, {A_DragonWing}, 0, 0, S_DRAGONWING4}, // S_DRAGONWING3
{SPR_DRAB, FF_PAPERSPRITE|10, 1, {A_DragonWing}, 0, 0, S_DRAGONWING1}, // S_DRAGONWING4
{SPR_DRAB, 1, 1, {A_DragonSegment}, 0, 0, S_DRAGONTAIL_LOADED}, // S_DRAGONTAIL_LOADED
{SPR_DRAB, 2, 1, {A_DragonSegment}, 0, 0, S_DRAGONTAIL_EMPTYLOOP}, // S_DRAGONTAIL_EMPTY
{SPR_DRAB, 2, 0, {A_Repeat}, 3*TICRATE, S_DRAGONTAIL_EMPTY, S_DRAGONTAIL_RELOAD}, // S_DRAGONTAIL_EMPTYLOOP
{SPR_DRAB, 1, 0, {A_PlayActiveSound}, 0, 0, S_DRAGONTAIL_LOADED}, // S_DRAGONTAIL_RELOAD
{SPR_DRAB, 3, 1, {A_MinusCheck}, S_DRAGONMINE_LAND1, 0, S_DRAGONMINE}, // S_DRAGONMINE
{SPR_DRAB, 4, 0, {A_PlayActiveSound}, 0, 0, S_DRAGONMINE_LAND2}, // S_DRAGONMINE_LAND1
{SPR_DRAB, 4, 2, {A_Thrust}, 0, 1, S_DRAGONMINE_SLOWFLASH1}, // S_DRAGONMINE_LAND2
{SPR_DRAB, 5, 11, {NULL}, 0, 0, S_DRAGONMINE_SLOWFLASH2}, // S_DRAGONMINE_SLOWFLASH1
{SPR_DRAB, FF_FULLBRIGHT|6, 1, {A_PlayAttackSound}, 0, 0, S_DRAGONMINE_SLOWLOOP}, // S_DRAGONMINE_SLOWFLASH2
{SPR_DRAB, 5, 0, {A_Repeat}, 4, S_DRAGONMINE_SLOWFLASH1, S_DRAGONMINE_FASTFLASH1}, // S_DRAGONMINE_SLOWLOOP
{SPR_DRAB, 5, 3, {NULL}, 0, 0, S_DRAGONMINE_FASTFLASH2}, // S_DRAGONMINE_FASTFLASH1
{SPR_DRAB, FF_FULLBRIGHT|6, 1, {A_PlayAttackSound}, 0, 0, S_DRAGONMINE_FASTLOOP}, // S_DRAGONMINE_FASTFLASH2
{SPR_DRAB, 5, 0, {A_Repeat}, 5, S_DRAGONMINE_FASTFLASH1, S_DEATHSTATE}, // S_DRAGONMINE_FASTLOOP
// Boss Explosion // Boss Explosion
{SPR_BOM2, FF_FULLBRIGHT|FF_ANIMATE, (5*7), {NULL}, 6, 5, S_NULL}, // S_BOSSEXPLODE {SPR_BOM2, FF_FULLBRIGHT|FF_ANIMATE, (5*7), {NULL}, 6, 5, S_NULL}, // S_BOSSEXPLODE
@ -1487,6 +1498,7 @@ state_t states[NUMSTATES] =
{SPR_FSGN, 0|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_FSGNA {SPR_FSGN, 0|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_FSGNA
{SPR_FSGN, 1|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_FSGNB {SPR_FSGN, 1|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_FSGNB
{SPR_FSGN, 2|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_FSGNC {SPR_FSGN, 2|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_FSGNC
{SPR_FSGN, 3|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_FSGND
// Black Eggman (Boss 7) // Black Eggman (Boss 7)
{SPR_BRAK, 0, 1, {A_SetReactionTime}, 0, 0, S_BLACKEGG_STND2}, // S_BLACKEGG_STND {SPR_BRAK, 0, 1, {A_SetReactionTime}, 0, 0, S_BLACKEGG_STND2}, // S_BLACKEGG_STND
@ -3319,6 +3331,9 @@ state_t states[NUMSTATES] =
{SPR_DRWN, 10, 40, {NULL}, 0, 0, S_NULL}, // S_FOUR2 {SPR_DRWN, 10, 40, {NULL}, 0, 0, S_NULL}, // S_FOUR2
{SPR_DRWN, 11, 40, {NULL}, 0, 0, S_NULL}, // S_FIVE2 {SPR_DRWN, 11, 40, {NULL}, 0, 0, S_NULL}, // S_FIVE2
// Flight indicator
{SPR_FLII, FF_FULLBRIGHT|FF_ANIMATE|0, -1, {NULL}, 4, 4, S_NULL}, // S_FLIGHTINDICATOR
{SPR_LCKN, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON1 {SPR_LCKN, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON1
{SPR_LCKN, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON2 {SPR_LCKN, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON2
{SPR_LCKN, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON3 {SPR_LCKN, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON3
@ -3527,13 +3542,9 @@ state_t states[NUMSTATES] =
{SPR_FFWR, 3, 3, {NULL}, 0, 0, S_FIREFLOWER1}, // S_FIREFLOWER4 {SPR_FFWR, 3, 3, {NULL}, 0, 0, S_FIREFLOWER1}, // S_FIREFLOWER4
// Thrown Mario Fireball // Thrown Mario Fireball
{SPR_FBLL, FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_FIREBALL2}, // S_FIREBALL1 {SPR_FBLL, FF_FULLBRIGHT, 1, {A_SpawnObjectRelative}, 0, MT_FIREBALLTRAIL, S_FIREBALL}, // S_FIREBALL
{SPR_FBLL, FF_FULLBRIGHT|1, 3, {NULL}, 0, 0, S_FIREBALL3}, // S_FIREBALL2 {SPR_FBLL, 1|FF_FULLBRIGHT|FF_TRANS50, 1, {A_SetScale}, FRACUNIT*3/4, 0, S_FIREBALLTRAIL2}, // S_FIREBALLTRAIL1
{SPR_FBLL, FF_FULLBRIGHT|2, 3, {NULL}, 0, 0, S_FIREBALL4}, // S_FIREBALL3 {SPR_FBLL, 1|FF_FULLBRIGHT|FF_TRANS50, 8, {A_SetScale}, FRACUNIT/6, 1, S_NULL}, // S_FIREBALLTRAIL2
{SPR_FBLL, FF_FULLBRIGHT|3, 3, {NULL}, 0, 0, S_FIREBALL1}, // S_FIREBALL4
{SPR_FBLL, FF_FULLBRIGHT|4, 3, {NULL}, 0, 0, S_FIREBALLEXP2}, // S_FIREBALLEXP1
{SPR_FBLL, FF_FULLBRIGHT|5, 3, {NULL}, 0, 0, S_FIREBALLEXP3}, // S_FIREBALLEXP2
{SPR_FBLL, FF_FULLBRIGHT|6, 3, {NULL}, 0, 0, S_NULL}, // S_FIREBALLEXP3
// Turtle Shell // Turtle Shell
{SPR_SHLL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHELL {SPR_SHLL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHELL
@ -3722,9 +3733,12 @@ state_t states[NUMSTATES] =
{SPR_PENG, 0, 5, {A_FaceTarget}, 0, 0, S_PENGUINATOR_LOOK}, // S_PENGUINATOR_SLIDE5 {SPR_PENG, 0, 5, {A_FaceTarget}, 0, 0, S_PENGUINATOR_LOOK}, // S_PENGUINATOR_SLIDE5
{SPR_POPH, 0, 2, {A_Look}, (2048<<16)|1, 0, S_POPHAT_LOOK}, // S_POPHAT_LOOK {SPR_POPH, 0, 2, {A_Look}, (2048<<16)|1, 0, S_POPHAT_LOOK}, // S_POPHAT_LOOK
{SPR_POPH, 1, 2, {A_LobShot}, MT_POPSHOT, (70<<16)|60, S_POPHAT_SHOOT2}, // S_POPHAT_SHOOT1 {SPR_POPH, 1, 0, {A_MultiShotDist}, (MT_SPINDUST<<16)|4, 24, S_POPHAT_SHOOT2}, // S_POPHAT_SHOOT1
{SPR_POPH, 2, 1, {NULL}, 0, 0, S_POPHAT_SHOOT3}, // S_POPHAT_SHOOT2 {SPR_POPH, 1, 2, {A_LobShot}, MT_POPSHOT, (70<<16)|60, S_POPHAT_SHOOT3}, // S_POPHAT_SHOOT2
{SPR_POPH, 0, 57, {NULL}, 0, 0, S_POPHAT_LOOK}, // S_POPHAT_SHOOT3 {SPR_POPH, 2, 1, {NULL}, 0, 0, S_POPHAT_SHOOT4}, // S_POPHAT_SHOOT3
{SPR_POPH, 0, 57, {NULL}, 0, 0, S_POPHAT_LOOK}, // S_POPHAT_SHOOT4
{SPR_POPH, 3, 3, {A_SpawnObjectRelative}, 0, MT_POPSHOT_TRAIL, S_POPSHOT}, // S_POPSHOT
{SPR_NULL, 0, 2, {NULL}, 0, 0, S_SPINDUST1}, // S_POPSHOT_TRAIL
{SPR_HIVE, 0, 5, {A_Look}, 1, 1, S_HIVEELEMENTAL_LOOK}, // S_HIVEELEMENTAL_LOOK {SPR_HIVE, 0, 5, {A_Look}, 1, 1, S_HIVEELEMENTAL_LOOK}, // S_HIVEELEMENTAL_LOOK
{SPR_HIVE, 0, 14, {A_PlaySound}, sfx_s3k76, 1, S_HIVEELEMENTAL_PREPARE2}, // S_HIVEELEMENTAL_PREPARE1 {SPR_HIVE, 0, 14, {A_PlaySound}, sfx_s3k76, 1, S_HIVEELEMENTAL_PREPARE2}, // S_HIVEELEMENTAL_PREPARE1
@ -3858,8 +3872,6 @@ state_t states[NUMSTATES] =
{SPR_DUST, 2|FF_TRANS60, 3, {NULL}, 0, 0, S_DUST4}, // S_DUST3 {SPR_DUST, 2|FF_TRANS60, 3, {NULL}, 0, 0, S_DUST4}, // S_DUST3
{SPR_DUST, 3|FF_TRANS70, 2, {NULL}, 0, 0, S_NULL}, // S_DUST4 {SPR_DUST, 3|FF_TRANS70, 2, {NULL}, 0, 0, S_NULL}, // S_DUST4
{SPR_WDDB, FF_ANIMATE, -1, {A_DebrisRandom}, 7, 2, S_NULL}, // S_WOODDEBRIS
{SPR_NULL, 0, 1, {A_RockSpawn}, 0, 0, S_ROCKSPAWN}, // S_ROCKSPAWN {SPR_NULL, 0, 1, {A_RockSpawn}, 0, 0, S_ROCKSPAWN}, // S_ROCKSPAWN
{SPR_ROIA, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEA {SPR_ROIA, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_NULL}, // S_ROCKCRUMBLEA
@ -3879,7 +3891,9 @@ state_t states[NUMSTATES] =
{SPR_ROIO, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEO {SPR_ROIO, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEO
{SPR_ROIP, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEP {SPR_ROIP, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 7, 2, S_NULL}, // S_ROCKCRUMBLEP
{SPR_GFZD, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 31, 1, S_NULL}, // S_GFZDEBRIS
{SPR_BRIC, FF_ANIMATE, -1, {A_DebrisRandom}, 7, 2, S_NULL}, // S_BRICKDEBRIS {SPR_BRIC, FF_ANIMATE, -1, {A_DebrisRandom}, 7, 2, S_NULL}, // S_BRICKDEBRIS
{SPR_WDDB, FF_ANIMATE, -1, {A_DebrisRandom}, 7, 2, S_NULL}, // S_WOODDEBRIS
#ifdef SEENAMES #ifdef SEENAMES
{SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK
@ -5238,6 +5252,114 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_DRAGONBOMBER
137, // doomednum
S_DRAGONBOMBER, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
6, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
10*FRACUNIT, // speed
28*FRACUNIT, // radius
48*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_SPECIAL|MF_SHOOTABLE|MF_ENEMY|MF_NOGRAVITY|MF_BOUNCE|MF_RUNSPAWNFUNC, // flags
S_NULL // raisestate
},
{ // MT_DRAGONWING
-1, // doomednum
S_DRAGONWING1, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
0, // speed
12*FRACUNIT, // radius
12*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_None, // activesound
MF_NOGRAVITY|MF_SCENERY|MF_NOBLOCKMAP|MF_NOCLIP, // flags
S_NULL // raisestate
},
{ // MT_DRAGONTAIL
-1, // doomednum
S_DRAGONTAIL_LOADED, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
MT_DRAGONMINE, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_XPLD1, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
20*FRACUNIT, // radius
40*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_tink, // activesound
MF_NOGRAVITY|MF_SLIDEME|MF_PAIN, // flags
S_DRAGONTAIL_EMPTY // raisestate
},
{ // MT_DRAGONMINE
-1, // doomednum
S_DRAGONMINE, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_s3k76, // seesound
0, // reactiontime
sfx_s3k89, // attacksound
S_NULL, // painstate
6, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_TNTBARREL_EXPL1, // deathstate
S_NULL, // xdeathstate
sfx_s3k6e, // deathsound
0, // speed
16*FRACUNIT, // radius
32*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_s3k5d, // activesound
MF_SPECIAL|MF_SHOOTABLE, // flags
S_NULL // raisestate
},
{ // MT_BOSSEXPLODE { // MT_BOSSEXPLODE
-1, // doomednum -1, // doomednum
S_BOSSEXPLODE, // spawnstate S_BOSSEXPLODE, // spawnstate
@ -6115,11 +6237,11 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // meleestate S_NULL, // meleestate
S_NULL, // missilestate S_NULL, // missilestate
S_FSGNC, // deathstate S_FSGNC, // deathstate
S_NULL, // xdeathstate S_FSGND, // xdeathstate
sfx_None, // deathsound sfx_None, // deathsound
0, // speed 0, // speed
124*FRACUNIT, // radius 74*FRACUNIT, // radius
640*FRACUNIT, // height 320*FRACUNIT, // height
0, // display offset 0, // display offset
0, // mass 0, // mass
0, // damage 0, // damage
@ -13247,7 +13369,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass 100, // mass
0, // damage 0, // damage
sfx_None, // activesound sfx_None, // activesound
MF_SPECIAL|MF_PAIN|MF_NOGRAVITY, // flags MF_SPECIAL|MF_PAIN|MF_NOGRAVITY|MF_FIRE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
@ -13315,7 +13437,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // attacksound sfx_None, // attacksound
S_NULL, // painstate S_NULL, // painstate
12*TICRATE, // painchance (sets how long an unridden rock should last before disappearing - set to 0 to disable) 12*TICRATE, // painchance (sets how long an unridden rock should last before disappearing - set to 0 to disable)
sfx_None, // painsound sfx_s3k49, // painsound
S_NULL, // meleestate S_NULL, // meleestate
S_NULL, // missilestate S_NULL, // missilestate
S_NULL, // deathstate S_NULL, // deathstate
@ -18910,31 +19032,58 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
{ // MT_FIREBALL { // MT_FIREBALL
-1, // doomednum -1, // doomednum
S_FIREBALL1, // spawnstate S_FIREBALL, // spawnstate
1000, // spawnhealth 1000, // spawnhealth
S_FIREBALLEXP1, // seestate S_NULL, // seestate
sfx_None, // seesound sfx_None, // seesound
8, // reactiontime 8, // reactiontime
sfx_None, // attacksound sfx_None, // attacksound
S_NULL, // painstate S_NULL, // painstate
0, // painchance 0, // painchance
sfx_None, // painsound sfx_None, // painsound
S_FIREBALLEXP1, // meleestate S_NULL, // meleestate
S_FIREBALLEXP1, // missilestate S_NULL, // missilestate
S_FIREBALLEXP1, // deathstate S_NULL, // deathstate
S_FIREBALLEXP1, // xdeathstate S_NULL, // xdeathstate
sfx_mario1, // deathsound sfx_None, // deathsound
10*FRACUNIT, // speed 40*FRACUNIT, // speed
4*FRACUNIT, // radius 4*FRACUNIT, // radius
8*FRACUNIT, // height 8*FRACUNIT, // height
0, // display offset 0, // display offset
DMG_FIRE, // mass DMG_FIRE, // mass
1, // damage 1, // damage
sfx_None, // activesound sfx_None, // activesound
MF_NOBLOCKMAP|MF_FIRE|MF_MISSILE, // flags MF_FIRE|MF_BOUNCE|MF_MISSILE, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_FIREBALLTRAIL
-1, // doomednum
S_FIREBALLTRAIL1, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
16*FRACUNIT, // radius
16*FRACUNIT, // height
0, // display offset
0, // mass
1, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_RUNSPAWNFUNC, // flags
S_NULL // raisestate
},
{ // MT_SHELL { // MT_SHELL
1804, // doomednum 1804, // doomednum
S_SHELL, // spawnstate S_SHELL, // spawnstate
@ -19989,10 +20138,10 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
{ // MT_POPSHOT { // MT_POPSHOT
-1, // doomednum -1, // doomednum
S_ROCKCRUMBLEI, // spawnstate S_POPSHOT, // spawnstate
1, // spawnhealth 1, // spawnhealth
S_NULL, // seestate S_NULL, // seestate
sfx_cannon, // seesound sfx_kc4c, // seesound
0, // reactiontime 0, // reactiontime
sfx_None, // attacksound sfx_None, // attacksound
S_NULL, // painstate S_NULL, // painstate
@ -20000,9 +20149,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound sfx_None, // painsound
S_NULL, // meleestate S_NULL, // meleestate
S_NULL, // missilestate S_NULL, // missilestate
S_XPLD1, // deathstate S_SONIC3KBOSSEXPLOSION1, // deathstate
S_NULL, // xdeathstate S_NULL, // xdeathstate
sfx_pop, // deathsound sfx_cybdth, // deathsound
0, // speed 0, // speed
16*FRACUNIT, // radius 16*FRACUNIT, // radius
32*FRACUNIT, // height 32*FRACUNIT, // height
@ -20014,6 +20163,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_POPSHOT_TRAIL
-1, // doomednum
S_POPSHOT_TRAIL,// spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
4*FRACUNIT, // speed
4*FRACUNIT, // radius
4*FRACUNIT, // height
0, // display offset
4, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP, // flags
S_NULL // raisestate
},
{ // MT_HIVEELEMENTAL { // MT_HIVEELEMENTAL
127, // doomednum 127, // doomednum
S_HIVEELEMENTAL_LOOK, // spawnstate S_HIVEELEMENTAL_LOOK, // spawnstate
@ -20773,33 +20949,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_WOODDEBRIS
-1, // doomednum
S_WOODDEBRIS, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
16*FRACUNIT, // radius
16*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_wbreak, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_RUNSPAWNFUNC|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
S_NULL // raisestate
},
{ // MT_ROCKSPAWNER { // MT_ROCKSPAWNER
1202, // doomednum 1202, // doomednum
S_ROCKSPAWN, // spawnstate S_ROCKSPAWN, // spawnstate
@ -21286,16 +21435,16 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_BRICKDEBRIS { // MT_GFZDEBRIS
-1, // doomednum -1, // doomednum
S_BRICKDEBRIS, // spawnstate S_GFZDEBRIS, // spawnstate
1, // spawnhealth 1, // spawnhealth
S_NULL, // seestate S_NULL, // seestate
sfx_None, // seesound sfx_None, // seesound
0, // reactiontime 0, // reactiontime
sfx_None, // attacksound sfx_None, // attacksound
S_NULL, // painstate S_NULL, // painstate
0, // painchance 0, // painchance
sfx_None, // painsound sfx_None, // painsound
S_NULL, // meleestate S_NULL, // meleestate
S_NULL, // missilestate S_NULL, // missilestate
@ -21303,16 +21452,70 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // xdeathstate S_NULL, // xdeathstate
sfx_None, // deathsound sfx_None, // deathsound
0, // speed 0, // speed
16*FRACUNIT, // radius 32*FRACUNIT, // radius
64*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_RUNSPAWNFUNC|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
S_NULL // raisestate
},
{ // MT_BRICKDEBRIS
-1, // doomednum
S_BRICKDEBRIS, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
16*FRACUNIT, // radius
16*FRACUNIT, // height 16*FRACUNIT, // height
0, // display offset 0, // display offset
100, // mass 100, // mass
0, // damage 0, // damage
sfx_None, // activesound sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_RUNSPAWNFUNC|MF_NOCLIPHEIGHT|MF_SCENERY, // flags MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_RUNSPAWNFUNC|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
S_NULL // raisestate S_NULL // raisestate
}, },
{ // MT_WOODDEBRIS
-1, // doomednum
S_WOODDEBRIS, // spawnstate
1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
0, // speed
16*FRACUNIT, // radius
16*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
sfx_wbreak, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_RUNSPAWNFUNC|MF_NOCLIPHEIGHT|MF_SCENERY, // flags
S_NULL // raisestate
},
#ifdef SEENAMES #ifdef SEENAMES
{ // MT_NAMECHECK { // MT_NAMECHECK
-1, // doomednum -1, // doomednum

View File

@ -282,6 +282,9 @@ void A_SpawnPterabytes();
void A_PterabyteHover(); void A_PterabyteHover();
void A_RolloutSpawn(); void A_RolloutSpawn();
void A_RolloutRock(); void A_RolloutRock();
void A_DragonbomberSpawn();
void A_DragonWing();
void A_DragonSegment();
// ratio of states to sprites to mobj types is roughly 6 : 1 : 1 // ratio of states to sprites to mobj types is roughly 6 : 1 : 1
#define NUMMOBJFREESLOTS 512 #define NUMMOBJFREESLOTS 512
@ -334,6 +337,7 @@ typedef enum sprite
SPR_CANG, // Canarivore gas SPR_CANG, // Canarivore gas
SPR_PYRE, // Pyre Fly SPR_PYRE, // Pyre Fly
SPR_PTER, // Pterabyte SPR_PTER, // Pterabyte
SPR_DRAB, // Dragonbomber
// Generic Boss Items // Generic Boss Items
SPR_JETF, // Boss jet fumes SPR_JETF, // Boss jet fumes
@ -664,6 +668,7 @@ typedef enum sprite
// Game Indicators // Game Indicators
SPR_SCOR, // Score logo SPR_SCOR, // Score logo
SPR_DRWN, // Drowning Timer SPR_DRWN, // Drowning Timer
SPR_FLII, // AI flight indicator
SPR_LCKN, // Target SPR_LCKN, // Target
SPR_TTAG, // Tag Sign SPR_TTAG, // Tag Sign
SPR_GFLG, // Got Flag sign SPR_GFLG, // Got Flag sign
@ -743,7 +748,6 @@ typedef enum sprite
SPR_BOM3, // Boss Explosion 2 SPR_BOM3, // Boss Explosion 2
SPR_BOM4, // Underwater Explosion SPR_BOM4, // Underwater Explosion
SPR_BMNB, // Mine Explosion SPR_BMNB, // Mine Explosion
SPR_WDDB, // Wood Debris
// Crumbly rocks // Crumbly rocks
SPR_ROIA, SPR_ROIA,
@ -763,8 +767,10 @@ typedef enum sprite
SPR_ROIO, SPR_ROIO,
SPR_ROIP, SPR_ROIP,
// Bricks // Level debris
SPR_BRIC, SPR_GFZD, // GFZ debris
SPR_BRIC, // Bricks
SPR_WDDB, // Wood Debris
// Gravity Well Objects // Gravity Well Objects
SPR_GWLG, SPR_GWLG,
@ -1264,22 +1270,10 @@ typedef enum state
S_MINUS_BURST4, S_MINUS_BURST4,
S_MINUS_BURST5, S_MINUS_BURST5,
S_MINUS_POPUP, S_MINUS_POPUP,
S_MINUS_UPWARD1, S_MINUS_AERIAL1,
S_MINUS_UPWARD2, S_MINUS_AERIAL2,
S_MINUS_UPWARD3, S_MINUS_AERIAL3,
S_MINUS_UPWARD4, S_MINUS_AERIAL4,
S_MINUS_UPWARD5,
S_MINUS_UPWARD6,
S_MINUS_UPWARD7,
S_MINUS_UPWARD8,
S_MINUS_DOWNWARD1,
S_MINUS_DOWNWARD2,
S_MINUS_DOWNWARD3,
S_MINUS_DOWNWARD4,
S_MINUS_DOWNWARD5,
S_MINUS_DOWNWARD6,
S_MINUS_DOWNWARD7,
S_MINUS_DOWNWARD8,
// Minus dirt // Minus dirt
S_MINUSDIRT1, S_MINUSDIRT1,
@ -1355,6 +1349,26 @@ typedef enum state
S_PTERABYTE_SWOOPDOWN, S_PTERABYTE_SWOOPDOWN,
S_PTERABYTE_SWOOPUP, S_PTERABYTE_SWOOPUP,
// Dragonbomber
S_DRAGONBOMBER,
S_DRAGONWING1,
S_DRAGONWING2,
S_DRAGONWING3,
S_DRAGONWING4,
S_DRAGONTAIL_LOADED,
S_DRAGONTAIL_EMPTY,
S_DRAGONTAIL_EMPTYLOOP,
S_DRAGONTAIL_RELOAD,
S_DRAGONMINE,
S_DRAGONMINE_LAND1,
S_DRAGONMINE_LAND2,
S_DRAGONMINE_SLOWFLASH1,
S_DRAGONMINE_SLOWFLASH2,
S_DRAGONMINE_SLOWLOOP,
S_DRAGONMINE_FASTFLASH1,
S_DRAGONMINE_FASTFLASH2,
S_DRAGONMINE_FASTLOOP,
// Boss Explosion // Boss Explosion
S_BOSSEXPLODE, S_BOSSEXPLODE,
@ -1657,6 +1671,7 @@ typedef enum state
S_FSGNA, S_FSGNA,
S_FSGNB, S_FSGNB,
S_FSGNC, S_FSGNC,
S_FSGND,
// Black Eggman (Boss 7) // Black Eggman (Boss 7)
S_BLACKEGG_STND, S_BLACKEGG_STND,
@ -3454,6 +3469,8 @@ typedef enum state
S_FOUR2, S_FOUR2,
S_FIVE2, S_FIVE2,
S_FLIGHTINDICATOR,
S_LOCKON1, S_LOCKON1,
S_LOCKON2, S_LOCKON2,
S_LOCKON3, S_LOCKON3,
@ -3637,13 +3654,9 @@ typedef enum state
S_FIREFLOWER2, S_FIREFLOWER2,
S_FIREFLOWER3, S_FIREFLOWER3,
S_FIREFLOWER4, S_FIREFLOWER4,
S_FIREBALL1, S_FIREBALL,
S_FIREBALL2, S_FIREBALLTRAIL1,
S_FIREBALL3, S_FIREBALLTRAIL2,
S_FIREBALL4,
S_FIREBALLEXP1,
S_FIREBALLEXP2,
S_FIREBALLEXP3,
S_SHELL, S_SHELL,
S_PUMA_START1, S_PUMA_START1,
S_PUMA_START2, S_PUMA_START2,
@ -3812,6 +3825,9 @@ typedef enum state
S_POPHAT_SHOOT1, S_POPHAT_SHOOT1,
S_POPHAT_SHOOT2, S_POPHAT_SHOOT2,
S_POPHAT_SHOOT3, S_POPHAT_SHOOT3,
S_POPHAT_SHOOT4,
S_POPSHOT,
S_POPSHOT_TRAIL,
S_HIVEELEMENTAL_LOOK, S_HIVEELEMENTAL_LOOK,
S_HIVEELEMENTAL_PREPARE1, S_HIVEELEMENTAL_PREPARE1,
@ -3943,8 +3959,6 @@ typedef enum state
S_DUST3, S_DUST3,
S_DUST4, S_DUST4,
S_WOODDEBRIS,
S_ROCKSPAWN, S_ROCKSPAWN,
S_ROCKCRUMBLEA, S_ROCKCRUMBLEA,
@ -3964,8 +3978,10 @@ typedef enum state
S_ROCKCRUMBLEO, S_ROCKCRUMBLEO,
S_ROCKCRUMBLEP, S_ROCKCRUMBLEP,
// Bricks // Level debris
S_GFZDEBRIS,
S_BRICKDEBRIS, S_BRICKDEBRIS,
S_WOODDEBRIS,
#ifdef SEENAMES #ifdef SEENAMES
S_NAMECHECK, S_NAMECHECK,
@ -4049,6 +4065,10 @@ typedef enum mobj_type
MT_PTERABYTESPAWNER, // Pterabyte spawner MT_PTERABYTESPAWNER, // Pterabyte spawner
MT_PTERABYTEWAYPOINT, // Pterabyte waypoint MT_PTERABYTEWAYPOINT, // Pterabyte waypoint
MT_PTERABYTE, // Pterabyte MT_PTERABYTE, // Pterabyte
MT_DRAGONBOMBER, // Dragonbomber
MT_DRAGONWING, // Dragonbomber wing
MT_DRAGONTAIL, // Dragonbomber tail segment
MT_DRAGONMINE, // Dragonbomber mine
// Generic Boss Items // Generic Boss Items
MT_BOSSEXPLODE, MT_BOSSEXPLODE,
@ -4654,6 +4674,7 @@ typedef enum mobj_type
MT_BLUEGOOMBA, MT_BLUEGOOMBA,
MT_FIREFLOWER, MT_FIREFLOWER,
MT_FIREBALL, MT_FIREBALL,
MT_FIREBALLTRAIL,
MT_SHELL, MT_SHELL,
MT_PUMA, MT_PUMA,
MT_PUMATRAIL, MT_PUMATRAIL,
@ -4698,6 +4719,7 @@ typedef enum mobj_type
MT_PENGUINATOR, MT_PENGUINATOR,
MT_POPHAT, MT_POPHAT,
MT_POPSHOT, MT_POPSHOT,
MT_POPSHOT_TRAIL,
MT_HIVEELEMENTAL, MT_HIVEELEMENTAL,
MT_BUMBLEBORE, MT_BUMBLEBORE,
@ -4735,7 +4757,6 @@ typedef enum mobj_type
MT_EXPLODE, // Robot Explosion MT_EXPLODE, // Robot Explosion
MT_UWEXPLODE, // Underwater Explosion MT_UWEXPLODE, // Underwater Explosion
MT_DUST, MT_DUST,
MT_WOODDEBRIS,
MT_ROCKSPAWNER, MT_ROCKSPAWNER,
MT_FALLINGROCK, MT_FALLINGROCK,
MT_ROCKCRUMBLE1, MT_ROCKCRUMBLE1,
@ -4755,8 +4776,10 @@ typedef enum mobj_type
MT_ROCKCRUMBLE15, MT_ROCKCRUMBLE15,
MT_ROCKCRUMBLE16, MT_ROCKCRUMBLE16,
// Bricks // Level debris
MT_GFZDEBRIS,
MT_BRICKDEBRIS, MT_BRICKDEBRIS,
MT_WOODDEBRIS,
#ifdef SEENAMES #ifdef SEENAMES
MT_NAMECHECK, MT_NAMECHECK,

View File

@ -1165,6 +1165,17 @@ static int lib_pElementalFire(lua_State *L)
return 0; return 0;
} }
static int lib_pDoPlayerFinish(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
NOHUD
INLEVEL
if (!player)
return LUA_ErrInvalid(L, "player_t");
P_DoPlayerFinish(player);
return 0;
}
static int lib_pDoPlayerExit(lua_State *L) static int lib_pDoPlayerExit(lua_State *L)
{ {
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@ -2674,6 +2685,13 @@ static int lib_gSetCustomExitVars(lua_State *L)
return 0; return 0;
} }
static int lib_gEnoughPlayersFinished(lua_State *L)
{
INLEVEL
lua_pushboolean(L, G_EnoughPlayersFinished());
return 1;
}
static int lib_gExitLevel(lua_State *L) static int lib_gExitLevel(lua_State *L)
{ {
int n = lua_gettop(L); // Num arguments int n = lua_gettop(L); // Num arguments
@ -2869,6 +2887,7 @@ static luaL_Reg lib[] = {
{"P_DoBubbleBounce",lib_pDoBubbleBounce}, {"P_DoBubbleBounce",lib_pDoBubbleBounce},
{"P_BlackOw",lib_pBlackOw}, {"P_BlackOw",lib_pBlackOw},
{"P_ElementalFire",lib_pElementalFire}, {"P_ElementalFire",lib_pElementalFire},
{"P_DoPlayerFinish",lib_pDoPlayerFinish},
{"P_DoPlayerExit",lib_pDoPlayerExit}, {"P_DoPlayerExit",lib_pDoPlayerExit},
{"P_InstaThrust",lib_pInstaThrust}, {"P_InstaThrust",lib_pInstaThrust},
{"P_ReturnThrustX",lib_pReturnThrustX}, {"P_ReturnThrustX",lib_pReturnThrustX},
@ -2981,6 +3000,7 @@ static luaL_Reg lib[] = {
{"G_BuildMapName",lib_gBuildMapName}, {"G_BuildMapName",lib_gBuildMapName},
{"G_DoReborn",lib_gDoReborn}, {"G_DoReborn",lib_gDoReborn},
{"G_SetCustomExitVars",lib_gSetCustomExitVars}, {"G_SetCustomExitVars",lib_gSetCustomExitVars},
{"G_EnoughPlayersFinished",lib_gEnoughPlayersFinished},
{"G_ExitLevel",lib_gExitLevel}, {"G_ExitLevel",lib_gExitLevel},
{"G_IsSpecialStage",lib_gIsSpecialStage}, {"G_IsSpecialStage",lib_gIsSpecialStage},
{"G_GametypeUsesLives",lib_gGametypeUsesLives}, {"G_GametypeUsesLives",lib_gGametypeUsesLives},

View File

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

View File

@ -92,12 +92,14 @@ static const char *const patch_opt[] = {
enum hudhook { enum hudhook {
hudhook_game = 0, hudhook_game = 0,
hudhook_scores, hudhook_scores,
hudhook_title hudhook_title,
hudhook_titlecard
}; };
static const char *const hudhook_opt[] = { static const char *const hudhook_opt[] = {
"game", "game",
"scores", "scores",
"title", "title",
"titlecard",
NULL}; NULL};
// alignment types for v.drawString // alignment types for v.drawString
@ -910,9 +912,17 @@ static int libd_RandomChance(lua_State *L)
return 1; return 1;
} }
// 30/10/18 Lat': Get cv_translucenthud's value for HUD rendering as a normal V_xxTRANS int // 30/10/18 Lat': Get st_translucency's value for HUD rendering as a normal V_xxTRANS int
// Could as well be thrown in global vars for ease of access but I guess it makes sense for it to be a HUD fn // Could as well be thrown in global vars for ease of access but I guess it makes sense for it to be a HUD fn
static int libd_getlocaltransflag(lua_State *L) static int libd_getlocaltransflag(lua_State *L)
{
HUDONLY
lua_pushinteger(L, (10-st_translucency)*V_10TRANS);
return 1;
}
// Get cv_translucenthud's value for HUD rendering as a normal V_xxTRANS int
static int libd_getusertransflag(lua_State *L)
{ {
HUDONLY HUDONLY
lua_pushinteger(L, (10-cv_translucenthud.value)*V_10TRANS); // A bit weird that it's called "translucenthud" yet 10 is fully opaque :V lua_pushinteger(L, (10-cv_translucenthud.value)*V_10TRANS); // A bit weird that it's called "translucenthud" yet 10 is fully opaque :V
@ -954,6 +964,7 @@ static luaL_Reg lib_draw[] = {
{"dupy", libd_dupy}, {"dupy", libd_dupy},
{"renderer", libd_renderer}, {"renderer", libd_renderer},
{"localTransFlag", libd_getlocaltransflag}, {"localTransFlag", libd_getlocaltransflag},
{"userTransFlag", libd_getusertransflag},
{NULL, NULL} {NULL, NULL}
}; };
@ -1043,6 +1054,9 @@ int LUA_HudLib(lua_State *L)
lua_newtable(L); lua_newtable(L);
lua_rawseti(L, -2, 4); // HUD[3] = title rendering functions array lua_rawseti(L, -2, 4); // HUD[3] = title rendering functions array
lua_newtable(L);
lua_rawseti(L, -2, 5); // HUD[4] = title card rendering functions array
lua_setfield(L, LUA_REGISTRYINDEX, "HUD"); lua_setfield(L, LUA_REGISTRYINDEX, "HUD");
luaL_newmetatable(L, META_HUDINFO); luaL_newmetatable(L, META_HUDINFO);
@ -1180,4 +1194,38 @@ void LUAh_TitleHUD(void)
hud_running = false; hud_running = false;
} }
void LUAh_TitleCardHUD(player_t *stplayr)
{
if (!gL || !(hudAvailable & (1<<hudhook_titlecard)))
return;
hud_running = true;
lua_pop(gL, -1);
lua_getfield(gL, LUA_REGISTRYINDEX, "HUD");
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -1, 5); // HUD[5] = rendering funcs
I_Assert(lua_istable(gL, -1));
lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw
I_Assert(lua_istable(gL, -1));
lua_remove(gL, -3); // pop HUD
LUA_PushUserdata(gL, stplayr, META_PLAYER);
lua_pushinteger(gL, lt_ticker);
lua_pushinteger(gL, (lt_endtime + TICRATE));
lua_pushnil(gL);
while (lua_next(gL, -6) != 0) {
lua_pushvalue(gL, -6); // graphics library (HUD[1])
lua_pushvalue(gL, -6); // stplayr
lua_pushvalue(gL, -6); // lt_ticker
lua_pushvalue(gL, -6); // lt_endtime
LUA_Call(gL, 4);
}
lua_pop(gL, -1);
hud_running = false;
}
#endif #endif

View File

@ -744,7 +744,7 @@ static int power_get(lua_State *L)
UINT16 *powers = *((UINT16 **)luaL_checkudata(L, 1, META_POWERS)); UINT16 *powers = *((UINT16 **)luaL_checkudata(L, 1, META_POWERS));
powertype_t p = luaL_checkinteger(L, 2); powertype_t p = luaL_checkinteger(L, 2);
if (p >= NUMPOWERS) if (p >= NUMPOWERS)
return luaL_error(L, LUA_QL("powertype_t") " cannot be %u", p); return luaL_error(L, LUA_QL("powertype_t") " cannot be %d", (INT16)p);
lua_pushinteger(L, powers[p]); lua_pushinteger(L, powers[p]);
return 1; return 1;
} }
@ -756,7 +756,7 @@ static int power_set(lua_State *L)
powertype_t p = luaL_checkinteger(L, 2); powertype_t p = luaL_checkinteger(L, 2);
UINT16 i = (UINT16)luaL_checkinteger(L, 3); UINT16 i = (UINT16)luaL_checkinteger(L, 3);
if (p >= NUMPOWERS) if (p >= NUMPOWERS)
return luaL_error(L, LUA_QL("powertype_t") " cannot be %u", p); return luaL_error(L, LUA_QL("powertype_t") " cannot be %d", (INT16)p);
if (hud_running) if (hud_running)
return luaL_error(L, "Do not alter player_t in HUD rendering code!"); return luaL_error(L, "Do not alter player_t in HUD rendering code!");
powers[p] = i; powers[p] = i;

View File

@ -16,6 +16,7 @@
#include "doomdef.h" #include "doomdef.h"
#include "command.h" #include "command.h"
#include "m_argv.h" #include "m_argv.h"
#include "m_misc.h"
/** \brief number of arg /** \brief number of arg
*/ */
@ -161,7 +162,7 @@ void M_FindResponseFile(void)
if (!file) if (!file)
I_Error("No more free memory for the response file"); I_Error("No more free memory for the response file");
if (fread(file, size, 1, handle) != 1) if (fread(file, size, 1, handle) != 1)
I_Error("Couldn't read response file because %s", strerror(ferror(handle))); I_Error("Couldn't read response file because %s", M_FileError(handle));
fclose(handle); fclose(handle);
// keep all the command line arguments following @responsefile // keep all the command line arguments following @responsefile

View File

@ -284,6 +284,8 @@ void M_SilentUpdateUnlockablesAndEmblems(void)
continue; continue;
unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1); unlockables[i].unlocked = M_Achieved(unlockables[i].conditionset - 1);
} }
players[consoleplayer].availabilities = players[1].availabilities = R_GetSkinAvailabilities(); // players[1] is supposed to be for 2p
} }
// Emblem unlocking shit // Emblem unlocking shit

View File

@ -201,14 +201,7 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedDiv(fixed_t a, fixed_t b)
*/ */
FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedRem(fixed_t x, fixed_t y) FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedRem(fixed_t x, fixed_t y)
{ {
const boolean n = x < 0; return x % y;
x = abs(x);
while (x >= y)
x -= y;
if (n)
return -x;
else
return x;
} }
/** \brief The FixedSqrt function /** \brief The FixedSqrt function

View File

@ -312,9 +312,7 @@ menu_t OP_VideoOptionsDef, OP_VideoModeDef, OP_ColorOptionsDef;
menu_t OP_OpenGLOptionsDef, OP_OpenGLFogDef, OP_OpenGLColorDef; menu_t OP_OpenGLOptionsDef, OP_OpenGLFogDef, OP_OpenGLColorDef;
#endif #endif
menu_t OP_SoundOptionsDef; menu_t OP_SoundOptionsDef;
#ifdef HAVE_MIXERX
menu_t OP_SoundAdvancedDef; menu_t OP_SoundAdvancedDef;
#endif
//Misc //Misc
menu_t OP_DataOptionsDef, OP_ScreenshotOptionsDef, OP_EraseDataDef; menu_t OP_DataOptionsDef, OP_ScreenshotOptionsDef, OP_EraseDataDef;
@ -474,25 +472,25 @@ static consvar_t cv_dummymares = {"dummymares", "Overall", CV_HIDEN|CV_CALL, dum
// --------- // ---------
static menuitem_t MainMenu[] = static menuitem_t MainMenu[] =
{ {
{IT_STRING|IT_CALL, NULL, "Secrets", M_SecretsMenu, 76}, {IT_STRING|IT_CALL, NULL, "1 Player", M_SinglePlayerMenu, 76},
{IT_STRING|IT_CALL, NULL, "1 player", M_SinglePlayerMenu, 84},
#ifndef NONET #ifndef NONET
{IT_STRING|IT_SUBMENU, NULL, "multiplayer", &MP_MainDef, 92}, {IT_STRING|IT_SUBMENU, NULL, "Multiplayer", &MP_MainDef, 84},
#else #else
{IT_STRING|IT_CALL, NULL, "multiplayer", M_StartSplitServerMenu, 92}, {IT_STRING|IT_CALL, NULL, "Multiplayer", M_StartSplitServerMenu, 84},
#endif #endif
{IT_STRING|IT_CALL, NULL, "options", M_Options, 100}, {IT_STRING|IT_CALL, NULL, "Extras", M_SecretsMenu, 92},
{IT_CALL |IT_STRING, NULL, "addons", M_Addons, 108}, {IT_CALL |IT_STRING, NULL, "Addons", M_Addons, 100},
{IT_STRING|IT_CALL, NULL, "quit game", M_QuitSRB2, 116}, {IT_STRING|IT_CALL, NULL, "Options", M_Options, 108},
{IT_STRING|IT_CALL, NULL, "Quit Game", M_QuitSRB2, 116},
}; };
typedef enum typedef enum
{ {
secrets = 0, singleplr = 0,
singleplr,
multiplr, multiplr,
options, secrets,
addons, addons,
options,
quitdoom quitdoom
} main_e; } main_e;
@ -661,7 +659,7 @@ static menuitem_t SR_PandorasBox[] =
// Sky Room Custom Unlocks // Sky Room Custom Unlocks
static menuitem_t SR_MainMenu[] = static menuitem_t SR_MainMenu[] =
{ {
{IT_STRING|IT_SUBMENU,NULL, "Secrets Checklist", &SR_UnlockChecklistDef, 0}, {IT_STRING|IT_SUBMENU,NULL, "Extras Checklist", &SR_UnlockChecklistDef, 0},
{IT_DISABLED, NULL, "", NULL, 0}, // Custom1 {IT_DISABLED, NULL, "", NULL, 0}, // Custom1
{IT_DISABLED, NULL, "", NULL, 0}, // Custom2 {IT_DISABLED, NULL, "", NULL, 0}, // Custom2
{IT_DISABLED, NULL, "", NULL, 0}, // Custom3 {IT_DISABLED, NULL, "", NULL, 0}, // Custom3
@ -726,19 +724,19 @@ static menuitem_t SR_EmblemHintMenu[] =
// Single Player Main // Single Player Main
static menuitem_t SP_MainMenu[] = static menuitem_t SP_MainMenu[] =
{ {
{IT_CALL | IT_STRING, NULL, "Tutorial", M_StartTutorial, 84}, {IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 84},
{IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 92}, {IT_SECRET, NULL, "Record Attack", M_TimeAttack, 92},
{IT_SECRET, NULL, "Record Attack", M_TimeAttack, 100}, {IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 100},
{IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 108}, {IT_CALL | IT_STRING, NULL, "Tutorial", M_StartTutorial, 108},
{IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 116}, {IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 116}
}; };
enum enum
{ {
sptutorial,
sploadgame, sploadgame,
sprecordattack, sprecordattack,
spnightsmode, spnightsmode,
sptutorial,
spstatistics spstatistics
}; };
@ -1224,32 +1222,33 @@ static menuitem_t OP_VideoOptionsMenu[] =
NULL, "HUD Transparency", &cv_translucenthud, 66}, NULL, "HUD Transparency", &cv_translucenthud, 66},
{IT_STRING | IT_CVAR, NULL, "Score/Time/Rings", &cv_timetic, 71}, {IT_STRING | IT_CVAR, NULL, "Score/Time/Rings", &cv_timetic, 71},
{IT_STRING | IT_CVAR, NULL, "Show Powerups", &cv_powerupdisplay, 76}, {IT_STRING | IT_CVAR, NULL, "Show Powerups", &cv_powerupdisplay, 76},
{IT_STRING | IT_CVAR, NULL, "Local ping display", &cv_showping, 81}, // shows ping next to framerate if we want to.
#ifdef SEENAMES #ifdef SEENAMES
{IT_STRING | IT_CVAR, NULL, "Show player names", &cv_seenames, 81}, {IT_STRING | IT_CVAR, NULL, "Show player names", &cv_seenames, 86},
#endif #endif
{IT_HEADER, NULL, "Console", NULL, 90}, {IT_HEADER, NULL, "Console", NULL, 95},
{IT_STRING | IT_CVAR, NULL, "Background color", &cons_backcolor, 96}, {IT_STRING | IT_CVAR, NULL, "Background color", &cons_backcolor, 101},
{IT_STRING | IT_CVAR, NULL, "Text Size", &cv_constextsize, 101}, {IT_STRING | IT_CVAR, NULL, "Text Size", &cv_constextsize, 106},
{IT_HEADER, NULL, "Chat", NULL, 110}, {IT_HEADER, NULL, "Chat", NULL, 115},
{IT_STRING | IT_CVAR, NULL, "Chat Mode", &cv_consolechat, 116}, {IT_STRING | IT_CVAR, NULL, "Chat Mode", &cv_consolechat, 121},
{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Width", &cv_chatwidth, 121}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Width", &cv_chatwidth, 126},
{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Height", &cv_chatheight, 126}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Height", &cv_chatheight, 131},
{IT_STRING | IT_CVAR, NULL, "Message Fadeout Time", &cv_chattime, 131}, {IT_STRING | IT_CVAR, NULL, "Message Fadeout Time", &cv_chattime, 136},
{IT_STRING | IT_CVAR, NULL, "Chat Notifications", &cv_chatnotifications, 136}, {IT_STRING | IT_CVAR, NULL, "Chat Notifications", &cv_chatnotifications, 141},
{IT_STRING | IT_CVAR, NULL, "Spam Protection", &cv_chatspamprotection, 141}, {IT_STRING | IT_CVAR, NULL, "Spam Protection", &cv_chatspamprotection, 146},
{IT_STRING | IT_CVAR, NULL, "Chat background tint", &cv_chatbacktint, 146}, {IT_STRING | IT_CVAR, NULL, "Chat background tint", &cv_chatbacktint, 151},
{IT_HEADER, NULL, "Level", NULL, 155}, {IT_HEADER, NULL, "Level", NULL, 160},
{IT_STRING | IT_CVAR, NULL, "Draw Distance", &cv_drawdist, 161}, {IT_STRING | IT_CVAR, NULL, "Draw Distance", &cv_drawdist, 166},
{IT_STRING | IT_CVAR, NULL, "Weather Draw Dist.", &cv_drawdist_precip, 166}, {IT_STRING | IT_CVAR, NULL, "Weather Draw Dist.", &cv_drawdist_precip, 171},
{IT_STRING | IT_CVAR, NULL, "NiGHTS Hoop Draw Dist.", &cv_drawdist_nights, 171}, {IT_STRING | IT_CVAR, NULL, "NiGHTS Hoop Draw Dist.", &cv_drawdist_nights, 176},
{IT_HEADER, NULL, "Diagnostic", NULL, 180}, {IT_HEADER, NULL, "Diagnostic", NULL, 184},
{IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 186}, {IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 190},
{IT_STRING | IT_CVAR, NULL, "Clear Before Redraw", &cv_homremoval, 191}, {IT_STRING | IT_CVAR, NULL, "Clear Before Redraw", &cv_homremoval, 195},
{IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 196}, {IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 200},
}; };
static menuitem_t OP_VideoModeMenu[] = static menuitem_t OP_VideoModeMenu[] =
@ -1347,29 +1346,22 @@ static menuitem_t OP_OpenGLColorMenu[] =
static menuitem_t OP_SoundOptionsMenu[] = static menuitem_t OP_SoundOptionsMenu[] =
{ {
{IT_HEADER, NULL, "Game Audio", NULL, 0}, {IT_HEADER, NULL, "Game Audio", NULL, 0},
{IT_STRING | IT_CVAR, NULL, "Sound Effects", &cv_gamesounds, 6}, {IT_STRING | IT_CVAR, NULL, "Sound Effects", &cv_gamesounds, 12},
{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Sound Volume", &cv_soundvolume, 11}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Sound Volume", &cv_soundvolume, 22},
{IT_STRING | IT_CVAR, NULL, "Digital Music", &cv_gamedigimusic, 21}, {IT_STRING | IT_CVAR, NULL, "Digital Music", &cv_gamedigimusic, 42},
{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Digital Music Volume", &cv_digmusicvolume, 26}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Digital Music Volume", &cv_digmusicvolume, 52},
{IT_STRING | IT_CVAR, NULL, "MIDI Music", &cv_gamemidimusic, 36}, {IT_STRING | IT_CVAR, NULL, "MIDI Music", &cv_gamemidimusic, 72},
{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "MIDI Music Volume", &cv_midimusicvolume, 41}, {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "MIDI Music Volume", &cv_midimusicvolume, 82},
{IT_HEADER, NULL, "Accessibility", NULL, 50}, {IT_HEADER, NULL, "Miscellaneous", NULL, 102},
{IT_STRING | IT_CVAR, NULL, "Closed Captioning", &cv_closedcaptioning, 56}, {IT_STRING | IT_CVAR, NULL, "Closed Captioning", &cv_closedcaptioning, 114},
{IT_STRING | IT_CVAR, NULL, "Reset Music Upon Dying", &cv_resetmusic, 61}, {IT_STRING | IT_CVAR, NULL, "Reset Music Upon Dying", &cv_resetmusic, 124},
{IT_STRING | IT_CVAR, NULL, "Play Sound Effects if Unfocused", &cv_playsoundsifunfocused, 71}, {IT_STRING | IT_SUBMENU, NULL, "Advanced Settings...", &OP_SoundAdvancedDef, 144},
{IT_STRING | IT_CVAR, NULL, "Play Music if Unfocused", &cv_playmusicifunfocused, 76},
#ifdef HAVE_MIXERX
{IT_STRING | IT_SUBMENU, NULL, "Advanced Settings...", &OP_SoundAdvancedDef, 94},
#endif
}; };
#ifdef HAVE_MIXERX
#ifdef HAVE_OPENMPT #ifdef HAVE_OPENMPT
#define OPENMPT_MENUOFFSET 32 #define OPENMPT_MENUOFFSET 32
#else #else
@ -1385,24 +1377,25 @@ static menuitem_t OP_SoundOptionsMenu[] =
static menuitem_t OP_SoundAdvancedMenu[] = static menuitem_t OP_SoundAdvancedMenu[] =
{ {
#ifdef HAVE_OPENMPT #ifdef HAVE_OPENMPT
{IT_HEADER, NULL, "OpenMPT Settings", NULL, 10}, {IT_HEADER, NULL, "OpenMPT Settings", NULL, 0},
{IT_STRING | IT_CVAR, NULL, "Instrument Filter", &cv_modfilter, 22}, {IT_STRING | IT_CVAR, NULL, "Instrument Filter", &cv_modfilter, 12},
#endif #endif
#ifdef HAVE_MIXERX #ifdef HAVE_MIXERX
{IT_HEADER, NULL, "MIDI Settings", NULL, OPENMPT_MENUOFFSET+10}, {IT_HEADER, NULL, "MIDI Settings", NULL, OPENMPT_MENUOFFSET},
{IT_STRING | IT_CVAR, NULL, "MIDI Player", &cv_midiplayer, OPENMPT_MENUOFFSET+22}, {IT_STRING | IT_CVAR, NULL, "MIDI Player", &cv_midiplayer, OPENMPT_MENUOFFSET+12},
{IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "FluidSynth Sound Font File", &cv_midisoundfontpath, OPENMPT_MENUOFFSET+34}, {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "FluidSynth Sound Font File", &cv_midisoundfontpath, OPENMPT_MENUOFFSET+24},
{IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "TiMidity++ Config Folder", &cv_miditimiditypath, OPENMPT_MENUOFFSET+61}, {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "TiMidity++ Config Folder", &cv_miditimiditypath, OPENMPT_MENUOFFSET+51},
#endif #endif
{IT_HEADER, NULL, "Miscellaneous", NULL, OPENMPT_MENUOFFSET+MIXERX_MENUOFFSET+10}, {IT_HEADER, NULL, "Miscellaneous", NULL, OPENMPT_MENUOFFSET+MIXERX_MENUOFFSET},
{IT_STRING | IT_CVAR, NULL, "Let Levels Force Reset Music", &cv_resetmusicbyheader, OPENMPT_MENUOFFSET+MIXERX_MENUOFFSET+22}, {IT_STRING | IT_CVAR, NULL, "Play Sound Effects if Unfocused", &cv_playsoundsifunfocused, OPENMPT_MENUOFFSET+MIXERX_MENUOFFSET+12},
{IT_STRING | IT_CVAR, NULL, "Play Music if Unfocused", &cv_playmusicifunfocused, OPENMPT_MENUOFFSET+MIXERX_MENUOFFSET+22},
{IT_STRING | IT_CVAR, NULL, "Let Levels Force Reset Music", &cv_resetmusicbyheader, OPENMPT_MENUOFFSET+MIXERX_MENUOFFSET+32},
}; };
#undef OPENMPT_MENUOFFSET #undef OPENMPT_MENUOFFSET
#undef MIXERX_MENUOFFSET #undef MIXERX_MENUOFFSET
#endif
static menuitem_t OP_DataOptionsMenu[] = static menuitem_t OP_DataOptionsMenu[] =
{ {
@ -1454,7 +1447,7 @@ enum
static menuitem_t OP_EraseDataMenu[] = static menuitem_t OP_EraseDataMenu[] =
{ {
{IT_STRING | IT_CALL, NULL, "Erase Record Data", M_EraseData, 10}, {IT_STRING | IT_CALL, NULL, "Erase Record Data", M_EraseData, 10},
{IT_STRING | IT_CALL, NULL, "Erase Secrets Data", M_EraseData, 20}, {IT_STRING | IT_CALL, NULL, "Erase Extras Data", M_EraseData, 20},
{IT_STRING | IT_CALL, NULL, "\x85" "Erase ALL Data", M_EraseData, 40}, {IT_STRING | IT_CALL, NULL, "\x85" "Erase ALL Data", M_EraseData, 40},
}; };
@ -1665,7 +1658,7 @@ menu_t SP_MainDef = //CENTERMENUSTYLE(NULL, SP_MainMenu, &MainDef, 72);
SP_MainMenu, SP_MainMenu,
M_DrawCenteredMenu, M_DrawCenteredMenu,
BASEVIDWIDTH/2, 72, BASEVIDWIDTH/2, 72,
1, // start at "Start Game" on first entry 0,
NULL NULL
}; };
@ -1991,12 +1984,10 @@ menu_t OP_ColorOptionsDef =
0, 0,
NULL NULL
}; };
menu_t OP_SoundOptionsDef = DEFAULTSCROLLMENUSTYLE( menu_t OP_SoundOptionsDef = DEFAULTMENUSTYLE(
MN_OP_MAIN + (MN_OP_SOUND << 6), MN_OP_MAIN + (MN_OP_SOUND << 6),
"M_SOUND", OP_SoundOptionsMenu, &OP_MainDef, 30, 30); "M_SOUND", OP_SoundOptionsMenu, &OP_MainDef, 30, 30);
#ifdef HAVE_MIXERX
menu_t OP_SoundAdvancedDef = DEFAULTMENUSTYLE(MN_OP_MAIN + (MN_OP_SOUND << 6), "M_SOUND", OP_SoundAdvancedMenu, &OP_SoundOptionsDef, 30, 30); menu_t OP_SoundAdvancedDef = DEFAULTMENUSTYLE(MN_OP_MAIN + (MN_OP_SOUND << 6), "M_SOUND", OP_SoundAdvancedMenu, &OP_SoundOptionsDef, 30, 30);
#endif
menu_t OP_ServerOptionsDef = DEFAULTSCROLLMENUSTYLE( menu_t OP_ServerOptionsDef = DEFAULTSCROLLMENUSTYLE(
MN_OP_MAIN + (MN_OP_SERVER << 6), MN_OP_MAIN + (MN_OP_SERVER << 6),
@ -3363,13 +3354,15 @@ boolean M_Responder(event_t *ev)
// //
void M_Drawer(void) void M_Drawer(void)
{ {
boolean wipe = WipeInAction;
if (currentMenu == &MessageDef) if (currentMenu == &MessageDef)
menuactive = true; menuactive = true;
if (menuactive) if (menuactive)
{ {
// now that's more readable with a faded background (yeah like Quake...) // now that's more readable with a faded background (yeah like Quake...)
if (!WipeInAction && (curfadevalue || (gamestate != GS_TITLESCREEN && gamestate != GS_TIMEATTACK))) if (!wipe && (curfadevalue || (gamestate != GS_TITLESCREEN && gamestate != GS_TIMEATTACK)))
V_DrawFadeScreen(0xFF00, (gamestate != GS_TITLESCREEN && gamestate != GS_TIMEATTACK) ? 16 : curfadevalue); V_DrawFadeScreen(0xFF00, (gamestate != GS_TITLESCREEN && gamestate != GS_TIMEATTACK) ? 16 : curfadevalue);
if (currentMenu->drawroutine) if (currentMenu->drawroutine)
@ -3431,6 +3424,8 @@ void M_StartControlPanel(void)
if (!Playing()) if (!Playing())
{ {
// Secret menu! // Secret menu!
MainMenu[singleplr].alphaKey = (M_AnySecretUnlocked()) ? 76 : 84;
MainMenu[multiplr].alphaKey = (M_AnySecretUnlocked()) ? 84 : 92;
MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED); MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
currentMenu = &MainDef; currentMenu = &MainDef;
@ -3532,6 +3527,7 @@ void M_StartControlPanel(void)
void M_EndModeAttackRun(void) void M_EndModeAttackRun(void)
{ {
G_ClearModeAttackRetryFlag();
M_ModeAttackEndGame(0); M_ModeAttackEndGame(0);
} }
@ -6692,7 +6688,7 @@ static void M_DrawChecklist(void)
|| !unlockables[i].conditionset || unlockables[i].conditionset > MAXCONDITIONSETS) || !unlockables[i].conditionset || unlockables[i].conditionset > MAXCONDITIONSETS)
continue; continue;
V_DrawString(currentMenu->x, y, ((unlockables[i].unlocked) ? V_GREENMAP : V_TRANSLUCENT), ((unlockables[i].unlocked || !unlockables[i].nochecklist) ? unlockables[i].name : M_CreateSecretMenuOption(unlockables[i].name))); V_DrawString(currentMenu->x, y, ((unlockables[i].unlocked) ? V_GREENMAP : V_TRANSLUCENT)|V_ALLOWLOWERCASE, ((unlockables[i].unlocked || !unlockables[i].nochecklist) ? unlockables[i].name : M_CreateSecretMenuOption(unlockables[i].name)));
for (j = i+1; j < MAXUNLOCKABLES; j++) for (j = i+1; j < MAXUNLOCKABLES; j++)
{ {
@ -7175,7 +7171,7 @@ static void M_DrawSoundTest(void)
titl = va("%s - ", curplaying->title); titl = va("%s - ", curplaying->title);
} }
else else
titl = "NONE - "; titl = "None - ";
i = V_LevelNameWidth(titl); i = V_LevelNameWidth(titl);
@ -7189,7 +7185,7 @@ static void M_DrawSoundTest(void)
while (x > y) while (x > y)
{ {
x -= i; x -= i;
V_DrawLevelTitle(x, 24, 0, titl); V_DrawLevelTitle(x, 22, 0, titl);
} }
if (curplaying) if (curplaying)
@ -8338,16 +8334,12 @@ static void M_SetupChoosePlayer(INT32 choice)
{ {
INT32 skinnum; INT32 skinnum;
UINT8 i; UINT8 i;
UINT8 firstvalid = 255; UINT8 firstvalid = 255, lastvalid = 255;
UINT8 lastvalid = 0;
boolean allowed = false; boolean allowed = false;
char *and; char *and;
(void)choice; (void)choice;
if (!(mapheaderinfo[startmap-1] if (!mapheaderinfo[startmap-1] || mapheaderinfo[startmap-1]->forcecharacter[0] == '\0')
&& (mapheaderinfo[startmap-1]->forcecharacter[0] != '\0'
|| (mapheaderinfo[startmap-1]->typeoflevel & TOL_NIGHTS)) // remove this later when everyone gets their own nights sprites, maybe
))
{ {
for (i = 0; i < 32; i++) // Handle charsels, availability, and unlocks. for (i = 0; i < 32; i++) // Handle charsels, availability, and unlocks.
{ {
@ -8357,6 +8349,8 @@ static void M_SetupChoosePlayer(INT32 choice)
if (and) if (and)
{ {
char firstskin[SKINNAMESIZE+1]; char firstskin[SKINNAMESIZE+1];
if (mapheaderinfo[startmap-1]->typeoflevel & TOL_NIGHTS) // skip tagteam characters for NiGHTS levels
continue;
strncpy(firstskin, description[i].skinname, (and - description[i].skinname)); strncpy(firstskin, description[i].skinname, (and - description[i].skinname));
firstskin[(and - description[i].skinname)] = '\0'; firstskin[(and - description[i].skinname)] = '\0';
description[i].skinnum[0] = R_SkinAvailable(firstskin); description[i].skinnum[0] = R_SkinAvailable(firstskin);
@ -8385,7 +8379,7 @@ static void M_SetupChoosePlayer(INT32 choice)
if (!(description[i].picname[0])) if (!(description[i].picname[0]))
{ {
if (skins[skinnum].sprites[SPR2_XTRA].numframes >= XTRA_CHARSEL+1) if (skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_CHARSEL)
{ {
spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA]; spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA];
spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CHARSEL]; spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CHARSEL];
@ -8410,17 +8404,16 @@ static void M_SetupChoosePlayer(INT32 choice)
} }
} }
if (firstvalid != 255) if (firstvalid == lastvalid) // We're being forced into a specific character, so might as well just skip it.
{ // One last bit of order we can't do in the iteration above.
description[firstvalid].prev = lastvalid;
description[lastvalid].next = firstvalid;
}
else // We're being forced into a specific character, so might as well just skip it.
{ {
M_ChoosePlayer(-1); M_ChoosePlayer(firstvalid);
return; return;
} }
// One last bit of order we can't do in the iteration above.
description[firstvalid].prev = lastvalid;
description[lastvalid].next = firstvalid;
M_ChangeMenuMusic("_chsel", true); M_ChangeMenuMusic("_chsel", true);
/* the menus suck -James */ /* the menus suck -James */
@ -8747,7 +8740,7 @@ static void M_ChoosePlayer(INT32 choice)
UINT8 skinnum; UINT8 skinnum;
// skip this if forcecharacter or no characters available // skip this if forcecharacter or no characters available
if (choice == -1) if (choice == 255)
{ {
skinnum = botskin = 0; skinnum = botskin = 0;
botingame = false; botingame = false;
@ -8859,9 +8852,9 @@ static void M_DrawStatsMaps(int location)
M_DrawMapEmblems(mnum+1, 292, y); M_DrawMapEmblems(mnum+1, 292, y);
if (mapheaderinfo[mnum]->actnum != 0) if (mapheaderinfo[mnum]->actnum != 0)
V_DrawString(20, y, V_YELLOWMAP, va("%s %d", mapheaderinfo[mnum]->lvlttl, mapheaderinfo[mnum]->actnum)); V_DrawString(20, y, V_YELLOWMAP|V_ALLOWLOWERCASE, va("%s %d", mapheaderinfo[mnum]->lvlttl, mapheaderinfo[mnum]->actnum));
else else
V_DrawString(20, y, V_YELLOWMAP, mapheaderinfo[mnum]->lvlttl); V_DrawString(20, y, V_YELLOWMAP|V_ALLOWLOWERCASE, mapheaderinfo[mnum]->lvlttl);
y += 8; y += 8;
@ -8905,7 +8898,7 @@ static void M_DrawStatsMaps(int location)
else else
V_DrawSmallScaledPatch(292, y, 0, W_CachePatchName("NEEDIT", PU_CACHE)); V_DrawSmallScaledPatch(292, y, 0, W_CachePatchName("NEEDIT", PU_CACHE));
V_DrawString(20, y, V_YELLOWMAP, va("%s", exemblem->description)); V_DrawString(20, y, V_YELLOWMAP|V_ALLOWLOWERCASE, va("%s", exemblem->description));
} }
y += 8; y += 8;
@ -9116,7 +9109,7 @@ void M_DrawTimeAttackMenu(void)
// Character face! // Character face!
{ {
if (skins[cv_chooseskin.value-1].sprites[SPR2_XTRA].numframes >= XTRA_CHARSEL+1) if (skins[cv_chooseskin.value-1].sprites[SPR2_XTRA].numframes > XTRA_CHARSEL)
{ {
spritedef_t *sprdef = &skins[cv_chooseskin.value-1].sprites[SPR2_XTRA]; spritedef_t *sprdef = &skins[cv_chooseskin.value-1].sprites[SPR2_XTRA];
spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CHARSEL]; spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CHARSEL];
@ -9240,10 +9233,7 @@ void M_DrawTimeAttackMenu(void)
V_DrawString(104-72, 73+lsheadingheight/2, V_YELLOWMAP, "RINGS:"); V_DrawString(104-72, 73+lsheadingheight/2, V_YELLOWMAP, "RINGS:");
if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->gotperfect) V_DrawRightAlignedString(104+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE|((mapvisited[cv_nextmap.value-1] & MV_PERFECT) ? V_YELLOWMAP : 0), beststr);
V_DrawRightAlignedString(104+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
else
V_DrawRightAlignedString(104+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE|V_YELLOWMAP, beststr);
V_DrawRightAlignedString(104+72, 83+lsheadingheight/2, V_ALLOWLOWERCASE, reqrings); V_DrawRightAlignedString(104+72, 83+lsheadingheight/2, V_ALLOWLOWERCASE, reqrings);
} }
@ -9395,6 +9385,7 @@ void M_DrawNightsAttackMenu(void)
{ {
emblem_t *em; emblem_t *em;
INT32 yHeight; INT32 yHeight;
INT32 xpos;
patch_t *PictureOfLevel; patch_t *PictureOfLevel;
lumpnum_t lumpnum; lumpnum_t lumpnum;
char beststr[40]; char beststr[40];
@ -9454,17 +9445,23 @@ void M_DrawNightsAttackMenu(void)
{ {
switch (em->type) switch (em->type)
{ {
case ET_NGRADE: yHeight = 48; break; case ET_NGRADE:
case ET_NTIME: yHeight = 68; break; xpos = 104+38;
yHeight = 48;
break;
case ET_NTIME:
xpos = 104+76;
yHeight = 68;
break;
default: default:
goto skipThisOne; goto skipThisOne;
} }
if (em->collected) if (em->collected)
V_DrawSmallMappedPatch(104+38, yHeight+lsheadingheight/2, 0, W_CachePatchName(M_GetEmblemPatch(em, false), PU_CACHE), V_DrawSmallMappedPatch(xpos, yHeight+lsheadingheight/2, 0, W_CachePatchName(M_GetEmblemPatch(em, false), PU_CACHE),
R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE)); R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
else else
V_DrawSmallScaledPatch(104+38, yHeight+lsheadingheight/2, 0, W_CachePatchName("NEEDIT", PU_CACHE)); V_DrawSmallScaledPatch(xpos, yHeight+lsheadingheight/2, 0, W_CachePatchName("NEEDIT", PU_CACHE));
skipThisOne: skipThisOne:
em = M_GetLevelEmblems(-1); em = M_GetLevelEmblems(-1);
@ -10905,7 +10902,7 @@ static void M_EraseData(INT32 choice)
if (choice == 0) if (choice == 0)
eschoice = M_GetText("Record Attack data"); eschoice = M_GetText("Record Attack data");
else if (choice == 1) else if (choice == 1)
eschoice = M_GetText("Secrets data"); eschoice = M_GetText("Extras data");
else else
eschoice = M_GetText("ALL game data"); eschoice = M_GetText("ALL game data");

View File

@ -99,7 +99,7 @@ typedef enum
MN_OP_SCREENSHOTS, MN_OP_SCREENSHOTS,
MN_OP_ERASEDATA, MN_OP_ERASEDATA,
// Secrets // Extras
MN_SR_MAIN, MN_SR_MAIN,
MN_SR_PANDORA, MN_SR_PANDORA,
MN_SR_LEVELSELECT, MN_SR_LEVELSELECT,

View File

@ -23,6 +23,8 @@
#include <unistd.h> #include <unistd.h>
#endif #endif
#include <errno.h>
// Extended map support. // Extended map support.
#include <ctype.h> #include <ctype.h>
@ -787,7 +789,7 @@ static void M_PNGText(png_structp png_ptr, png_infop png_info_ptr, PNG_CONST png
if (gamestate == GS_LEVEL && mapheaderinfo[gamemap-1]->lvlttl[0] != '\0') if (gamestate == GS_LEVEL && mapheaderinfo[gamemap-1]->lvlttl[0] != '\0')
snprintf(lvlttltext, 48, "%s%s%s", snprintf(lvlttltext, 48, "%s%s%s",
mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->lvlttl,
(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE", (mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " Zone",
(mapheaderinfo[gamemap-1]->actnum > 0) ? va(" %d",mapheaderinfo[gamemap-1]->actnum) : ""); (mapheaderinfo[gamemap-1]->actnum > 0) ? va(" %d",mapheaderinfo[gamemap-1]->actnum) : "");
else else
snprintf(lvlttltext, 48, "Unknown"); snprintf(lvlttltext, 48, "Unknown");
@ -2441,3 +2443,13 @@ void M_SetupMemcpy(void)
M_Memcpy = cpu_cpy; M_Memcpy = cpu_cpy;
#endif #endif
} }
/** Return the appropriate message for a file error or end of file.
*/
const char *M_FileError(FILE *fp)
{
if (ferror(fp))
return strerror(errno);
else
return "end-of-file";
}

View File

@ -94,6 +94,8 @@ void strcatbf(char *s1, const char *s2, const char *s3);
void M_SetupMemcpy(void); void M_SetupMemcpy(void);
const char *M_FileError(FILE *handle);
// counting bits, for weapon ammo code, usually // counting bits, for weapon ammo code, usually
FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size); FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size);

View File

@ -312,6 +312,9 @@ void A_SpawnPterabytes(mobj_t *actor);
void A_PterabyteHover(mobj_t *actor); void A_PterabyteHover(mobj_t *actor);
void A_RolloutSpawn(mobj_t *actor); void A_RolloutSpawn(mobj_t *actor);
void A_RolloutRock(mobj_t *actor); void A_RolloutRock(mobj_t *actor);
void A_DragonbomberSpawn(mobj_t *actor);
void A_DragonWing(mobj_t *actor);
void A_DragonSegment(mobj_t *actor);
//for p_enemy.c //for p_enemy.c
@ -2473,12 +2476,8 @@ void A_VultureBlast(mobj_t *actor)
void A_VultureFly(mobj_t *actor) void A_VultureFly(mobj_t *actor)
{ {
fixed_t speedmax = 18*FRACUNIT; fixed_t speedmax = 18*FRACUNIT;
angle_t angledif = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle; angle_t angledif;
fixed_t dx = actor->target->x - actor->x; fixed_t dx, dy, dz, dxy, dm;
fixed_t dy = actor->target->y - actor->y;
fixed_t dz = actor->target->z - actor->z;
fixed_t dxy = FixedHypot(dx, dy);
fixed_t dm;
mobj_t *dust; mobj_t *dust;
fixed_t momm; fixed_t momm;
@ -2487,6 +2486,18 @@ void A_VultureFly(mobj_t *actor)
return; return;
#endif #endif
if (!actor->target || P_MobjWasRemoved(actor->target))
{
P_SetMobjState(actor, actor->info->spawnstate);
return;
}
angledif = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle;
dx = actor->target->x - actor->x;
dy = actor->target->y - actor->y;
dz = actor->target->z - actor->z;
dxy = FixedHypot(dx, dy);
if (leveltime % 4 == 0) if (leveltime % 4 == 0)
S_StartSound(actor, actor->info->activesound); S_StartSound(actor, actor->info->activesound);
@ -4070,19 +4081,28 @@ bossjustdie:
mobj_t *pole = P_SpawnMobj( mobj_t *pole = P_SpawnMobj(
mo->tracer->x - P_ReturnThrustX(mo->tracer, mo->tracer->angle, speed*time), mo->tracer->x - P_ReturnThrustX(mo->tracer, mo->tracer->angle, speed*time),
mo->tracer->y - P_ReturnThrustY(mo->tracer, mo->tracer->angle, speed*time), mo->tracer->y - P_ReturnThrustY(mo->tracer, mo->tracer->angle, speed*time),
mo->tracer->floorz + 4*FRACUNIT, mo->tracer->floorz + (256+1)*FRACUNIT,
MT_FSGNB); MT_FSGNB);
P_SetTarget(&pole->tracer, P_SpawnMobj( P_SetTarget(&pole->tracer, P_SpawnMobj(
pole->x, pole->y,
pole->z - 256*FRACUNIT,
MT_FSGNB));
P_SetTarget(&pole->tracer->tracer, P_SpawnMobj(
pole->x + P_ReturnThrustX(pole, mo->tracer->angle, FRACUNIT), pole->x + P_ReturnThrustX(pole, mo->tracer->angle, FRACUNIT),
pole->y + P_ReturnThrustY(pole, mo->tracer->angle, FRACUNIT), pole->y + P_ReturnThrustY(pole, mo->tracer->angle, FRACUNIT),
pole->z + 256*FRACUNIT, pole->z + 256*FRACUNIT,
MT_FSGNA)); MT_FSGNA));
pole->angle = mo->tracer->angle; pole->tracer->flags |= MF_NOCLIPTHING;
pole->tracer->angle = pole->angle - ANGLE_90; P_SetScale(pole, (pole->destscale = 2*FRACUNIT));
P_SetScale(pole->tracer, (pole->tracer->destscale = 2*FRACUNIT));
pole->angle = pole->tracer->angle = mo->tracer->angle;
pole->tracer->tracer->angle = pole->angle - ANGLE_90;
pole->momx = P_ReturnThrustX(pole, pole->angle, speed); pole->momx = P_ReturnThrustX(pole, pole->angle, speed);
pole->momy = P_ReturnThrustY(pole, pole->angle, speed); pole->momy = P_ReturnThrustY(pole, pole->angle, speed);
pole->tracer->momx = pole->momx; pole->tracer->momx = pole->momx;
pole->tracer->momy = pole->momy; pole->tracer->momy = pole->momy;
pole->tracer->tracer->momx = pole->momx;
pole->tracer->tracer->momy = pole->momy;
} }
} }
else else
@ -5121,7 +5141,7 @@ void A_SignPlayer(mobj_t *actor)
return; return;
#endif #endif
if (actor->tracer == NULL || locvar1 < -3 || locvar1 >= numskins) if (actor->tracer == NULL || locvar1 < -3 || locvar1 >= numskins || signcolor >= MAXTRANSLATIONS)
return; return;
// if no face overlay, spawn one // if no face overlay, spawn one
@ -5148,26 +5168,9 @@ void A_SignPlayer(mobj_t *actor)
if (signcolor) if (signcolor)
; ;
else if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor? else if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor?
{
signcolor = skin->prefoppositecolor; signcolor = skin->prefoppositecolor;
/*
If you're here from the comment above Color_Opposite,
the following line is the one which is dependent on the
array being symmetrical. It gets the opposite of the
opposite of your desired colour just so it can get the
brightness frame for the End Sign. It's not a great
design choice, but it's constant time array access and
the idea that the colours should be OPPOSITES is kind
of in the name. If you have a better idea, feel free
to let me know. ~toast 2016/07/20
*/
signframe += (15 - Color_Opposite[Color_Opposite[skin->prefoppositecolor - 1][0] - 1][1]);
}
else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor. else if (actor->target->player->skincolor) // Set the sign to be an appropriate background color for this player's skincolor.
{
signcolor = Color_Opposite[actor->target->player->skincolor - 1][0]; signcolor = Color_Opposite[actor->target->player->skincolor - 1][0];
signframe += (15 - Color_Opposite[actor->target->player->skincolor - 1][1]);
}
else else
signcolor = SKINCOLOR_NONE; signcolor = SKINCOLOR_NONE;
} }
@ -5188,10 +5191,10 @@ void A_SignPlayer(mobj_t *actor)
skinnum = P_RandomKey(skincount); skinnum = P_RandomKey(skincount);
for (skincount = 0; skincount < numskins; skincount++) for (skincount = 0; skincount < numskins; skincount++)
{ {
if (skincheck(skincount))
skinnum++;
if (skincount > skinnum) if (skincount > skinnum)
break; break;
if (skincheck(skincount))
skinnum++;
} }
} }
else // otherwise, advance 1 skin else // otherwise, advance 1 skin
@ -5203,42 +5206,46 @@ void A_SignPlayer(mobj_t *actor)
skin = &skins[skinnum]; skin = &skins[skinnum];
} }
else // specific skin else // specific skin
{
skin = &skins[locvar1]; skin = &skins[locvar1];
}
facecolor = skin->prefcolor; facecolor = skin->prefcolor;
if (signcolor) if (signcolor)
; ;
else if (skin->prefoppositecolor) else if (skin->prefoppositecolor)
{
signcolor = skin->prefoppositecolor; signcolor = skin->prefoppositecolor;
} else if (facecolor)
else
{
signcolor = Color_Opposite[facecolor - 1][0]; signcolor = Color_Opposite[facecolor - 1][0];
}
signframe += (15 - Color_Opposite[Color_Opposite[signcolor - 1][0] - 1][1]);
} }
if (skin != NULL && skin->sprites[SPR2_SIGN].numframes) // player face if (skin && skin->sprites[SPR2_SIGN].numframes) // player face
{ {
ov->color = facecolor; ov->color = facecolor;
ov->skin = skin; ov->skin = skin;
P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN
actor->tracer->color = signcolor;
actor->tracer->frame = signframe;
} }
else // Eggman face else // Eggman face
{ {
ov->color = SKINCOLOR_NONE; ov->color = SKINCOLOR_NONE;
P_SetMobjState(ov, actor->info->meleestate); // S_EGGMANSIGN P_SetMobjState(ov, actor->info->meleestate); // S_EGGMANSIGN
if (signcolor) if (!signcolor)
actor->tracer->color = signcolor; signcolor = SKINCOLOR_CARBON;
else
actor->tracer->color = signcolor = SKINCOLOR_CARBON;
actor->tracer->frame = signframe += (15 - Color_Opposite[Color_Opposite[signcolor - 1][0] - 1][1]);
} }
actor->tracer->color = signcolor;
/*
If you're here from the comment above Color_Opposite,
the following line is the one which is dependent on the
array being symmetrical. It gets the opposite of the
opposite of your desired colour just so it can get the
brightness frame for the End Sign. It's not a great
design choice, but it's constant time array access and
the idea that the colours should be OPPOSITES is kind
of in the name. If you have a better idea, feel free
to let me know. ~toast 2016/07/20
*/
if (signcolor && signcolor < MAXSKINCOLORS)
signframe += (15 - Color_Opposite[Color_Opposite[signcolor - 1][0] - 1][1]);
actor->tracer->frame = signframe;
} }
// Function: A_OverlayThink // Function: A_OverlayThink
@ -5665,10 +5672,10 @@ void A_MinusPopup(mobj_t *actor)
S_StartSound(actor, sfx_s3k82); S_StartSound(actor, sfx_s3k82);
for (i = 1; i <= num; i++) for (i = 1; i <= num; i++)
{ {
mobj_t *rock = P_SpawnMobj(actor->x, actor->y, actor->z + actor->height/4, MT_ROCKCRUMBLE1); mobj_t *rock = P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_ROCKCRUMBLE1);
P_Thrust(rock, ani*i, FRACUNIT); P_Thrust(rock, ani*i, FRACUNIT);
rock->momz = 3*FRACUNIT; P_SetObjectMomZ(rock, 3*FRACUNIT, false);
P_SetScale(rock, FRACUNIT/3); P_SetScale(rock, rock->scale/3);
} }
P_RadiusAttack(actor, actor, 2*actor->radius, 0); P_RadiusAttack(actor, actor, 2*actor->radius, 0);
if (actor->tracer) if (actor->tracer)
@ -5682,11 +5689,12 @@ void A_MinusPopup(mobj_t *actor)
// Description: If the minus hits the floor, dig back into the ground. // Description: If the minus hits the floor, dig back into the ground.
// //
// var1 = State to switch to (if 0, use seestate). // var1 = State to switch to (if 0, use seestate).
// var2 = unused // var2 = If not 0, spawn debris when hitting the floor.
// //
void A_MinusCheck(mobj_t *actor) void A_MinusCheck(mobj_t *actor)
{ {
INT32 locvar1 = var1; INT32 locvar1 = var1;
INT32 locvar2 = var2;
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
if (LUA_CallAction("A_MinusCheck", actor)) if (LUA_CallAction("A_MinusCheck", actor))
@ -5697,6 +5705,18 @@ void A_MinusCheck(mobj_t *actor)
{ {
P_SetMobjState(actor, locvar1 ? (statenum_t)locvar1 : actor->info->seestate); P_SetMobjState(actor, locvar1 ? (statenum_t)locvar1 : actor->info->seestate);
actor->flags = actor->info->flags; actor->flags = actor->info->flags;
if (locvar2)
{
INT32 i, num = 6;
angle_t ani = FixedAngle(FRACUNIT*360/num);
for (i = 1; i <= num; i++)
{
mobj_t *rock = P_SpawnMobjFromMobj(actor, 0, 0, actor->height/4, MT_ROCKCRUMBLE1);
P_Thrust(rock, ani*i, FRACUNIT);
P_SetObjectMomZ(rock, 3*FRACUNIT, false);
P_SetScale(rock, rock->scale/3);
}
}
} }
} }
@ -14535,6 +14555,9 @@ void A_RolloutRock(mobj_t *actor)
actor->friction = FRACUNIT; // turns out riding on solids sucks, so let's just make it easier on ourselves actor->friction = FRACUNIT; // turns out riding on solids sucks, so let's just make it easier on ourselves
if (actor->eflags & MFE_JUSTHITFLOOR)
S_StartSound(actor, actor->info->painsound);
if (actor->threshold) if (actor->threshold)
actor->threshold--; actor->threshold--;
@ -14588,6 +14611,9 @@ void A_RolloutRock(mobj_t *actor)
actor->frame = actor->reactiontime % maxframes; // set frame actor->frame = actor->reactiontime % maxframes; // set frame
if (!actor->tracer || P_MobjWasRemoved(actor->tracer) || !actor->tracer->health)
actor->flags |= MF_PUSHABLE;
if (!(actor->flags & MF_PUSHABLE)) // if being ridden, don't disappear if (!(actor->flags & MF_PUSHABLE)) // if being ridden, don't disappear
actor->fuse = 0; actor->fuse = 0;
else if (!actor->fuse && actor->movecount == 1) // otherwise if rock has moved, set its fuse else if (!actor->fuse && actor->movecount == 1) // otherwise if rock has moved, set its fuse
@ -14597,3 +14623,97 @@ void A_RolloutRock(mobj_t *actor)
actor->flags2 ^= MF2_DONTDRAW; actor->flags2 ^= MF2_DONTDRAW;
} }
// Function: A_DragonbomberSpawn
//
// Description: Spawns the body parts for Dragonbomber
//
// var1 = Tail segments to spawn
// var2 = unused
//
void A_DragonbomberSpawn(mobj_t *actor)
{
UINT8 i;
mobj_t *mo = actor;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_DragonbomberSpawn", actor))
return;
#endif
for (i = 0; i < var1; i++) // spawn tail segments
{
mobj_t *segment;
fixed_t x, y;
x = P_ReturnThrustX(mo, mo->angle, -mo->radius << 1);
y = P_ReturnThrustY(mo, mo->angle, -mo->radius << 1);
segment = P_SpawnMobjFromMobj(mo, x, y, 0, MT_DRAGONTAIL);
P_SetTarget(&segment->target, mo);
P_SetTarget(&mo->tracer, segment);
segment->angle = mo->angle;
mo = segment;
}
for (i = 0; i < 2; i++) // spawn wings
{
mo = P_SpawnMobjFromMobj(actor, 0, 0, 0, MT_DRAGONWING);
P_SetTarget(&mo->target, actor);
mo->movedir = ANGLE_90 + i * ANGLE_180;
}
}
// Function: A_DragonWing
//
// Description: Moves actor such that it is placed away from its target at a distance equal to the target's radius in the direction of its target's angle.
// The actor's movedir can be used to offset the angle.
//
// var1 = unused
// var2 = unused
//
void A_DragonWing(mobj_t *actor)
{
mobj_t *target = actor->target;
fixed_t x, y;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_DragonWing", actor))
return;
#endif
if (target == NULL || !target->health)
{
P_RemoveMobj(actor);
return;
}
actor->angle = target->angle + actor->movedir;
x = target->x + P_ReturnThrustX(actor, actor->angle, -target->radius);
y = target->y + P_ReturnThrustY(actor, actor->angle, -target->radius);
P_TeleportMove(actor, x, y, target->z);
}
// Function: A_DragonSegment
//
// Description: Moves actor such that it is placed away from its target at an absolute distance equal to the sum of the two mobjs' radii.
//
// var1 = unused
// var2 = unused
//
void A_DragonSegment(mobj_t *actor)
{
mobj_t *target = actor->target;
fixed_t dist = P_AproxDistance(P_AproxDistance(actor->x - target->x, actor->y - target->y), actor->z - target->z);
fixed_t radius = actor->radius + target->radius;
angle_t hangle = R_PointToAngle2(target->x, target->y, actor->x, actor->y);
angle_t zangle = R_PointToAngle2(0, target->z, dist, actor->z);
fixed_t hdist = P_ReturnThrustX(target, zangle, radius);
fixed_t xdist = P_ReturnThrustX(target, hangle, hdist);
fixed_t ydist = P_ReturnThrustY(target, hangle, hdist);
fixed_t zdist = P_ReturnThrustY(target, zangle, radius);
#ifdef HAVE_BLUA
if (LUA_CallAction("A_DragonSegment", actor))
return;
#endif
actor->angle = hangle;
P_TeleportMove(actor, target->x + xdist, target->y + ydist, target->z + zdist);
}

View File

@ -148,15 +148,19 @@ void P_ResetStarposts(void)
// //
boolean P_CanPickupItem(player_t *player, boolean weapon) boolean P_CanPickupItem(player_t *player, boolean weapon)
{ {
if (player->bot && weapon) if (!player->mo || player->mo->health <= 0)
return false; return false;
if (player->bot)
{
if (weapon)
return false;
return P_CanPickupItem(&players[consoleplayer], true); // weapon is true to prevent infinite recursion if p1 is bot - doesn't occur in vanilla, but may be relevant for mods
}
if (player->powers[pw_flashing] > (flashingtics/4)*3 && player->powers[pw_flashing] < UINT16_MAX) if (player->powers[pw_flashing] > (flashingtics/4)*3 && player->powers[pw_flashing] < UINT16_MAX)
return false; return false;
if (player->mo && player->mo->health <= 0)
return false;
return true; return true;
} }
@ -2521,7 +2525,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0)) if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0))
; ;
else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) && (target->player->lives != INFLIVES) else if (!target->player->bot && !target->player->spectator && (target->player->lives != INFLIVES)
&& G_GametypeUsesLives()) && G_GametypeUsesLives())
{ {
target->player->lives -= 1; // Lose a life Tails 03-11-2000 target->player->lives -= 1; // Lose a life Tails 03-11-2000
@ -2678,6 +2682,17 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
target->flags = (target->flags|MF_NOCLIPHEIGHT) & ~MF_NOGRAVITY; target->flags = (target->flags|MF_NOCLIPHEIGHT) & ~MF_NOGRAVITY;
break; break;
case MT_DRAGONBOMBER:
{
mobj_t *segment = target;
while (segment->tracer != NULL)
{
P_KillMobj(segment->tracer, NULL, NULL, 0);
segment = segment->tracer;
}
break;
}
case MT_EGGMOBILE3: case MT_EGGMOBILE3:
{ {
mobj_t *mo2; mobj_t *mo2;
@ -2741,7 +2756,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
case MT_EGGTRAP: case MT_EGGTRAP:
// Time for birdies! Yaaaaaaaay! // Time for birdies! Yaaaaaaaay!
target->fuse = TICRATE*2; target->fuse = TICRATE;
break; break;
case MT_MINECART: case MT_MINECART:
@ -2803,13 +2818,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if (flip) if (flip)
momz *= -1; momz *= -1;
#define makechunk(angtweak, xmov, ymov) \ #define makechunk(angtweak, xmov, ymov) \
chunk = P_SpawnMobj(target->x, target->y, target->z, MT_SPIKE);\ chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SPIKE);\
chunk->eflags |= flip;\
P_SetMobjState(chunk, target->info->xdeathstate);\ P_SetMobjState(chunk, target->info->xdeathstate);\
chunk->health = 0;\ chunk->health = 0;\
chunk->angle = angtweak;\ chunk->angle = angtweak;\
chunk->destscale = scale;\
P_SetScale(chunk, scale);\
P_UnsetThingPosition(chunk);\ P_UnsetThingPosition(chunk);\
chunk->flags = MF_NOCLIP;\ chunk->flags = MF_NOCLIP;\
chunk->x += xmov;\ chunk->x += xmov;\
@ -2828,14 +2840,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if (flip) if (flip)
momz *= -1; momz *= -1;
chunk = P_SpawnMobj(target->x, target->y, target->z, MT_SPIKE); chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SPIKE);
chunk->eflags |= flip;
P_SetMobjState(chunk, target->info->deathstate); P_SetMobjState(chunk, target->info->deathstate);
chunk->health = 0; chunk->health = 0;
chunk->angle = ang + ANGLE_180; chunk->angle = ang + ANGLE_180;
chunk->destscale = scale;
P_SetScale(chunk, scale);
P_UnsetThingPosition(chunk); P_UnsetThingPosition(chunk);
chunk->flags = MF_NOCLIP; chunk->flags = MF_NOCLIP;
chunk->x -= xoffs; chunk->x -= xoffs;
@ -2878,13 +2886,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
sprflip = P_RandomChance(FRACUNIT/2); sprflip = P_RandomChance(FRACUNIT/2);
#define makechunk(angtweak, xmov, ymov) \ #define makechunk(angtweak, xmov, ymov) \
chunk = P_SpawnMobj(target->x, target->y, target->z, MT_WALLSPIKE);\ chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_WALLSPIKE);\
chunk->eflags |= flip;\
P_SetMobjState(chunk, target->info->xdeathstate);\ P_SetMobjState(chunk, target->info->xdeathstate);\
chunk->health = 0;\ chunk->health = 0;\
chunk->angle = target->angle;\ chunk->angle = target->angle;\
chunk->destscale = scale;\
P_SetScale(chunk, scale);\
P_UnsetThingPosition(chunk);\ P_UnsetThingPosition(chunk);\
chunk->flags = MF_NOCLIP;\ chunk->flags = MF_NOCLIP;\
chunk->x += xmov - forwardxoffs;\ chunk->x += xmov - forwardxoffs;\
@ -2906,14 +2911,11 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
sprflip = P_RandomChance(FRACUNIT/2); sprflip = P_RandomChance(FRACUNIT/2);
chunk = P_SpawnMobj(target->x, target->y, target->z, MT_WALLSPIKE); chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_WALLSPIKE);
chunk->eflags |= flip;
P_SetMobjState(chunk, target->info->deathstate); P_SetMobjState(chunk, target->info->deathstate);
chunk->health = 0; chunk->health = 0;
chunk->angle = target->angle; chunk->angle = target->angle;
chunk->destscale = scale;
P_SetScale(chunk, scale);
P_UnsetThingPosition(chunk); P_UnsetThingPosition(chunk);
chunk->flags = MF_NOCLIP; chunk->flags = MF_NOCLIP;
chunk->x += forwardxoffs - xoffs; chunk->x += forwardxoffs - xoffs;
@ -3002,6 +3004,10 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
P_SetPlayerMobjState(target, S_PLAY_NIGHTS_STUN); P_SetPlayerMobjState(target, S_PLAY_NIGHTS_STUN);
S_StartSound(target, sfx_nghurt); S_StartSound(target, sfx_nghurt);
#ifdef ROTSPRITE
player->mo->rollangle = 0;
#endif
if (oldnightstime > 10*TICRATE if (oldnightstime > 10*TICRATE
&& player->nightstime < 10*TICRATE) && player->nightstime < 10*TICRATE)
{ {
@ -3517,7 +3523,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
// Make sure that boxes cannot be popped by enemies, red rings, etc. // Make sure that boxes cannot be popped by enemies, red rings, etc.
if (target->flags & MF_MONITOR && ((!source || !source->player || source->player->bot) if (target->flags & MF_MONITOR && ((!source || !source->player || source->player->bot)
|| (inflictor && inflictor->type >= MT_REDRING && inflictor->type <= MT_GRENADERING))) || (inflictor && (inflictor->type == MT_REDRING || (inflictor->type >= MT_THROWNBOUNCE && inflictor->type <= MT_THROWNGRENADE)))))
return false; return false;
} }

View File

@ -172,6 +172,7 @@ void P_ElementalFire(player_t *player, boolean cropcircle);
void P_DoPityCheck(player_t *player); void P_DoPityCheck(player_t *player);
void P_PlayerThink(player_t *player); void P_PlayerThink(player_t *player);
void P_PlayerAfterThink(player_t *player); void P_PlayerAfterThink(player_t *player);
void P_DoPlayerFinish(player_t *player);
void P_DoPlayerExit(player_t *player); void P_DoPlayerExit(player_t *player);
void P_NightserizePlayer(player_t *player, INT32 ptime); void P_NightserizePlayer(player_t *player, INT32 ptime);
@ -322,6 +323,7 @@ SINT8 P_MobjFlip(mobj_t *mobj);
fixed_t P_GetMobjGravity(mobj_t *mo); fixed_t P_GetMobjGravity(mobj_t *mo);
FUNCMATH boolean P_WeaponOrPanel(mobjtype_t type); FUNCMATH boolean P_WeaponOrPanel(mobjtype_t type);
void P_CalcChasePostImg(player_t *player, camera_t *thiscam);
boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled); boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled);
void P_Attract(mobj_t *source, mobj_t *enemy, boolean nightsgrab); void P_Attract(mobj_t *source, mobj_t *enemy, boolean nightsgrab);

View File

@ -592,7 +592,7 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails)
if (!(tails->pflags & PF_CANCARRY)) if (!(tails->pflags & PF_CANCARRY))
return; return;
if (tails->bot == 1) if (sonic->pflags & PF_FINISHED)
return; return;
if ((sonic->mo->eflags & MFE_VERTICALFLIP) != (tails->mo->eflags & MFE_VERTICALFLIP)) if ((sonic->mo->eflags & MFE_VERTICALFLIP) != (tails->mo->eflags & MFE_VERTICALFLIP))
@ -661,31 +661,32 @@ static void P_SlapStick(mobj_t *fang, mobj_t *pole)
momx2 = fang->momx/dist; momx2 = fang->momx/dist;
momy2 = fang->momy/dist; momy2 = fang->momy/dist;
pole->tracer->momx = momx1 + (dist-1)*momx2; pole->tracer->tracer->momx = momx1 + (dist-1)*momx2;
pole->tracer->momy = momy1 + (dist-1)*momy2; pole->tracer->tracer->momy = momy1 + (dist-1)*momy2;
fang->momx = (dist-1)*momx1 + momx2; fang->momx = (dist-1)*momx1 + momx2;
fang->momy = (dist-1)*momy1 + momy2; fang->momy = (dist-1)*momy1 + momy2;
#undef dist #undef dist
P_SetMobjState(pole, pole->info->deathstate); P_SetObjectMomZ(pole->tracer->tracer, 6*FRACUNIT, false);
pole->tracer->tracer->flags &= ~(MF_NOGRAVITY|MF_NOCLIP);
P_SetObjectMomZ(pole->tracer, 6*FRACUNIT, false); pole->tracer->tracer->movedir = ANGLE_67h;
pole->tracer->flags &= ~(MF_NOGRAVITY|MF_NOCLIP); if ((R_PointToAngle(fang->x - pole->tracer->tracer->x, fang->y - pole->tracer->tracer->y) - pole->angle) > ANGLE_180)
pole->tracer->movedir = ANGLE_67h; pole->tracer->tracer->movedir = InvAngle(pole->tracer->movedir);
if ((R_PointToAngle(fang->x - pole->tracer->x, fang->y - pole->tracer->y) - pole->angle) > ANGLE_180)
pole->tracer->movedir = InvAngle(pole->tracer->movedir);
P_SetObjectMomZ(fang, 14*FRACUNIT, false); P_SetObjectMomZ(fang, 14*FRACUNIT, false);
fang->flags |= MF_NOGRAVITY|MF_NOCLIP; fang->flags |= MF_NOGRAVITY|MF_NOCLIP;
P_SetMobjState(fang, fang->info->xdeathstate); P_SetMobjState(fang, fang->info->xdeathstate);
pole->tracer->tics = pole->tics = fang->tics; pole->tracer->tracer->tics = pole->tracer->tics = pole->tics = fang->tics;
var1 = var2 = 0; var1 = var2 = 0;
A_Scream(pole->tracer); A_Scream(pole->tracer->tracer);
S_StartSound(fang, sfx_altdi1); S_StartSound(fang, sfx_altdi1);
P_SetTarget(&pole->tracer->tracer, NULL);
P_SetMobjState(pole->tracer, pole->info->xdeathstate);
P_SetTarget(&pole->tracer, NULL); P_SetTarget(&pole->tracer, NULL);
P_SetMobjState(pole, pole->info->deathstate);
} }
static void P_PlayerBarrelCollide(mobj_t *toucher, mobj_t *barrel) static void P_PlayerBarrelCollide(mobj_t *toucher, mobj_t *barrel)
@ -783,12 +784,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (thing->type == MT_SPIKE if (thing->type == MT_SPIKE
|| thing->type == MT_WALLSPIKE) || thing->type == MT_WALLSPIKE)
{ {
mobjtype_t type = thing->type; mobj_t *iter;
if (thing->flags & MF_SOLID) if (thing->flags & MF_SOLID)
S_StartSound(tmthing, thing->info->deathsound); S_StartSound(tmthing, thing->info->deathsound);
for (thing = thing->subsector->sector->thinglist; thing; thing = thing->snext) for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
if (thing->type == type && thing->health > 0 && thing->flags & MF_SOLID && P_AproxDistance(P_AproxDistance(thing->x - tmthing->x, thing->y - tmthing->y), thing->z - tmthing->z) < 56*thing->scale)//FixedMul(56*FRACUNIT, thing->scale)) if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
P_KillMobj(thing, tmthing, tmthing, 0); P_KillMobj(iter, tmthing, tmthing, 0);
} }
else else
{ {
@ -822,12 +823,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (thing->type == MT_SPIKE if (thing->type == MT_SPIKE
|| thing->type == MT_WALLSPIKE) || thing->type == MT_WALLSPIKE)
{ {
mobjtype_t type = thing->type; mobj_t *iter;
if (thing->flags & MF_SOLID) if (thing->flags & MF_SOLID)
S_StartSound(tmthing, thing->info->deathsound); S_StartSound(tmthing, thing->info->deathsound);
for (thing = thing->subsector->sector->thinglist; thing; thing = thing->snext) for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext)
if (thing->type == type && thing->health > 0 && thing->flags & MF_SOLID && P_AproxDistance(P_AproxDistance(thing->x - tmthing->x, thing->y - tmthing->y), thing->z - tmthing->z) < 56*thing->scale)//FixedMul(56*FRACUNIT, thing->scale)) if (iter->type == thing->type && iter->health > 0 && iter->flags & MF_SOLID && (iter == thing || P_AproxDistance(P_AproxDistance(thing->x - iter->x, thing->y - iter->y), thing->z - iter->z) < 56*thing->scale))//FixedMul(56*FRACUNIT, thing->scale))
P_KillMobj(thing, tmthing, tmthing, 0); P_KillMobj(iter, tmthing, tmthing, 0);
} }
else else
{ {
@ -1020,7 +1021,6 @@ static boolean PIT_CheckThing(mobj_t *thing)
if ((thing->flags & MF_PUSHABLE) // not carrying a player if ((thing->flags & MF_PUSHABLE) // not carrying a player
&& (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something && (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something
&& ((tmthing->eflags & MFE_VERTICALFLIP) == (thing->eflags & MFE_VERTICALFLIP)) && ((tmthing->eflags & MFE_VERTICALFLIP) == (thing->eflags & MFE_VERTICALFLIP))
&& (P_AproxDistance(thing->x - tmthing->x, thing->y - tmthing->y) < (thing->radius))
&& (P_MobjFlip(tmthing)*tmthing->momz <= 0) && (P_MobjFlip(tmthing)*tmthing->momz <= 0)
&& ((!(tmthing->eflags & MFE_VERTICALFLIP) && abs(thing->z + thing->height - tmthing->z) < (thing->height>>2)) && ((!(tmthing->eflags & MFE_VERTICALFLIP) && abs(thing->z + thing->height - tmthing->z) < (thing->height>>2))
|| (tmthing->eflags & MFE_VERTICALFLIP && abs(tmthing->z + tmthing->height - thing->z) < (thing->height>>2)))) || (tmthing->eflags & MFE_VERTICALFLIP && abs(tmthing->z + tmthing->height - thing->z) < (thing->height>>2))))
@ -1032,7 +1032,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
P_SetPlayerMobjState(tmthing, S_PLAY_WALK); P_SetPlayerMobjState(tmthing, S_PLAY_WALK);
tmthing->player->powers[pw_carry] = CR_ROLLOUT; tmthing->player->powers[pw_carry] = CR_ROLLOUT;
P_SetTarget(&tmthing->tracer, thing); P_SetTarget(&tmthing->tracer, thing);
P_SetObjectMomZ(thing, tmthing->momz, true); if (!P_IsObjectOnGround(thing))
thing->momz += tmthing->momz;
return true; return true;
} }
} }
@ -1063,6 +1064,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
thing->momy = tmthing->momy; thing->momy = tmthing->momy;
tmthing->momx = tempmomx; tmthing->momx = tempmomx;
tmthing->momy = tempmomy; tmthing->momy = tempmomy;
S_StartSound(thing, thing->info->painsound);
} }
} }
@ -1089,7 +1091,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
return true; // overhead return true; // overhead
if (thing->z + thing->height < tmthing->z) if (thing->z + thing->height < tmthing->z)
return true; // underneath return true; // underneath
if (!thing->tracer) if (!thing->tracer || !thing->tracer->tracer)
return true; return true;
P_SlapStick(tmthing, thing); P_SlapStick(tmthing, thing);
// no return value was used in the original prototype script at this point, // no return value was used in the original prototype script at this point,
@ -1349,6 +1351,11 @@ static boolean PIT_CheckThing(mobj_t *thing)
P_DamageMobj(thing, tmthing, tmthing->target, 1, damagetype); P_DamageMobj(thing, tmthing, tmthing->target, 1, damagetype);
} }
// Fireball touched an enemy
// Don't bounce though, just despawn right there
if ((tmthing->type == MT_FIREBALL) && (thing->flags & MF_ENEMY))
P_KillMobj(tmthing, NULL, NULL, 0);
// don't traverse any more // don't traverse any more
if (tmthing->type == MT_SHELL) if (tmthing->type == MT_SHELL)
@ -1712,8 +1719,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
} }
} }
if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM) && (thing->player)) if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player))
; // springs and gas jets should never be able to step up onto a player ; // springs, gas jets and springs should never be able to step up onto a player
// z checking at last // z checking at last
// Treat noclip things as non-solid! // Treat noclip things as non-solid!
else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID
@ -3464,7 +3471,7 @@ isblocking:
} }
// see about climbing on the wall // see about climbing on the wall
if (!(checkline->flags & ML_NOCLIMB)) if (!(checkline->flags & ML_NOCLIMB) && checkline->special != HORIZONSPECIAL)
{ {
boolean canclimb; boolean canclimb;
angle_t climbangle, climbline; angle_t climbangle, climbline;
@ -3751,6 +3758,33 @@ void P_SlideMove(mobj_t *mo)
v2.x = tmhitthing->x + cosradius; v2.x = tmhitthing->x + cosradius;
v2.y = tmhitthing->y + sinradius; v2.y = tmhitthing->y + sinradius;
// Can we box collision our way into smooth movement..?
if (sinradius && mo->y + mo->radius <= min(v1.y, v2.y))
{
mo->momy = 0;
P_TryMove(mo, mo->x + mo->momx, min(v1.y, v2.y) - mo->radius, true);
return;
}
else if (sinradius && mo->y - mo->radius >= max(v1.y, v2.y))
{
mo->momy = 0;
P_TryMove(mo, mo->x + mo->momx, max(v1.y, v2.y) + mo->radius, true);
return;
}
else if (cosradius && mo->x + mo->radius <= min(v1.x, v2.x))
{
mo->momx = 0;
P_TryMove(mo, min(v1.x, v2.x) - mo->radius, mo->y + mo->momy, true);
return;
}
else if (cosradius && mo->x - mo->radius >= max(v1.x, v2.x))
{
mo->momx = 0;
P_TryMove(mo, max(v1.x, v2.x) + mo->radius, mo->y + mo->momy, true);
return;
}
// nope, gotta fuck around with a fake linedef!
junk.v1 = &v1; junk.v1 = &v1;
junk.v2 = &v2; junk.v2 = &v2;
junk.dx = 2*cosradius; // v2.x - v1.x; junk.dx = 2*cosradius; // v2.x - v1.x;

View File

@ -246,6 +246,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
{ {
case S_PLAY_STND: case S_PLAY_STND:
case S_PLAY_WAIT: case S_PLAY_WAIT:
case S_PLAY_NIGHTS_STAND:
player->panim = PA_IDLE; player->panim = PA_IDLE;
break; break;
case S_PLAY_EDGE: case S_PLAY_EDGE:
@ -269,6 +270,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
break; break;
case S_PLAY_ROLL: case S_PLAY_ROLL:
//case S_PLAY_SPINDASH: -- everyone can ROLL thanks to zoom tubes... //case S_PLAY_SPINDASH: -- everyone can ROLL thanks to zoom tubes...
case S_PLAY_NIGHTS_ATTACK:
player->panim = PA_ROLL; player->panim = PA_ROLL;
break; break;
case S_PLAY_JUMP: case S_PLAY_JUMP:
@ -278,6 +280,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
player->panim = PA_SPRING; player->panim = PA_SPRING;
break; break;
case S_PLAY_FALL: case S_PLAY_FALL:
case S_PLAY_NIGHTS_FLOAT:
player->panim = PA_FALL; player->panim = PA_FALL;
break; break;
case S_PLAY_FLY: case S_PLAY_FLY:
@ -3405,7 +3408,7 @@ void P_MobjCheckWater(mobj_t *mobj)
// Drown timer setting // Drown timer setting
if ((p->powers[pw_shield] & SH_PROTECTWATER) // Has water protection if ((p->powers[pw_shield] & SH_PROTECTWATER) // Has water protection
|| (p->exiting) // Or exiting || (p->exiting) || (p->pflags & PF_FINISHED) // Or finished/exiting
|| (maptol & TOL_NIGHTS) // Or in NiGHTS mode || (maptol & TOL_NIGHTS) // Or in NiGHTS mode
|| (mariomode)) // Or in Mario mode... || (mariomode)) // Or in Mario mode...
{ {
@ -3721,17 +3724,10 @@ void P_DestroyRobots(void)
} }
} }
// P_CameraThinker // the below is chasecam only, if you're curious. check out P_CalcPostImg in p_user.c for first person
// void P_CalcChasePostImg(player_t *player, camera_t *thiscam)
// Process the mobj-ish required functions of the camera
boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled)
{ {
boolean itsatwodlevel = false;
postimg_t postimg = postimg_none; postimg_t postimg = postimg_none;
if (twodlevel
|| (thiscam == &camera && players[displayplayer].mo && (players[displayplayer].mo->flags2 & MF2_TWOD))
|| (thiscam == &camera2 && players[secondarydisplayplayer].mo && (players[secondarydisplayplayer].mo->flags2 & MF2_TWOD)))
itsatwodlevel = true;
if (player->pflags & PF_FLIPCAM && !(player->powers[pw_carry] == CR_NIGHTSMODE) && player->mo->eflags & MFE_VERTICALFLIP) if (player->pflags & PF_FLIPCAM && !(player->powers[pw_carry] == CR_NIGHTSMODE) && player->mo->eflags & MFE_VERTICALFLIP)
postimg = postimg_flip; postimg = postimg_flip;
@ -3759,13 +3755,27 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled
postimg = postimg_heat; postimg = postimg_heat;
} }
if (postimg != postimg_none) if (postimg == postimg_none)
{ return;
if (splitscreen && player == &players[secondarydisplayplayer])
postimgtype2 = postimg; if (splitscreen && player == &players[secondarydisplayplayer])
else postimgtype2 = postimg;
postimgtype = postimg; else
} postimgtype = postimg;
}
// P_CameraThinker
//
// Process the mobj-ish required functions of the camera
boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled)
{
boolean itsatwodlevel = false;
if (twodlevel
|| (thiscam == &camera && players[displayplayer].mo && (players[displayplayer].mo->flags2 & MF2_TWOD))
|| (thiscam == &camera2 && players[secondarydisplayplayer].mo && (players[secondarydisplayplayer].mo->flags2 & MF2_TWOD)))
itsatwodlevel = true;
P_CalcChasePostImg(player, thiscam);
if (thiscam->momx || thiscam->momy) if (thiscam->momx || thiscam->momy)
{ {
@ -4535,23 +4545,29 @@ static void P_Boss3Thinker(mobj_t *mobj)
} }
else if (mobj->movecount) // Firing mode else if (mobj->movecount) // Firing mode
{ {
// look for a new target
P_BossTargetPlayer(mobj, false);
if (!mobj->target || !mobj->target->player)
return;
// Always face your target.
A_FaceTarget(mobj);
// Check if the attack animation is running. If not, play it. // Check if the attack animation is running. If not, play it.
if (mobj->state < &states[mobj->info->missilestate] || mobj->state > &states[mobj->info->raisestate]) if (mobj->state < &states[mobj->info->missilestate] || mobj->state > &states[mobj->info->raisestate])
{ {
// look for a new target
P_BossTargetPlayer(mobj, true);
if (!mobj->target || !mobj->target->player)
return;
if (mobj->health <= mobj->info->damage) // pinch phase if (mobj->health <= mobj->info->damage) // pinch phase
mobj->movecount--; // limited number of shots before diving again mobj->movecount--; // limited number of shots before diving again
if (mobj->movecount) if (mobj->movecount)
P_SetMobjState(mobj, mobj->info->missilestate+1); P_SetMobjState(mobj, mobj->info->missilestate+1);
} }
else if (mobj->target && mobj->target->player)
{
angle_t diff = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y) - mobj->angle;
if (diff > ANGLE_180)
diff = InvAngle(InvAngle(diff)/4);
else
diff /= 4;
mobj->angle += diff;
}
} }
else if (mobj->threshold >= 0) // Traveling mode else if (mobj->threshold >= 0) // Traveling mode
{ {
@ -4666,13 +4682,10 @@ static void P_Boss3Thinker(mobj_t *mobj)
S_StartSound(mobj, shock->info->seesound); S_StartSound(mobj, shock->info->seesound);
// look for a new target // look for a new target
P_BossTargetPlayer(mobj, false); P_BossTargetPlayer(mobj, true);
if (mobj->target && mobj->target->player) if (mobj->target && mobj->target->player)
{
A_FaceTarget(mobj);
P_SetMobjState(mobj, mobj->info->missilestate); P_SetMobjState(mobj, mobj->info->missilestate);
}
} }
else if (mobj->flags2 & (MF2_STRONGBOX|MF2_CLASSICPUSH)) // just hit the bottom of your tube else if (mobj->flags2 & (MF2_STRONGBOX|MF2_CLASSICPUSH)) // just hit the bottom of your tube
{ {
@ -7076,7 +7089,7 @@ static void P_SpawnMinecartSegments(mobj_t *mobj, boolean mode)
seg = P_SpawnMobj(x, y, z, MT_MINECARTSEG); seg = P_SpawnMobj(x, y, z, MT_MINECARTSEG);
P_SetMobjState(seg, (statenum_t)(S_MINECARTSEG_FRONT + i)); P_SetMobjState(seg, (statenum_t)(S_MINECARTSEG_FRONT + i));
if (i >= 2) if (i >= 2)
seg->extravalue1 = (i == 2) ? -18 : 18; seg->extravalue1 = (i == 2) ? -20 : 20;
else else
{ {
seg->extravalue2 = (i == 0) ? 24 : -24; seg->extravalue2 = (i == 0) ? 24 : -24;
@ -8227,7 +8240,7 @@ void P_MobjThinker(mobj_t *mobj)
mobj->flags2 ^= MF2_DONTDRAW; mobj->flags2 ^= MF2_DONTDRAW;
break; break;
case MT_EGGTRAP: // Egg Capsule animal release case MT_EGGTRAP: // Egg Capsule animal release
if (mobj->fuse > 0 && mobj->fuse < 2*TICRATE-(TICRATE/7)) if (mobj->fuse > 0)// && mobj->fuse < TICRATE-(TICRATE/7))
{ {
INT32 i; INT32 i;
fixed_t x,y,z; fixed_t x,y,z;
@ -8236,9 +8249,9 @@ void P_MobjThinker(mobj_t *mobj)
mobj_t *flicky; mobj_t *flicky;
z = mobj->subsector->sector->floorheight + FRACUNIT + (P_RandomKey(64)<<FRACBITS); z = mobj->subsector->sector->floorheight + FRACUNIT + (P_RandomKey(64)<<FRACBITS);
for (i = 0; i < 2; i++) for (i = 0; i < 3; i++)
{ {
const angle_t fa = (P_RandomByte()*FINEANGLES/16) & FINEMASK; const angle_t fa = P_RandomKey(FINEANGLES) & FINEMASK;
ns = 64 * FRACUNIT; ns = 64 * FRACUNIT;
x = mobj->x + FixedMul(FINESINE(fa),ns); x = mobj->x + FixedMul(FINESINE(fa),ns);
y = mobj->y + FixedMul(FINECOSINE(fa),ns); y = mobj->y + FixedMul(FINECOSINE(fa),ns);
@ -8690,6 +8703,13 @@ void P_MobjThinker(mobj_t *mobj)
case MT_KOOPA: case MT_KOOPA:
P_KoopaThinker(mobj); P_KoopaThinker(mobj);
break; break;
case MT_FIREBALL:
if (P_AproxDistance(mobj->momx, mobj->momy) <= 16*FRACUNIT) // Once fireballs lose enough speed, kill them
{
P_KillMobj(mobj, NULL, NULL, 0);
return;
}
break;
case MT_REDRING: case MT_REDRING:
if (((mobj->z < mobj->floorz) || (mobj->z + mobj->height > mobj->ceilingz)) if (((mobj->z < mobj->floorz) || (mobj->z + mobj->height > mobj->ceilingz))
&& mobj->flags & MF_MISSILE) && mobj->flags & MF_MISSILE)
@ -9622,6 +9642,90 @@ void P_MobjThinker(mobj_t *mobj)
} }
break; break;
} }
case MT_DRAGONBOMBER:
{
#define DRAGONTURNSPEED ANG2
mobj->movecount = (mobj->movecount + 9) % 360;
P_SetObjectMomZ(mobj, 4*FINESINE(((mobj->movecount*ANG1) >> ANGLETOFINESHIFT) & FINEMASK), false);
if (mobj->threshold > 0) // are we dropping mines?
{
mobj->threshold--;
if (mobj->threshold == 0) // if the timer hits 0, look for a mine to drop!
{
mobj_t *segment = mobj;
while (segment->tracer != NULL && !P_MobjWasRemoved(segment->tracer) && segment->tracer->state == &states[segment->tracer->info->spawnstate])
{
segment = segment->tracer;
}
if (segment != mobj) // found an unactivated segment?
{
mobj_t *mine = P_SpawnMobjFromMobj(segment, 0, 0, 0, segment->info->painchance);
mine->angle = segment->angle;
P_InstaThrust(mine, mobj->angle, P_AproxDistance(mobj->momx, mobj->momy) >> 1);
P_SetObjectMomZ(mine, -2*FRACUNIT, true);
S_StartSound(mine, mine->info->seesound);
P_SetMobjState(segment, segment->info->raisestate);
mobj->threshold = mobj->info->painchance;
}
}
}
if (mobj->target != NULL) // Are we chasing a player?
{
fixed_t dist = P_AproxDistance(mobj->x - mobj->target->x, mobj->y - mobj->target->y);
if (dist > 2000 * mobj->scale) // Not anymore!
P_SetTarget(&mobj->target, NULL);
else
{
fixed_t vspeed = FixedMul(mobj->info->speed >> 3, mobj->scale);
fixed_t z = mobj->target->z + (mobj->height >> 1) + (mobj->flags & MFE_VERTICALFLIP ? -128*mobj->scale : 128*mobj->scale + mobj->target->height);
angle_t diff = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x, mobj->target->y) - mobj->angle;
if (diff > ANGLE_180)
mobj->angle -= DRAGONTURNSPEED;
else
mobj->angle += DRAGONTURNSPEED;
if (!mobj->threshold && dist < 512 * mobj->scale) // Close enough to drop bombs
{
mobj->threshold = mobj->info->painchance;
}
mobj->momz += max(min(z - mobj->z, vspeed), -vspeed);
}
}
else // Can we find a player to chase?
{
if (mobj->tracer == NULL || mobj->tracer->state != &states[mobj->tracer->info->spawnstate]
|| !P_LookForPlayers(mobj, true, false, 2000*mobj->scale)) // if not, circle around the spawnpoint
{
if (!mobj->spawnpoint) // unless we don't have one, in which case uhhh just circle around wherever we currently are I guess??
mobj->angle += DRAGONTURNSPEED;
else
{
fixed_t vspeed = FixedMul(mobj->info->speed >> 3, mobj->scale);
fixed_t x = mobj->spawnpoint->x << FRACBITS;
fixed_t y = mobj->spawnpoint->y << FRACBITS;
fixed_t z = mobj->spawnpoint->z << FRACBITS;
angle_t diff = R_PointToAngle2(mobj->x, mobj->y, x, y) - mobj->angle;
if (diff > ANGLE_180)
mobj->angle -= DRAGONTURNSPEED;
else
mobj->angle += DRAGONTURNSPEED;
mobj->momz += max(min(z - mobj->z, vspeed), -vspeed);
}
}
}
P_InstaThrust(mobj, mobj->angle, FixedMul(mobj->info->speed, mobj->scale));
#undef DRAGONTURNSPEED
}
break;
case MT_MINUS:
#ifdef ROTSPRITE
{
if (P_IsObjectOnGround(mobj))
mobj->rollangle = 0;
else
mobj->rollangle = R_PointToAngle2(0, 0, mobj->momz, (mobj->scale << 1) - min(abs(mobj->momz), mobj->scale << 1));
}
#endif
break;
case MT_SPINFIRE: case MT_SPINFIRE:
if (mobj->flags & MF_NOGRAVITY) if (mobj->flags & MF_NOGRAVITY)
{ {
@ -11207,7 +11311,7 @@ void P_SpawnPlayer(INT32 playernum)
mobj->radius = FixedMul(skins[p->skin].radius, mobj->scale); mobj->radius = FixedMul(skins[p->skin].radius, mobj->scale);
mobj->height = P_GetPlayerHeight(p); mobj->height = P_GetPlayerHeight(p);
if (!leveltime && ((maptol & TOL_NIGHTS) == TOL_NIGHTS) != (G_IsSpecialStage(gamemap))) // non-special NiGHTS stage or special non-NiGHTS stage if (!leveltime && !p->spectator && ((maptol & TOL_NIGHTS) == TOL_NIGHTS) != (G_IsSpecialStage(gamemap))) // non-special NiGHTS stage or special non-NiGHTS stage
{ {
if (maptol & TOL_NIGHTS) if (maptol & TOL_NIGHTS)
{ {

View File

@ -2704,6 +2704,7 @@ boolean P_SetupLevel(boolean skipprecip)
// Cancel all d_main.c fadeouts (keep fade in though). // Cancel all d_main.c fadeouts (keep fade in though).
wipegamestate = FORCEWIPEOFF; wipegamestate = FORCEWIPEOFF;
wipestyleflags = 0;
// Special stage fade to white // Special stage fade to white
// This is handled BEFORE sounds are stopped. // This is handled BEFORE sounds are stopped.
@ -2724,11 +2725,22 @@ boolean P_SetupLevel(boolean skipprecip)
S_FadeOutStopMusic(MUSICRATE/4); //FixedMul(FixedDiv(F_GetWipeLength(wipedefs[wipe_speclevel_towhite])*NEWTICRATERATIO, NEWTICRATE), MUSICRATE) S_FadeOutStopMusic(MUSICRATE/4); //FixedMul(FixedDiv(F_GetWipeLength(wipedefs[wipe_speclevel_towhite])*NEWTICRATERATIO, NEWTICRATE), MUSICRATE)
F_WipeStartScreen(); F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0); wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
#ifdef HWRENDER
// uh..........
if (rendermode == render_opengl)
F_WipeColorFill(0);
#endif
F_WipeEndScreen(); F_WipeEndScreen();
F_RunWipe(wipedefs[wipe_speclevel_towhite], false); F_RunWipe(wipedefs[wipe_speclevel_towhite], false);
I_OsPolling();
I_FinishUpdate(); // page flip or blit buffer
if (moviemode)
M_SaveFrame();
nowtime = lastwipetic; nowtime = lastwipetic;
// Hold on white for extra effect. // Hold on white for extra effect.
@ -2745,6 +2757,13 @@ boolean P_SetupLevel(boolean skipprecip)
ranspecialwipe = 1; ranspecialwipe = 1;
} }
if (G_GetModeAttackRetryFlag())
{
if (modeattacking)
wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE);
G_ClearModeAttackRetryFlag();
}
// Make sure all sounds are stopped before Z_FreeTags. // Make sure all sounds are stopped before Z_FreeTags.
S_StopSounds(); S_StopSounds();
S_ClearSfx(); S_ClearSfx();
@ -2762,7 +2781,13 @@ boolean P_SetupLevel(boolean skipprecip)
if (rendermode != render_none && !ranspecialwipe) if (rendermode != render_none && !ranspecialwipe)
{ {
F_WipeStartScreen(); F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); wipestyleflags |= WSF_FADEOUT;
#ifdef HWRENDER
// uh..........
if (rendermode == render_opengl)
F_WipeColorFill(31);
#endif
F_WipeEndScreen(); F_WipeEndScreen();
// for titlemap: run a specific wipe if specified // for titlemap: run a specific wipe if specified
@ -2787,12 +2812,12 @@ boolean P_SetupLevel(boolean skipprecip)
{ {
// Don't include these in the fade! // Don't include these in the fade!
char tx[64]; char tx[64];
V_DrawSmallString(1, 191, V_ALLOWLOWERCASE, M_GetText("Speeding off to...")); V_DrawSmallString(1, 191, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, M_GetText("Speeding off to..."));
snprintf(tx, 63, "%s%s%s", snprintf(tx, 63, "%s%s%s",
mapheaderinfo[gamemap-1]->lvlttl, mapheaderinfo[gamemap-1]->lvlttl,
(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE", (mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " Zone",
(mapheaderinfo[gamemap-1]->actnum > 0) ? va(", Act %d",mapheaderinfo[gamemap-1]->actnum) : ""); (mapheaderinfo[gamemap-1]->actnum > 0) ? va(" %d",mapheaderinfo[gamemap-1]->actnum) : "");
V_DrawSmallString(1, 195, V_ALLOWLOWERCASE, tx); V_DrawSmallString(1, 195, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, tx);
I_UpdateNoVsync(); I_UpdateNoVsync();
} }
@ -3177,7 +3202,7 @@ boolean P_SetupLevel(boolean skipprecip)
// Remove the loading shit from the screen // Remove the loading shit from the screen
if (rendermode != render_none && !titlemapinaction) if (rendermode != render_none && !titlemapinaction)
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); F_WipeColorFill(levelfadecol);
if (precache || dedicated) if (precache || dedicated)
R_PrecacheLevel(); R_PrecacheLevel();
@ -3226,44 +3251,22 @@ boolean P_SetupLevel(boolean skipprecip)
#endif #endif
} }
// Stage title! // No render mode, stop here.
if (rendermode != render_none if (rendermode == render_none)
&& (!titlemapinaction) return true;
&& ranspecialwipe != 2
&& *mapheaderinfo[gamemap-1]->lvlttl != '\0'
#ifdef HAVE_BLUA
&& LUA_HudEnabled(hud_stagetitle)
#endif
)
{
tic_t starttime = I_GetTime();
tic_t endtime = starttime + (10*NEWTICRATERATIO);
tic_t nowtime = starttime;
tic_t lasttime = starttime;
while (nowtime < endtime)
{
// draw loop
while (!((nowtime = I_GetTime()) - lasttime))
I_Sleep();
lasttime = nowtime;
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); // Title card!
stplyr = &players[consoleplayer]; G_StartTitleCard();
ST_drawLevelTitle(nowtime - starttime);
if (splitscreen)
{
stplyr = &players[secondarydisplayplayer];
ST_drawLevelTitle(nowtime - starttime);
}
I_OsPolling(); // Can the title card actually run, though?
I_UpdateNoBlit(); if (!WipeStageTitle)
I_FinishUpdate(); // page flip or blit buffer return true;
if (ranspecialwipe == 2)
return true;
if (moviemode) // make sure we save frames for the white hold too // If so...
M_SaveFrame(); if ((!(mapheaderinfo[gamemap-1]->levelflags & LF_NOTITLECARD)) && (*mapheaderinfo[gamemap-1]->lvlttl != '\0'))
} G_PreLevelTitleCard(lt_ticker, true);
}
return true; return true;
} }

View File

@ -2721,6 +2721,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
if (line->tag != 0) // Do special stuff only if a non-zero linedef tag is set if (line->tag != 0) // Do special stuff only if a non-zero linedef tag is set
{ {
// Play sounds from tagged sectors' origins.
if (line->flags & ML_EFFECT5) // Repeat Midtexture if (line->flags & ML_EFFECT5) // Repeat Midtexture
{ {
// Additionally play the sound from tagged sectors' soundorgs // Additionally play the sound from tagged sectors' soundorgs
@ -2732,31 +2733,45 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
S_StartSound(&sec->soundorg, sfxnum); S_StartSound(&sec->soundorg, sfxnum);
} }
} }
else if (mo) // A mobj must have triggered the executor
// Play the sound without origin for anyone, as long as they're inside tagged areas.
else
{ {
// Only trigger if mobj is touching the tag UINT8 i = 0;
mobj_t* camobj = players[displayplayer].mo;
ffloor_t *rover; ffloor_t *rover;
boolean foundit = false; boolean foundit = false;
for(rover = mo->subsector->sector->ffloors; rover; rover = rover->next) for (i = 0; i < 2; camobj = players[secondarydisplayplayer].mo, i++)
{ {
if (rover->master->frontsector->tag != line->tag) if (!camobj)
continue; continue;
if (mo->z > P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector)) if (foundit || (camobj->subsector->sector->tag == line->tag))
continue; {
foundit = true;
break;
}
if (mo->z + mo->height < P_GetSpecialBottomZ(mo, sectors + rover->secnum, mo->subsector->sector)) // Only trigger if mobj is touching the tag
continue; for(rover = camobj->subsector->sector->ffloors; rover; rover = rover->next)
{
if (rover->master->frontsector->tag != line->tag)
continue;
foundit = true; if (camobj->z > P_GetSpecialTopZ(camobj, sectors + rover->secnum, camobj->subsector->sector))
continue;
if (camobj->z + camobj->height < P_GetSpecialBottomZ(camobj, sectors + rover->secnum, camobj->subsector->sector))
continue;
foundit = true;
break;
}
} }
if (mo->subsector->sector->tag == line->tag) if (foundit)
foundit = true; S_StartSound(NULL, sfxnum);
if (!foundit)
return;
} }
} }
else else
@ -4439,59 +4454,55 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
case 3: // Linedef executor requires all players present case 3: // Linedef executor requires all players present
/// \todo check continues for proper splitscreen support? /// \todo check continues for proper splitscreen support?
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && !players[i].bot && players[i].mo && (gametype != GT_COOP || players[i].lives > 0)) {
if (!playeringame[i])
continue;
if (!players[i].mo)
continue;
if (players[i].spectator)
continue;
if (players[i].bot)
continue;
if (gametype == GT_COOP && players[i].lives <= 0)
continue;
if (roversector)
{ {
if (roversector) if (sector->flags & SF_TRIGGERSPECIAL_TOUCH)
{ {
if (players[i].mo->subsector->sector == roversector) msecnode_t *node;
; for (node = players[i].mo->touching_sectorlist; node; node = node->m_sectorlist_next)
else if (sector->flags & SF_TRIGGERSPECIAL_TOUCH)
{ {
boolean insector = false; if (P_ThingIsOnThe3DFloor(players[i].mo, sector, node->m_sector))
msecnode_t *node; break;
for (node = players[i].mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{
if (node->m_sector == roversector)
{
insector = true;
break;
}
}
if (!insector)
goto DoneSection2;
} }
else if (!node)
goto DoneSection2; goto DoneSection2;
}
if (!P_ThingIsOnThe3DFloor(players[i].mo, sector, roversector)) else if (players[i].mo->subsector && !P_ThingIsOnThe3DFloor(players[i].mo, sector, players[i].mo->subsector->sector)) // this function handles basically everything for us lmao
goto DoneSection2;
}
else
{
if (players[i].mo->subsector->sector == sector)
;
else if (sector->flags & SF_TRIGGERSPECIAL_TOUCH)
{
msecnode_t *node;
for (node = players[i].mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{
if (node->m_sector == sector)
break;
}
if (!node)
goto DoneSection2; goto DoneSection2;
} }
else else
{ goto DoneSection2;
if (players[i].mo->subsector->sector == sector)
;
else if (sector->flags & SF_TRIGGERSPECIAL_TOUCH)
{
boolean insector = false;
msecnode_t *node;
for (node = players[i].mo->touching_sectorlist; node; node = node->m_sectorlist_next)
{
if (node->m_sector == sector)
{
insector = true;
break;
}
}
if (!insector)
goto DoneSection2;
}
else
goto DoneSection2;
if (special == 3 && !P_MobjReadyToTrigger(players[i].mo, sector)) if (special == 3 && !P_MobjReadyToTrigger(players[i].mo, sector))
goto DoneSection2; goto DoneSection2;
}
} }
}
/* FALLTHRU */ /* FALLTHRU */
case 4: // Linedef executor that doesn't require touching floor case 4: // Linedef executor that doesn't require touching floor
case 5: // Linedef executor case 5: // Linedef executor
@ -4675,7 +4686,7 @@ DoneSection2:
{ {
INT32 lineindex; INT32 lineindex;
P_DoPlayerExit(player); P_DoPlayerFinish(player);
P_SetupSignExit(player); P_SetupSignExit(player);
// important: use sector->tag on next line instead of player->mo->subsector->tag // important: use sector->tag on next line instead of player->mo->subsector->tag

View File

@ -481,6 +481,9 @@ static inline void P_DoSpecialStageStuff(void)
tic_t oldnightstime = players[i].nightstime; tic_t oldnightstime = players[i].nightstime;
countspheres += players[i].spheres; countspheres += players[i].spheres;
if (!oldnightstime)
continue;
// If in water, deplete timer 6x as fast. // If in water, deplete timer 6x as fast.
if (players[i].mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER) && !(players[i].powers[pw_shield] & SH_PROTECTWATER)) if (players[i].mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER) && !(players[i].powers[pw_shield] & SH_PROTECTWATER))
players[i].nightstime -= 5; players[i].nightstime -= 5;
@ -506,12 +509,11 @@ static inline void P_DoSpecialStageStuff(void)
{ {
// Halt all the players // Halt all the players
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i]) if (playeringame[i] && !players[i].exiting)
{ {
players[i].mo->momx = players[i].mo->momy = 0; players[i].mo->momx = players[i].mo->momy = 0;
players[i].exiting = (14*TICRATE)/5 + 1; players[i].exiting = (14*TICRATE)/5 + 1;
} }
sstimer = 0; sstimer = 0;
P_GiveEmerald(true); P_GiveEmerald(true);
P_RestoreMusic(&players[consoleplayer]); P_RestoreMusic(&players[consoleplayer]);

View File

@ -343,13 +343,15 @@ void P_GiveEmerald(boolean spawnObj)
continue; continue;
emmo = P_SpawnMobjFromMobj(players[i].mo, 0, 0, players[i].mo->height, MT_GOTEMERALD); emmo = P_SpawnMobjFromMobj(players[i].mo, 0, 0, players[i].mo->height, MT_GOTEMERALD);
if (!emmo)
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);
P_SetTarget(&players[i].mo->tracer, emmo); P_SetTarget(&players[i].mo->tracer, emmo);
if (pnum == 255) if (pnum == 255)
{ {
i = pnum; pnum = i;
continue; continue;
} }
@ -636,6 +638,10 @@ static void P_DeNightserizePlayer(player_t *player)
player->marebonuslap = 0; player->marebonuslap = 0;
player->flyangle = 0; player->flyangle = 0;
player->anotherflyangle = 0; player->anotherflyangle = 0;
#ifdef ROTSPRITE
player->mo->rollangle = 0;
#endif
P_SetTarget(&player->mo->target, NULL); P_SetTarget(&player->mo->target, NULL);
P_SetTarget(&player->axis1, P_SetTarget(&player->axis2, NULL)); P_SetTarget(&player->axis1, P_SetTarget(&player->axis2, NULL));
@ -762,6 +768,9 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
player->secondjump = 0; player->secondjump = 0;
player->flyangle = 0; player->flyangle = 0;
player->anotherflyangle = 0; player->anotherflyangle = 0;
#ifdef ROTSPRITE
player->mo->rollangle = 0;
#endif
player->powers[pw_shield] = SH_NONE; player->powers[pw_shield] = SH_NONE;
player->powers[pw_super] = 0; player->powers[pw_super] = 0;
@ -1216,6 +1225,7 @@ void P_GivePlayerSpheres(player_t *player, INT32 num_spheres)
// //
void P_GivePlayerLives(player_t *player, INT32 numlives) void P_GivePlayerLives(player_t *player, INT32 numlives)
{ {
UINT8 prevlives = player->lives;
if (!player) if (!player)
return; return;
@ -1232,10 +1242,9 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0)
{ {
UINT8 prevlives = player->lives;
P_GivePlayerRings(player, 100*numlives); P_GivePlayerRings(player, 100*numlives);
if (player->lives - prevlives >= numlives) if (player->lives - prevlives >= numlives)
return; goto docooprespawn;
numlives = (numlives + prevlives - player->lives); numlives = (numlives + prevlives - player->lives);
} }
@ -1249,6 +1258,15 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
player->lives = 99; player->lives = 99;
else if (player->lives < 1) else if (player->lives < 1)
player->lives = 1; player->lives = 1;
docooprespawn:
if (cv_coopstarposts.value)
return;
if (prevlives > 0)
return;
if (!player->spectator)
return;
P_SpectatorJoinGame(player);
} }
void P_GiveCoopLives(player_t *player, INT32 numlives, boolean sound) void P_GiveCoopLives(player_t *player, INT32 numlives, boolean sound)
@ -2124,6 +2142,34 @@ void P_SpawnSpinMobj(player_t *player, mobjtype_t type)
P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do
} }
/** Called when \p player finishes the level.
*
* Only use for cases where the player should be able to move
* while waiting for others to finish. Otherwise, use P_DoPlayerExit().
*
* In single player or if ::cv_exitmove is disabled, this will also cause
* P_PlayerThink() to call P_DoPlayerExit(), so you do not need to
* make a special cases for those.
*
* \param player The player who finished the level.
* \sa P_DoPlayerExit
*
*/
void P_DoPlayerFinish(player_t *player)
{
if (player->pflags & PF_FINISHED)
return;
player->pflags |= PF_FINISHED;
if (netgame)
CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]);
player->powers[pw_underwater] = 0;
player->powers[pw_spacetime] = 0;
P_RestoreMusic(player);
}
// //
// P_DoPlayerExit // P_DoPlayerExit
// //
@ -2158,12 +2204,14 @@ void P_DoPlayerExit(player_t *player)
player->pflags |= P_GetJumpFlags(player); player->pflags |= P_GetJumpFlags(player);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP); P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
} }
else if (player->pflags & PF_STARTDASH)
{
player->pflags &= ~PF_STARTDASH;
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
}
player->powers[pw_underwater] = 0; player->powers[pw_underwater] = 0;
player->powers[pw_spacetime] = 0; player->powers[pw_spacetime] = 0;
P_RestoreMusic(player); P_RestoreMusic(player);
if (playeringame[player-players] && netgame && !circuitmap)
CONS_Printf(M_GetText("%s has completed the level.\n"), player_names[player-players]);
} }
#define SPACESPECIAL 12 #define SPACESPECIAL 12
@ -2263,7 +2311,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
else if (!player->skidtime) else if (!player->skidtime)
player->pflags &= ~PF_GLIDING; player->pflags &= ~PF_GLIDING;
} }
else if (player->charability == CA_GLIDEANDCLIMB && player->pflags & PF_THOKKED && !(player->pflags & PF_SHIELDABILITY) && player->mo->state-states == S_PLAY_FALL) else if (player->charability == CA_GLIDEANDCLIMB && player->pflags & PF_THOKKED && !(player->pflags & (PF_JUMPED|PF_SHIELDABILITY)) && player->mo->state-states == S_PLAY_FALL)
{ {
if (player->mo->state-states != S_PLAY_GLIDE_LANDING) if (player->mo->state-states != S_PLAY_GLIDE_LANDING)
{ {
@ -2326,11 +2374,23 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
; ;
else if (player->panim != PA_IDLE && player->panim != PA_WALK && player->panim != PA_RUN && player->panim != PA_DASH) else if (player->panim != PA_IDLE && player->panim != PA_WALK && player->panim != PA_RUN && player->panim != PA_DASH)
{ {
fixed_t runspd = FixedMul(player->runspeed, player->mo->scale);
// See comments in P_MovePlayer for explanation of changes.
if (player->powers[pw_super])
runspd = FixedMul(runspd, 5*FRACUNIT/3);
runspd = FixedMul(runspd, player->mo->movefactor);
if (maptol & TOL_2D)
runspd = FixedMul(runspd, 2*FRACUNIT/3);
if (player->cmomx || player->cmomy) if (player->cmomx || player->cmomy)
{ {
if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim != PA_DASH) if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim != PA_DASH)
P_SetPlayerMobjState(player->mo, S_PLAY_DASH); P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
else if (player->speed >= FixedMul(player->runspeed, player->mo->scale) else if (player->speed >= runspd
&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
P_SetPlayerMobjState(player->mo, S_PLAY_RUN); P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
else if ((player->rmomx || player->rmomy) else if ((player->rmomx || player->rmomy)
@ -2343,7 +2403,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff)
{ {
if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim != PA_DASH) if (player->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim != PA_DASH)
P_SetPlayerMobjState(player->mo, S_PLAY_DASH); P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
else if (player->speed >= FixedMul(player->runspeed, player->mo->scale) else if (player->speed >= runspd
&& (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN)) && (player->panim != PA_RUN || player->mo->state-states == S_PLAY_FLOAT_RUN))
P_SetPlayerMobjState(player->mo, S_PLAY_RUN); P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
else if ((player->mo->momx || player->mo->momy) else if ((player->mo->momx || player->mo->momy)
@ -2827,7 +2887,7 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player)
{ {
tic_t timeleft = (player->powers[pw_spacetime]) ? player->powers[pw_spacetime] : player->powers[pw_underwater]; tic_t timeleft = (player->powers[pw_spacetime]) ? player->powers[pw_spacetime] : player->powers[pw_underwater];
if (player->exiting) if (player->exiting || (player->pflags & PF_FINISHED))
player->powers[pw_underwater] = player->powers[pw_spacetime] = 0; player->powers[pw_underwater] = player->powers[pw_spacetime] = 0;
timeleft--; // The original code was all n*TICRATE + 1, so let's remove 1 tic for simplicity timeleft--; // The original code was all n*TICRATE + 1, so let's remove 1 tic for simplicity
@ -3478,7 +3538,7 @@ static void P_DoClimbing(player_t *player)
{ {
P_SetObjectMomZ(player->mo, 2*FRACUNIT, true); P_SetObjectMomZ(player->mo, 2*FRACUNIT, true);
if (cmd->forwardmove) if (cmd->forwardmove)
P_SetObjectMomZ(player->mo, 2*player->mo->momz/3, false); player->mo->momz = 2*player->mo->momz/3;
} }
if (thrust) if (thrust)
P_Thrust(player->mo, player->mo->angle, FixedMul(4*FRACUNIT, player->mo->scale)); // Lil' boost up. P_Thrust(player->mo, player->mo->angle, FixedMul(4*FRACUNIT, player->mo->scale)); // Lil' boost up.
@ -3993,12 +4053,14 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
if (player->pflags & PF_ATTACKDOWN || player->climbing || (G_TagGametype() && !(player->pflags & PF_TAGIT))) if (player->pflags & PF_ATTACKDOWN || player->climbing || (G_TagGametype() && !(player->pflags & PF_TAGIT)))
return; return;
if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) if (((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) && !(player->weapondelay))
{ {
player->pflags |= PF_ATTACKDOWN; player->pflags |= PF_ATTACKDOWN;
mo = P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0); mo = P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0);
P_InstaThrust(mo, player->mo->angle, ((mo->info->speed>>FRACBITS)*player->mo->scale) + player->speed); if (mo)
P_InstaThrust(mo, player->mo->angle, ((mo->info->speed>>FRACBITS)*player->mo->scale) + player->speed);
S_StartSound(player->mo, sfx_mario7); S_StartSound(player->mo, sfx_mario7);
P_SetWeaponDelay(player, TICRATE); // Short delay between fireballs so you can't spam them everywhere
return; return;
} }
@ -4017,8 +4079,8 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING); mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING);
if (mo) if (mo)
mo->fuse = 3*TICRATE; // Bounce Ring time mo->fuse = 3*TICRATE; // Bounce Ring time
} }
// Rail ring // Rail ring
else if (player->currentweapon == WEP_RAIL && player->powers[pw_railring]) else if (player->currentweapon == WEP_RAIL && player->powers[pw_railring])
@ -4391,7 +4453,11 @@ void P_DoJump(player_t *player, boolean soundandstate)
} }
else if (player->powers[pw_carry] == CR_ROLLOUT) else if (player->powers[pw_carry] == CR_ROLLOUT)
{ {
player->mo->momz = 9*FRACUNIT + player->mo->tracer->momz; player->mo->momz = 9*FRACUNIT;
if (P_MobjFlip(player->mo->tracer)*player->mo->tracer->momz > 0)
player->mo->momz += player->mo->tracer->momz;
if (!P_IsObjectOnGround(player->mo->tracer))
P_SetObjectMomZ(player->mo->tracer, -9*FRACUNIT, true);
player->powers[pw_carry] = CR_NONE; player->powers[pw_carry] = CR_NONE;
player->mo->tracer->flags |= MF_PUSHABLE; player->mo->tracer->flags |= MF_PUSHABLE;
P_SetTarget(&player->mo->tracer->tracer, NULL); P_SetTarget(&player->mo->tracer->tracer, NULL);
@ -4562,6 +4628,13 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
// Revving // Revving
else if ((cmd->buttons & BT_USE) && (player->pflags & PF_STARTDASH)) else if ((cmd->buttons & BT_USE) && (player->pflags & PF_STARTDASH))
{ {
if (player->speed > 5*player->mo->scale)
{
player->pflags &= ~PF_STARTDASH;
P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
S_StartSound(player->mo, sfx_spin);
break;
}
if (player->dashspeed < player->maxdash) if (player->dashspeed < player->maxdash)
{ {
#define chargecalculation (6*(player->dashspeed - player->mindash))/(player->maxdash - player->mindash) #define chargecalculation (6*(player->dashspeed - player->mindash))/(player->maxdash - player->mindash)
@ -4577,7 +4650,6 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
G_GhostAddRev(); G_GhostAddRev();
} }
} }
// If not moving up or down, and travelling faster than a speed of five while not holding // If not moving up or down, and travelling faster than a speed of five while not holding
// down the spin button and not spinning. // down the spin button and not spinning.
// AKA Just go into a spin on the ground, you idiot. ;) // AKA Just go into a spin on the ground, you idiot. ;)
@ -4729,10 +4801,10 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
// Rolling normally // Rolling normally
if (onground && player->pflags & PF_SPINNING && !(player->pflags & PF_STARTDASH) if (onground && player->pflags & PF_SPINNING && !(player->pflags & PF_STARTDASH)
&& player->speed < FixedMul(5*FRACUNIT,player->mo->scale) && canstand) && player->speed < 5*player->mo->scale && canstand)
{ {
if (GETSECSPECIAL(player->mo->subsector->sector->special, 4) == 7 || (player->mo->ceilingz - player->mo->floorz < P_GetPlayerHeight(player))) if (GETSECSPECIAL(player->mo->subsector->sector->special, 4) == 7 || (player->mo->ceilingz - player->mo->floorz < P_GetPlayerHeight(player)))
P_InstaThrust(player->mo, player->mo->angle, FixedMul(10*FRACUNIT, player->mo->scale)); P_InstaThrust(player->mo, player->mo->angle, 10*player->mo->scale);
else else
{ {
player->skidtime = 0; player->skidtime = 0;
@ -5295,7 +5367,10 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer
player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_STARTDASH); player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE|PF_SPINNING|PF_STARTDASH);
player->pflags |= (PF_THOKKED|PF_CANCARRY); if (player->bot == 1)
player->pflags |= PF_THOKKED;
else
player->pflags |= (PF_THOKKED|PF_CANCARRY);
} }
break; break;
case CA_GLIDEANDCLIMB: case CA_GLIDEANDCLIMB:
@ -5496,7 +5571,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
else else
potentialmomz = ((player->speed < 10*player->mo->scale) potentialmomz = ((player->speed < 10*player->mo->scale)
? (player->speed - 10*player->mo->scale)/5 ? (player->speed - 10*player->mo->scale)/5
: 0); : -1); // Should be 0, but made negative to ensure P_PlayerHitFloor runs upon touching ground
if (P_MobjFlip(player->mo)*player->mo->momz < potentialmomz) if (P_MobjFlip(player->mo)*player->mo->momz < potentialmomz)
player->mo->momz = P_MobjFlip(player->mo)*potentialmomz; player->mo->momz = P_MobjFlip(player->mo)*potentialmomz;
player->pflags &= ~PF_SPINNING; player->pflags &= ~PF_SPINNING;
@ -5891,6 +5966,8 @@ static void P_3dMovement(player_t *player)
// When sliding, don't allow forward/back // When sliding, don't allow forward/back
if (player->pflags & PF_SLIDING) if (player->pflags & PF_SLIDING)
cmd->forwardmove = 0; cmd->forwardmove = 0;
else if (onground && player->mo->state == states+S_PLAY_PAIN)
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
player->aiming = cmd->aiming<<FRACBITS; player->aiming = cmd->aiming<<FRACBITS;
@ -7705,6 +7782,9 @@ void P_ElementalFire(player_t *player, boolean cropcircle)
else else
ground = player->mo->floorz; ground = player->mo->floorz;
if (cropcircle)
ground += P_MobjFlip(player->mo);
if (cropcircle) if (cropcircle)
{ {
#define numangles 8 #define numangles 8
@ -7882,6 +7962,11 @@ static void P_MovePlayer(player_t *player)
cmd = &player->cmd; cmd = &player->cmd;
runspd = FixedMul(player->runspeed, player->mo->scale); runspd = FixedMul(player->runspeed, player->mo->scale);
// This was done in Sonic 3 & Knuckles, but has been missed in Sonic Mania and the Taxman/Stealth mobile remakes. Thanks to NeoHazard for his 2017 blogpost on the matter, because this oversight otherwise almost made it all the way to 2.2's release.
//https://s3unlocked.blogspot.com/2017/12/over-threshold.html
if (player->powers[pw_super])
runspd = FixedMul(runspd, 5*FRACUNIT/3);
// Let's have some movement speed fun on low-friction surfaces, JUST for players... (high friction surfaces shouldn't have any adjustment, since the acceleration in this game is super high and that ends up cheesing high-friction surfaces.) // Let's have some movement speed fun on low-friction surfaces, JUST for players... (high friction surfaces shouldn't have any adjustment, since the acceleration in this game is super high and that ends up cheesing high-friction surfaces.)
runspd = FixedMul(runspd, player->mo->movefactor); runspd = FixedMul(runspd, player->mo->movefactor);
@ -8364,7 +8449,7 @@ static void P_MovePlayer(player_t *player)
// Tails Put-Put noise // Tails Put-Put noise
if (player->charability == CA_FLY if (player->charability == CA_FLY
&& player->bot != 1 && (player->pflags & PF_CANCARRY)
&& !(player->mo->eflags & MFE_UNDERWATER) && !(player->mo->eflags & MFE_UNDERWATER)
&& leveltime % 10 == 0 && leveltime % 10 == 0
&& !player->spectator) && !player->spectator)
@ -9420,7 +9505,6 @@ static void P_DeathThink(player_t *player)
} }
else if ((netgame || multiplayer) && player->deadtimer >= 8*TICRATE) else if ((netgame || multiplayer) && player->deadtimer >= 8*TICRATE)
{ {
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
@ -9590,8 +9674,25 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
mo = player->mo; mo = player->mo;
if (player->exiting && mo->target && mo->target->type == MT_SIGN) if (player->playerstate == PST_REBORN)
sign = mo->target; {
P_CalcChasePostImg(player, thiscam);
return true;
}
if (player->exiting)
{
if (mo->target && mo->target->type == MT_SIGN && mo->target->spawnpoint
&& !(gametype == GT_COOP && (netgame || multiplayer) && cv_exitmove.value))
sign = mo->target;
else if ((player->powers[pw_carry] == CR_NIGHTSMODE)
&& !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1]
&& player->mo->state <= &states[S_PLAY_NIGHTS_TRANS6]))
{
P_CalcChasePostImg(player, thiscam);
return true;
}
}
cameranoclip = (player->powers[pw_carry] == CR_NIGHTSMODE || player->pflags & PF_NOCLIP) || (mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)); // Noclipping player camera noclips too!! cameranoclip = (player->powers[pw_carry] == CR_NIGHTSMODE || player->pflags & PF_NOCLIP) || (mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)); // Noclipping player camera noclips too!!
@ -9671,7 +9772,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
camorbit = (!stricmp(cv_cam_orbit.defaultvalue, "off")) ? false : true; camorbit = (!stricmp(cv_cam_orbit.defaultvalue, "off")) ? false : true;
camrotate = atoi(cv_cam_rotate.defaultvalue); camrotate = atoi(cv_cam_rotate.defaultvalue);
camdist = FixedMul((INT32)(atof(cv_cam_dist.defaultvalue) * FRACUNIT), mo->scale); camdist = FixedMul((INT32)(atof(cv_cam_dist.defaultvalue) * FRACUNIT), mo->scale);
camheight = FixedMul((INT32)(atof(cv_cam_height.defaultvalue) * FRACUNIT), FixedMul(player->camerascale, mo->scale)); camheight = FixedMul((INT32)(atof(cv_cam_height.defaultvalue) * FRACUNIT), mo->scale);
} }
else if (thiscam == &camera) else if (thiscam == &camera)
{ {
@ -9680,7 +9781,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
camorbit = cv_cam_orbit.value; camorbit = cv_cam_orbit.value;
camrotate = cv_cam_rotate.value; camrotate = cv_cam_rotate.value;
camdist = FixedMul(cv_cam_dist.value, mo->scale); camdist = FixedMul(cv_cam_dist.value, mo->scale);
camheight = FixedMul(cv_cam_height.value, FixedMul(player->camerascale, mo->scale)); camheight = FixedMul(cv_cam_height.value, mo->scale);
} }
else // Camera 2 else // Camera 2
{ {
@ -9689,9 +9790,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
camorbit = cv_cam2_orbit.value; camorbit = cv_cam2_orbit.value;
camrotate = cv_cam2_rotate.value; camrotate = cv_cam2_rotate.value;
camdist = FixedMul(cv_cam2_dist.value, mo->scale); camdist = FixedMul(cv_cam2_dist.value, mo->scale);
camheight = FixedMul(cv_cam2_height.value, FixedMul(player->camerascale, mo->scale)); camheight = FixedMul(cv_cam2_height.value, mo->scale);
} }
if (!(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE))
camheight = FixedMul(camheight, player->camerascale);
#ifdef REDSANALOG #ifdef REDSANALOG
if (P_AnalogMove(player) && (player->cmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT)) { if (P_AnalogMove(player) && (player->cmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT)) {
camstill = true; camstill = true;
@ -9802,9 +9906,10 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
dist <<= 1; dist <<= 1;
} }
if (!(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE))
dist = FixedMul(dist, player->camerascale);
checkdist = dist;
checkdist = (dist = FixedMul(dist, player->camerascale));
if (checkdist < 128*FRACUNIT) if (checkdist < 128*FRACUNIT)
checkdist = 128*FRACUNIT; checkdist = 128*FRACUNIT;
@ -9889,10 +9994,20 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
pviewheight = FixedMul(41*player->height/48, mo->scale); pviewheight = FixedMul(41*player->height/48, mo->scale);
if (mo->eflags & MFE_VERTICALFLIP) if (sign)
z = mo->z + mo->height - pviewheight - camheight + distz; {
if (mo->eflags & MFE_VERTICALFLIP)
z = sign->ceilingz - pviewheight - camheight;
else
z = sign->floorz + pviewheight + camheight;
}
else else
z = mo->z + pviewheight + camheight + distz; {
if (mo->eflags & MFE_VERTICALFLIP)
z = mo->z + mo->height - pviewheight - camheight + distz;
else
z = mo->z + pviewheight + camheight + distz;
}
// move camera down to move under lower ceilings // move camera down to move under lower ceilings
newsubsec = R_IsPointInSubsector(((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1), ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1)); newsubsec = R_IsPointInSubsector(((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1), ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1));
@ -10112,17 +10227,6 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
if (!camstill && !resetcalled && !paused) if (!camstill && !resetcalled && !paused)
thiscam->angle = R_PointToAngle2(thiscam->x, thiscam->y, viewpointx, viewpointy); thiscam->angle = R_PointToAngle2(thiscam->x, thiscam->y, viewpointx, viewpointy);
if (sign)
{
viewpointx = sign->x + FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
viewpointy = sign->y + FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
}
else
{
viewpointx = mo->x + FixedMul(FINECOSINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
viewpointy = mo->y + FixedMul(FINESINE((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
}
/* /*
if (twodlevel || (mo->flags2 & MF2_TWOD)) if (twodlevel || (mo->flags2 & MF2_TWOD))
thiscam->angle = angle; thiscam->angle = angle;
@ -10166,9 +10270,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
dist = FixedHypot(f1, f2); dist = FixedHypot(f1, f2);
if (mo->eflags & MFE_VERTICALFLIP) if (mo->eflags & MFE_VERTICALFLIP)
angle = R_PointToAngle2(0, thiscam->z + thiscam->height, dist, mo->z + mo->height - P_GetPlayerHeight(player)); angle = R_PointToAngle2(0, thiscam->z + thiscam->height, dist, (sign ? sign->ceilingz : mo->z + mo->height) - P_GetPlayerHeight(player));
else else
angle = R_PointToAngle2(0, thiscam->z, dist, mo->z + P_GetPlayerHeight(player)); angle = R_PointToAngle2(0, thiscam->z, dist, (sign ? sign->floorz : mo->z) + P_GetPlayerHeight(player));
if (player->playerstate != PST_DEAD) if (player->playerstate != PST_DEAD)
angle += (focusaiming < ANGLE_180 ? focusaiming/2 : InvAngle(InvAngle(focusaiming)/2)); // overcomplicated version of '((signed)focusaiming)/2;' angle += (focusaiming < ANGLE_180 ? focusaiming/2 : InvAngle(InvAngle(focusaiming)/2)); // overcomplicated version of '((signed)focusaiming)/2;'
@ -10328,6 +10432,7 @@ boolean P_SpectatorJoinGame(player_t *player)
return false; return false;
} }
// the below is first person only, if you're curious. check out P_CalcChasePostImg in p_mobj.c for chasecam
static void P_CalcPostImg(player_t *player) static void P_CalcPostImg(player_t *player)
{ {
sector_t *sector = player->mo->subsector->sector; sector_t *sector = player->mo->subsector->sector;
@ -11400,6 +11505,14 @@ void P_PlayerThink(player_t *player)
} }
} }
if (player->pflags & PF_FINISHED)
{
if ((gametype == GT_COOP && cv_exitmove.value) && !G_EnoughPlayersFinished())
player->exiting = 0;
else
P_DoPlayerExit(player);
}
// check water content, set stuff in mobj // check water content, set stuff in mobj
P_MobjCheckWater(player->mo); P_MobjCheckWater(player->mo);
@ -12276,7 +12389,7 @@ void P_PlayerAfterThink(player_t *player)
player->mo->momz = tails->momz; player->mo->momz = tails->momz;
} }
if (gametype == GT_COOP) if (gametype == GT_COOP && (!tails->player || tails->player->bot != 1))
{ {
player->mo->angle = tails->angle; player->mo->angle = tails->angle;

View File

@ -24,6 +24,7 @@
#include "z_zone.h" #include "z_zone.h"
#include "p_setup.h" // levelflats #include "p_setup.h" // levelflats
#include "v_video.h" // pMasterPalette #include "v_video.h" // pMasterPalette
#include "f_finale.h" // wipes
#include "byteptr.h" #include "byteptr.h"
#include "dehacked.h" #include "dehacked.h"
@ -113,6 +114,7 @@ INT32 *texturetranslation;
sprcache_t *spritecachedinfo; sprcache_t *spritecachedinfo;
lighttable_t *colormaps; lighttable_t *colormaps;
lighttable_t *fadecolormap;
// for debugging/info purposes // for debugging/info purposes
size_t flatmemory, spritememory, texturememory; size_t flatmemory, spritememory, texturememory;
@ -1455,18 +1457,111 @@ static void R_InitSpriteLumps(void)
Z_Malloc(max_spritelumps*sizeof(*spritecachedinfo), PU_STATIC, &spritecachedinfo); Z_Malloc(max_spritelumps*sizeof(*spritecachedinfo), PU_STATIC, &spritecachedinfo);
} }
//
// R_CreateFadeColormaps
//
static void R_CreateFadeColormaps(void)
{
UINT8 px, fade;
RGBA_t rgba;
INT32 r, g, b;
size_t len, i;
len = (256 * FADECOLORMAPROWS);
fadecolormap = Z_MallocAlign(len*2, PU_STATIC, NULL, 8);
for (i = 0; i < len*2; i++)
fadecolormap[i] = (i%256);
// Load in the light tables, now 64k aligned for smokie...
{
lumpnum_t lump = W_CheckNumForName("FADECMAP");
lumpnum_t wlump = W_CheckNumForName("FADEWMAP");
// to black
if (lump != LUMPERROR)
W_ReadLumpHeader(lump, fadecolormap, len, 0U);
// to white
if (wlump != LUMPERROR)
W_ReadLumpHeader(wlump, fadecolormap+len, len, 0U);
// missing "to white" colormap lump
if (lump != LUMPERROR && wlump == LUMPERROR)
goto makewhite;
// missing "to black" colormap lump
else if (lump == LUMPERROR && wlump != LUMPERROR)
goto makeblack;
// both lumps found
else if (lump != LUMPERROR && wlump != LUMPERROR)
return;
}
#define GETCOLOR \
px = colormaps[i%256]; \
fade = (i/256) * (256 / FADECOLORMAPROWS); \
rgba = V_GetColor(px);
// to black
makeblack:
for (i = 0; i < len; i++)
{
// find pixel and fade amount
GETCOLOR;
// subtractive color blending
r = rgba.s.red - FADEREDFACTOR*fade/10;
g = rgba.s.green - FADEGREENFACTOR*fade/10;
b = rgba.s.blue - FADEBLUEFACTOR*fade/10;
// clamp values
if (r < 0) r = 0;
if (g < 0) g = 0;
if (b < 0) b = 0;
// find nearest color in palette
fadecolormap[i] = NearestColor(r,g,b);
}
// to white
makewhite:
for (i = len; i < len*2; i++)
{
// find pixel and fade amount
GETCOLOR;
// additive color blending
r = rgba.s.red + FADEREDFACTOR*fade/10;
g = rgba.s.green + FADEGREENFACTOR*fade/10;
b = rgba.s.blue + FADEBLUEFACTOR*fade/10;
// clamp values
if (r > 255) r = 255;
if (g > 255) g = 255;
if (b > 255) b = 255;
// find nearest color in palette
fadecolormap[i] = NearestColor(r,g,b);
}
#undef GETCOLOR
}
// //
// R_InitColormaps // R_InitColormaps
// //
static void R_InitColormaps(void) static void R_InitColormaps(void)
{ {
size_t len;
lumpnum_t lump; lumpnum_t lump;
// Load in the light tables // Load in the light tables
lump = W_GetNumForName("COLORMAP"); lump = W_GetNumForName("COLORMAP");
colormaps = Z_MallocAlign(W_LumpLength (lump), PU_STATIC, NULL, 8); len = W_LumpLength(lump);
colormaps = Z_MallocAlign(len, PU_STATIC, NULL, 8);
W_ReadLump(lump, colormaps); W_ReadLump(lump, colormaps);
// Make colormap for fades
R_CreateFadeColormaps();
// Init Boom colormaps. // Init Boom colormaps.
R_ClearColormaps(); R_ClearColormaps();
#ifdef EXTRACOLORMAPLUMPS #ifdef EXTRACOLORMAPLUMPS
@ -1495,6 +1590,9 @@ void R_ReInitColormaps(UINT16 num)
} }
W_ReadLumpHeader(lump, colormaps, W_LumpLength(basecolormaplump), 0U); W_ReadLumpHeader(lump, colormaps, W_LumpLength(basecolormaplump), 0U);
if (fadecolormap)
Z_Free(fadecolormap);
R_CreateFadeColormaps();
// Init Boom colormaps. // Init Boom colormaps.
R_ClearColormaps(); R_ClearColormaps();

View File

@ -410,6 +410,8 @@ typedef enum
ST_NEGATIVE ST_NEGATIVE
} slopetype_t; } slopetype_t;
#define HORIZONSPECIAL 41
typedef struct line_s typedef struct line_s
{ {
// Vertices, from v1 to v2. // Vertices, from v1 to v2.

View File

@ -123,8 +123,12 @@ consvar_t cv_chasecam2 = {"chasecam2", "On", CV_CALL, CV_OnOff, ChaseCam2_OnChan
consvar_t cv_flipcam = {"flipcam", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_flipcam = {"flipcam", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam2_OnChange, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo, FlipCam2_OnChange, 0, NULL, NULL, 0, 0, NULL};
#if defined(FLOORSPLATS) || defined(GLBADSHADOWS)
consvar_t cv_shadow = {"shadow", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_shadow = {"shadow", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif //#if defined(FLOORSPLATS) || defined(GLBADSHADOWS)
#ifdef GLBADSHADOWS
consvar_t cv_shadowoffs = {"offsetshadows", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_shadowoffs = {"offsetshadows", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif //#ifdef GLBADSHADOWS
consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_showhud = {"showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_showhud = {"showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize, 0, NULL, NULL, 0, 0, NULL};
@ -1172,8 +1176,12 @@ void R_RegisterEngineStuff(void)
CV_RegisterVar(&cv_chasecam); CV_RegisterVar(&cv_chasecam);
CV_RegisterVar(&cv_chasecam2); CV_RegisterVar(&cv_chasecam2);
#if defined(FLOORSPLATS) || defined(GLBADSHADOWS)
CV_RegisterVar(&cv_shadow); CV_RegisterVar(&cv_shadow);
#endif //#if defined(FLOORSPLATS) || defined(GLBADSHADOWS)
#ifdef GLBADSHADOWS
CV_RegisterVar(&cv_shadowoffs); CV_RegisterVar(&cv_shadowoffs);
#endif //#ifdef GLBADSHADOWS
CV_RegisterVar(&cv_skybox); CV_RegisterVar(&cv_skybox);
CV_RegisterVar(&cv_cam_dist); CV_RegisterVar(&cv_cam_dist);

View File

@ -76,7 +76,12 @@ extern consvar_t cv_showhud, cv_translucenthud;
extern consvar_t cv_homremoval; extern consvar_t cv_homremoval;
extern consvar_t cv_chasecam, cv_chasecam2; extern consvar_t cv_chasecam, cv_chasecam2;
extern consvar_t cv_flipcam, cv_flipcam2; extern consvar_t cv_flipcam, cv_flipcam2;
extern consvar_t cv_shadow, cv_shadowoffs; #if defined(FLOORSPLATS) || defined(GLBADSHADOWS)
extern consvar_t cv_shadow;
#endif
#ifdef GLBADSHADOWS
extern conscar_t cv_shadowoffs;
#endif //#ifdef GLBADSHADOWS
extern consvar_t cv_translucency; extern consvar_t cv_translucency;
extern consvar_t cv_drawdist, cv_drawdist_nights, cv_drawdist_precip; extern consvar_t cv_drawdist, cv_drawdist_nights, cv_drawdist_precip;
extern consvar_t cv_skybox; extern consvar_t cv_skybox;

View File

@ -1007,6 +1007,8 @@ void R_DrawSinglePlane(visplane_t *pl)
R_CheckFlatLength(W_LumpLength(levelflat->u.flat.lumpnum)); R_CheckFlatLength(W_LumpLength(levelflat->u.flat.lumpnum));
// Raw flats always have dimensions that are powers-of-two numbers. // Raw flats always have dimensions that are powers-of-two numbers.
ds_powersoftwo = true; ds_powersoftwo = true;
if (spanfunc == basespanfunc)
spanfunc = mmxspanfunc;
break; break;
default: default:
switch (type) switch (type)

View File

@ -2694,7 +2694,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
worldbottomslope >>= 4; worldbottomslope >>= 4;
#endif #endif
if (linedef->special == 41) { // HORIZON LINES if (linedef->special == HORIZONSPECIAL) { // HORIZON LINES
topstep = bottomstep = 0; topstep = bottomstep = 0;
topfrac = bottomfrac = (centeryfrac>>4); topfrac = bottomfrac = (centeryfrac>>4);
topfrac++; // Prevent 1px HOM topfrac++; // Prevent 1px HOM
@ -2825,7 +2825,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
#ifdef ESLOPE #ifdef ESLOPE
ffloor[i].f_pos_slope >>= 4; ffloor[i].f_pos_slope >>= 4;
#endif #endif
if (linedef->special == 41) // Horizon lines extend FOFs in contact with them too. if (linedef->special == HORIZONSPECIAL) // Horizon lines extend FOFs in contact with them too.
{ {
ffloor[i].f_step = 0; ffloor[i].f_step = 0;
ffloor[i].f_frac = (centeryfrac>>4); ffloor[i].f_frac = (centeryfrac>>4);

View File

@ -38,6 +38,7 @@ typedef struct
extern sprcache_t *spritecachedinfo; extern sprcache_t *spritecachedinfo;
extern lighttable_t *colormaps; extern lighttable_t *colormaps;
extern lighttable_t *fadecolormap;
// Boom colormaps. // Boom colormaps.
extern extracolormap_t *extra_colormaps; extern extracolormap_t *extra_colormaps;

View File

@ -1137,8 +1137,6 @@ static void R_ProjectSprite(mobj_t *thing)
UINT32 rollangle = AngleFixed(arollangle)>>FRACBITS; UINT32 rollangle = AngleFixed(arollangle)>>FRACBITS;
#endif #endif
fixed_t ang_scale = FRACUNIT;
// transform the origin point // transform the origin point
tr_x = thing->x - viewx; tr_x = thing->x - viewx;
tr_y = thing->y - viewy; tr_y = thing->y - viewy;
@ -1196,20 +1194,20 @@ static void R_ProjectSprite(mobj_t *thing)
#ifdef ROTSPRITE #ifdef ROTSPRITE
sprinfo = NULL; sprinfo = NULL;
#endif #endif
}
if (rot >= sprdef->numframes) if (rot >= sprdef->numframes)
{
CONS_Alert(CONS_ERROR, M_GetText("R_ProjectSprite: invalid sprite frame %s/%s for %s\n"),
sizeu1(rot), sizeu2(sprdef->numframes), sprnames[thing->sprite]);
thing->sprite = states[S_UNKNOWN].sprite;
thing->frame = states[S_UNKNOWN].frame;
sprdef = &sprites[thing->sprite];
rot = thing->frame&FF_FRAMEMASK;
if (!thing->skin)
{ {
thing->state->sprite = thing->sprite; CONS_Alert(CONS_ERROR, M_GetText("R_ProjectSprite: invalid sprite frame %s/%s for %s\n"),
thing->state->frame = thing->frame; sizeu1(rot), sizeu2(sprdef->numframes), sprnames[thing->sprite]);
if (thing->sprite == thing->state->sprite && thing->frame == thing->state->frame)
{
thing->state->sprite = states[S_UNKNOWN].sprite;
thing->state->frame = states[S_UNKNOWN].frame;
}
thing->sprite = states[S_UNKNOWN].sprite;
thing->frame = states[S_UNKNOWN].frame;
sprdef = &sprites[thing->sprite];
rot = thing->frame&FF_FRAMEMASK;
} }
} }
@ -1223,8 +1221,6 @@ static void R_ProjectSprite(mobj_t *thing)
if (sprframe->rotate != SRF_SINGLE || papersprite) if (sprframe->rotate != SRF_SINGLE || papersprite)
{ {
ang = R_PointToAngle (thing->x, thing->y) - (thing->player ? thing->player->drawangle : thing->angle); ang = R_PointToAngle (thing->x, thing->y) - (thing->player ? thing->player->drawangle : thing->angle);
if (papersprite)
ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT));
} }
if (sprframe->rotate == SRF_SINGLE) if (sprframe->rotate == SRF_SINGLE)
@ -1286,24 +1282,11 @@ static void R_ProjectSprite(mobj_t *thing)
else else
offset = -spr_offset; offset = -spr_offset;
offset = FixedMul(offset, this_scale); offset = FixedMul(offset, this_scale);
tx += FixedMul(offset, ang_scale);
x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS;
// off the right side?
if (x1 > viewwidth)
return;
offset2 = FixedMul(spr_width, this_scale); offset2 = FixedMul(spr_width, this_scale);
tx += FixedMul(offset2, ang_scale);
x2 = ((centerxfrac + FixedMul (tx,xscale)) >> FRACBITS) - (papersprite ? 2 : 1);
// off the left side
if (x2 < 0)
return;
if (papersprite) if (papersprite)
{ {
fixed_t yscale2, cosmul, sinmul, tz2; fixed_t xscale2, yscale2, cosmul, sinmul, tz2;
INT32 range; INT32 range;
if (ang >= ANGLE_180) if (ang >= ANGLE_180)
@ -1323,6 +1306,16 @@ static void R_ProjectSprite(mobj_t *thing)
yscale = FixedDiv(projectiony, tz); yscale = FixedDiv(projectiony, tz);
if (yscale < 64) return; // Fix some funky visuals if (yscale < 64) return; // Fix some funky visuals
gxt = -FixedMul(tr_x, viewsin);
gyt = FixedMul(tr_y, viewcos);
tx = -(gyt + gxt);
xscale = FixedDiv(projection, tz);
x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
// off the right side?
if (x1 > viewwidth)
return;
tr_x += FixedMul(offset2, cosmul); tr_x += FixedMul(offset2, cosmul);
tr_y += FixedMul(offset2, sinmul); tr_y += FixedMul(offset2, sinmul);
gxt = FixedMul(tr_x, viewcos); gxt = FixedMul(tr_x, viewcos);
@ -1331,15 +1324,25 @@ static void R_ProjectSprite(mobj_t *thing)
yscale2 = FixedDiv(projectiony, tz2); yscale2 = FixedDiv(projectiony, tz2);
if (yscale2 < 64) return; // ditto if (yscale2 < 64) return; // ditto
gxt = -FixedMul(tr_x, viewsin);
gyt = FixedMul(tr_y, viewcos);
tx = -(gyt + gxt);
xscale2 = FixedDiv(projection, tz2);
x2 = (centerxfrac + FixedMul(tx,xscale2))>>FRACBITS; x2--;
// off the left side
if (x2 < 0)
return;
if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-papersprite clipping is handled earlier if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-papersprite clipping is handled earlier
return; return;
if (x2 > x1) if ((range = x2 - x1) <= 0)
range = (x2 - x1);
else
range = 1; range = 1;
scalestep = (yscale2 - yscale)/range ?: 1; scalestep = (yscale2 - yscale)/range;
scalestep = scalestep ? scalestep : 1;
xscale = FixedDiv(range<<FRACBITS, abs(offset2))+1;
// The following two are alternate sorting methods which might be more applicable in some circumstances. TODO - maybe enable via MF2? // The following two are alternate sorting methods which might be more applicable in some circumstances. TODO - maybe enable via MF2?
// sortscale = max(yscale, yscale2); // sortscale = max(yscale, yscale2);
@ -1349,9 +1352,20 @@ static void R_ProjectSprite(mobj_t *thing)
{ {
scalestep = 0; scalestep = 0;
yscale = sortscale; yscale = sortscale;
} tx += offset;
x1 = (centerxfrac + FixedMul(tx,xscale))>>FRACBITS;
xscale = FixedMul(xscale, ang_scale); // off the right side?
if (x1 > viewwidth)
return;
tx += offset2;
x2 = ((centerxfrac + FixedMul(tx,xscale))>>FRACBITS); x2--;
// off the left side
if (x2 < 0)
return;
}
if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY) if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY)
{ {

View File

@ -860,7 +860,6 @@ static INT32 actualmidimusicvolume;
void S_UpdateSounds(void) void S_UpdateSounds(void)
{ {
INT32 audible, cnum, volume, sep, pitch; INT32 audible, cnum, volume, sep, pitch;
UINT8 i;
channel_t *c; channel_t *c;
listener_t listener; listener_t listener;
@ -1017,28 +1016,30 @@ void S_UpdateSounds(void)
notinlevel: notinlevel:
I_UpdateSound(); I_UpdateSound();
}
void S_UpdateClosedCaptions(void)
{
UINT8 i;
boolean gamestopped = (paused || P_AutoPause());
for (i = 0; i < NUMCAPTIONS; i++) // update captions
{ {
boolean gamestopped = (paused || P_AutoPause()); if (!closedcaptions[i].s)
for (i = 0; i < NUMCAPTIONS; i++) // update captions continue;
if (i == 0 && (closedcaptions[0].s-S_sfx == sfx_None) && gamestopped)
continue;
if (!(--closedcaptions[i].t))
{ {
if (!closedcaptions[i].s) closedcaptions[i].c = NULL;
continue; closedcaptions[i].s = NULL;
}
if (i == 0 && (closedcaptions[0].s-S_sfx == sfx_None) && gamestopped) else if (closedcaptions[i].c && !I_SoundIsPlaying(closedcaptions[i].c->handle))
continue; {
closedcaptions[i].c = NULL;
if (!(--closedcaptions[i].t)) if (closedcaptions[i].t > CAPTIONFADETICS)
{ closedcaptions[i].t = CAPTIONFADETICS;
closedcaptions[i].c = NULL;
closedcaptions[i].s = NULL;
}
else if (closedcaptions[i].c && !I_SoundIsPlaying(closedcaptions[i].c->handle))
{
closedcaptions[i].c = NULL;
if (closedcaptions[i].t > CAPTIONFADETICS)
closedcaptions[i].t = CAPTIONFADETICS;
}
} }
} }
} }
@ -1685,7 +1686,7 @@ boolean S_PrepareSoundTest(void)
soundtestdefs[pos++] = def; soundtestdefs[pos++] = def;
if (def->soundtestcond > 0 && !(mapvisited[def->soundtestcond-1] & MV_BEATEN)) if (def->soundtestcond > 0 && !(mapvisited[def->soundtestcond-1] & MV_BEATEN))
continue; continue;
if (def->soundtestcond < 0 && !M_Achieved(1-def->soundtestcond)) if (def->soundtestcond < 0 && !M_Achieved(-1-def->soundtestcond))
continue; continue;
def->allowed = true; def->allowed = true;
} }

View File

@ -303,6 +303,7 @@ boolean S_FadeOutStopMusic(UINT32 ms);
// Updates music & sounds // Updates music & sounds
// //
void S_UpdateSounds(void); void S_UpdateSounds(void);
void S_UpdateClosedCaptions(void);
FUNCMATH fixed_t S_CalculateSoundDistance(fixed_t px1, fixed_t py1, fixed_t pz1, fixed_t px2, fixed_t py2, fixed_t pz2); FUNCMATH fixed_t S_CalculateSoundDistance(fixed_t px1, fixed_t py1, fixed_t pz1, fixed_t px2, fixed_t py2, fixed_t pz2);

View File

@ -421,13 +421,24 @@ void SCR_DisplayTicRate(void)
else if (totaltics == TICRATE) ticcntcolor = V_GREENMAP; else if (totaltics == TICRATE) ticcntcolor = V_GREENMAP;
V_DrawString(vid.width-(72*vid.dupx), h, V_DrawString(vid.width-(72*vid.dupx), h,
V_YELLOWMAP|V_NOSCALESTART|V_HUDTRANS, "FPS:"); V_YELLOWMAP|V_NOSCALESTART|V_USERHUDTRANS, "FPS:");
V_DrawString(vid.width-(40*vid.dupx), h, V_DrawString(vid.width-(40*vid.dupx), h,
ticcntcolor|V_NOSCALESTART|V_HUDTRANS, va("%02d/%02u", totaltics, TICRATE)); ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%02d/%02u", totaltics, TICRATE));
lasttic = ontic; lasttic = ontic;
} }
void SCR_DisplayLocalPing(void)
{
UINT32 ping = playerpingtable[consoleplayer]; // consoleplayer's ping is everyone's ping in a splitnetgame :P
if (cv_showping.value == 1 || (cv_showping.value == 2 && servermaxping && ping > servermaxping)) // only show 2 (warning) if our ping is at a bad level
{
INT32 dispy = cv_ticrate.value ? 180 : 189;
HU_drawPing(307, dispy, ping, true, V_SNAPTORIGHT | V_SNAPTOBOTTOM);
}
}
void SCR_ClosedCaptions(void) void SCR_ClosedCaptions(void)
{ {
UINT8 i; UINT8 i;

View File

@ -167,5 +167,6 @@ FUNCMATH boolean SCR_IsAspectCorrect(INT32 width, INT32 height);
// move out to main code for consistency // move out to main code for consistency
void SCR_DisplayTicRate(void); void SCR_DisplayTicRate(void);
void SCR_ClosedCaptions(void); void SCR_ClosedCaptions(void);
void SCR_DisplayLocalPing(void);
#undef DNWH #undef DNWH
#endif //__SCREEN_H__ #endif //__SCREEN_H__

View File

@ -1176,11 +1176,11 @@ void I_FinishUpdate(void)
if (cv_closedcaptioning.value) if (cv_closedcaptioning.value)
SCR_ClosedCaptions(); SCR_ClosedCaptions();
if (st_overlay) if (cv_ticrate.value)
{ SCR_DisplayTicRate();
if (cv_ticrate.value)
SCR_DisplayTicRate(); if (cv_showping.value && netgame && consoleplayer != serverplayer)
} SCR_DisplayLocalPing();
if (rendermode == render_soft && screens[0]) if (rendermode == render_soft && screens[0])
{ {

View File

@ -92,7 +92,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"pstop", false, 100, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Crusher stomp"}, {"pstop", false, 100, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Crusher stomp"},
{"steam1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Steam jet"}, // Tails 06-19-2001 {"steam1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Steam jet"}, // Tails 06-19-2001
{"steam2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Steam jet"}, // Tails 06-19-2001 {"steam2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Steam jet"}, // Tails 06-19-2001
{"wbreak", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Wood breaking"}, {"wbreak", true, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Wood breaking"},
{"ambmac", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Machinery"}, {"ambmac", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Machinery"},
{"spsmsh", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Heavy impact"}, {"spsmsh", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Heavy impact"},
@ -208,7 +208,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"shrpsp", true, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spincushion"}, {"shrpsp", true, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Spincushion"},
{"shrpgo", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Launch"}, {"shrpgo", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Launch"},
{"mswarp", false, 60, 16, -1, NULL, 0, -1, -1, LUMPERROR, "Spinning out"}, {"mswarp", false, 60, 16, -1, NULL, 0, -1, -1, LUMPERROR, "Spinning out"},
{"mspogo", false, 60, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Breaking through"}, {"mspogo", true, 60, 8, -1, NULL, 0, -1, -1, LUMPERROR, "Breaking through"},
{"boingf", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bouncing"}, {"boingf", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Bouncing"},
{"corkp", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Cork fired"}, {"corkp", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Cork fired"},
{"corkh", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Cork hit"}, {"corkh", false, 32, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Cork hit"},
@ -779,7 +779,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"kc49", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc49", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"kc4a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc4a", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"kc4b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc4b", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"kc4c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc4c", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Pop-shot"},
{"kc4d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Power up"}, {"kc4d", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Power up"},
{"kc4e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc4e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},
{"kc4f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc4f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""},

View File

@ -23,6 +23,7 @@
#include "v_video.h" #include "v_video.h"
#include "z_zone.h" #include "z_zone.h"
#include "hu_stuff.h" #include "hu_stuff.h"
#include "console.h"
#include "s_sound.h" #include "s_sound.h"
#include "i_system.h" #include "i_system.h"
#include "m_menu.h" #include "m_menu.h"
@ -186,14 +187,18 @@ boolean ST_SameTeam(player_t *a, player_t *b)
static boolean st_stopped = true; static boolean st_stopped = true;
void ST_Ticker(void) void ST_Ticker(boolean run)
{ {
if (st_stopped) if (st_stopped)
return; return;
if (run)
ST_runTitleCard();
} }
// 0 is default, any others are special palettes. // 0 is default, any others are special palettes.
INT32 st_palette = 0; INT32 st_palette = 0;
INT32 st_translucency = 10;
void ST_doPaletteStuff(void) void ST_doPaletteStuff(void)
{ {
@ -348,12 +353,12 @@ void ST_LoadGraphics(void)
// made separate so that skins code can reload custom face graphics // made separate so that skins code can reload custom face graphics
void ST_LoadFaceGraphics(INT32 skinnum) void ST_LoadFaceGraphics(INT32 skinnum)
{ {
if (skins[skinnum].sprites[SPR2_XTRA].numframes) if (skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_LIFEPIC)
{ {
spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA]; spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA];
spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_LIFEPIC]; spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_LIFEPIC];
faceprefix[skinnum] = W_CachePatchNum(sprframe->lumppat[0], PU_HUDGFX); faceprefix[skinnum] = W_CachePatchNum(sprframe->lumppat[0], PU_HUDGFX);
if (skins[skinnum].sprites[(SPR2_XTRA|FF_SPR2SUPER)].numframes) if (skins[skinnum].sprites[(SPR2_XTRA|FF_SPR2SUPER)].numframes > XTRA_LIFEPIC)
{ {
sprdef = &skins[skinnum].sprites[SPR2_XTRA|FF_SPR2SUPER]; sprdef = &skins[skinnum].sprites[SPR2_XTRA|FF_SPR2SUPER];
sprframe = &sprdef->spriteframes[0]; sprframe = &sprdef->spriteframes[0];
@ -818,7 +823,7 @@ static void ST_drawLivesArea(void)
face = superprefix[stplyr->skin]; face = superprefix[stplyr->skin];
V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y, V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y,
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, face, colormap); hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, face, colormap);
if (cv_translucenthud.value == 10 && stplyr->powers[pw_super] == 1 && stplyr->mo->tracer) if (st_translucency == 10 && stplyr->powers[pw_super] == 1 && stplyr->mo->tracer)
{ {
INT32 v_supertrans = (stplyr->mo->tracer->frame & FF_TRANSMASK) >> FF_TRANSSHIFT; INT32 v_supertrans = (stplyr->mo->tracer->frame & FF_TRANSMASK) >> FF_TRANSSHIFT;
if (v_supertrans < 10) if (v_supertrans < 10)
@ -1160,16 +1165,142 @@ static void ST_drawInput(void)
V_DrawThinString(x, y, hudinfo[HUD_LIVES].f|((leveltime & 4) ? V_YELLOWMAP : V_REDMAP), "BAD DEMO!!"); V_DrawThinString(x, y, hudinfo[HUD_LIVES].f|((leveltime & 4) ? V_YELLOWMAP : V_REDMAP), "BAD DEMO!!");
} }
void ST_drawLevelTitle(tic_t titletime) static patch_t *lt_patches[3];
static INT32 lt_scroll = 0;
static INT32 lt_mom = 0;
static INT32 lt_zigzag = 0;
tic_t lt_ticker = 0, lt_lasttic = 0;
tic_t lt_exitticker = 0, lt_endtime = 0;
//
// Load the graphics for the title card.
//
static void ST_cacheLevelTitle(void)
{
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_WARNINGTITLE))
{
lt_patches[0] = (patch_t *)W_CachePatchName("LTACTBLU", PU_HUDGFX);
lt_patches[1] = (patch_t *)W_CachePatchName("LTZIGZAG", PU_HUDGFX);
lt_patches[2] = (patch_t *)W_CachePatchName("LTZZTEXT", PU_HUDGFX);
}
else // boss map
{
lt_patches[0] = (patch_t *)W_CachePatchName("LTACTRED", PU_HUDGFX);
lt_patches[1] = (patch_t *)W_CachePatchName("LTZIGRED", PU_HUDGFX);
lt_patches[2] = (patch_t *)W_CachePatchName("LTZZWARN", PU_HUDGFX);
}
}
//
// Start the title card.
//
void ST_startTitleCard(void)
{
// cache every HUD patch used
ST_cacheLevelTitle();
// initialize HUD variables
lt_ticker = lt_exitticker = lt_lasttic = 0;
lt_endtime = 2*TICRATE + (10*NEWTICRATERATIO);
lt_scroll = BASEVIDWIDTH * FRACUNIT;
lt_zigzag = -((lt_patches[1])->width * FRACUNIT);
lt_mom = 0;
}
//
// What happens before drawing the title card.
// Which is just setting the HUD translucency.
//
void ST_preDrawTitleCard(void)
{
if (lt_ticker >= (lt_endtime + TICRATE))
return;
if (!lt_exitticker)
st_translucency = 0;
else
st_translucency = max(0, min((INT32)lt_exitticker-4, cv_translucenthud.value));
}
//
// Run the title card.
// Called from ST_Ticker.
//
void ST_runTitleCard(void)
{
if (lt_ticker >= (lt_endtime + TICRATE))
return;
if (!(paused || P_AutoPause()))
{
// tick
lt_ticker++;
if (lt_ticker >= lt_endtime)
lt_exitticker++;
// scroll to screen (level title)
if (!lt_exitticker)
{
if (abs(lt_scroll) > FRACUNIT)
lt_scroll -= (lt_scroll>>2);
else
lt_scroll = 0;
}
// scroll away from screen (level title)
else
{
lt_mom -= FRACUNIT*6;
lt_scroll += lt_mom;
}
// scroll to screen (zigzag)
if (!lt_exitticker)
{
if (abs(lt_zigzag) > FRACUNIT)
lt_zigzag -= (lt_zigzag>>2);
else
lt_zigzag = 0;
}
// scroll away from screen (zigzag)
else
lt_zigzag += lt_mom;
}
}
//
// Draw the title card itself.
//
void ST_drawTitleCard(void)
{ {
char *lvlttl = mapheaderinfo[gamemap-1]->lvlttl; char *lvlttl = mapheaderinfo[gamemap-1]->lvlttl;
char *subttl = mapheaderinfo[gamemap-1]->subttl; char *subttl = mapheaderinfo[gamemap-1]->subttl;
INT32 actnum = mapheaderinfo[gamemap-1]->actnum; INT32 actnum = mapheaderinfo[gamemap-1]->actnum;
INT32 lvlttly, zoney, lvlttlxpos, ttlnumxpos, zonexpos; INT32 lvlttlxpos, ttlnumxpos, zonexpos;
INT32 subttlxpos = BASEVIDWIDTH/2; INT32 subttlxpos = BASEVIDWIDTH/2;
INT32 ttlscroll = FixedInt(lt_scroll);
INT32 zzticker;
patch_t *actpat, *zigzag, *zztext;
if (!(titletime > 2 && titletime-3 < 110)) #ifdef HAVE_BLUA
if (!LUA_HudEnabled(hud_stagetitle))
goto luahook;
#endif
if (lt_ticker >= (lt_endtime + TICRATE))
#ifdef HAVE_BLUA
goto luahook;
#else
return; return;
#endif
if ((lt_ticker-lt_lasttic) > 1)
lt_ticker = lt_lasttic+1;
ST_cacheLevelTitle();
actpat = lt_patches[0];
zigzag = lt_patches[1];
zztext = lt_patches[2];
lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2)); lvlttlxpos = ((BASEVIDWIDTH/2) - (V_LevelNameWidth(lvlttl)/2));
@ -1177,72 +1308,71 @@ void ST_drawLevelTitle(tic_t titletime)
lvlttlxpos -= V_LevelActNumWidth(actnum); lvlttlxpos -= V_LevelActNumWidth(actnum);
ttlnumxpos = lvlttlxpos + V_LevelNameWidth(lvlttl); ttlnumxpos = lvlttlxpos + V_LevelNameWidth(lvlttl);
zonexpos = ttlnumxpos - V_LevelNameWidth(M_GetText("ZONE")); zonexpos = ttlnumxpos - V_LevelNameWidth(M_GetText("Zone"));
ttlnumxpos++; ttlnumxpos++;
if (lvlttlxpos < 0) if (lvlttlxpos < 0)
lvlttlxpos = 0; lvlttlxpos = 0;
#if 0 // toaster's experiment. srb2&toast.exe one day, maybe? Requires stuff below to be converted to fixed point. if (!splitscreen || (splitscreen && stplyr == &players[displayplayer]))
#define MIDTTLY 79
#define MIDZONEY 105
#define MIDDIFF 4
if (titletime < 10)
{ {
fixed_t z = ((titletime - 3)<<FRACBITS)/7; zzticker = lt_ticker;
INT32 ttlh = V_LevelNameHeight(lvlttl); V_DrawScaledPatch(FixedInt(lt_zigzag), (-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag);
zoney = (200<<FRACBITS) - ((200 - (MIDZONEY + MIDDIFF))*z); V_DrawScaledPatch(FixedInt(lt_zigzag), (zigzag->height-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag);
lvlttly = ((MIDTTLY + ttlh - MIDDIFF)*z) - (ttlh<<FRACBITS); V_DrawScaledPatch(FixedInt(lt_zigzag), (-zigzag->height+zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext);
V_DrawScaledPatch(FixedInt(lt_zigzag), (zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext);
} }
else if (titletime < 105)
{
fixed_t z = (((titletime - 10)*MIDDIFF)<<(FRACBITS+1))/95;
zoney = ((MIDZONEY + MIDDIFF)<<FRACBITS) - z;
lvlttly = ((MIDTTLY - MIDDIFF)<<FRACBITS) + z;
}
else
{
fixed_t z = ((titletime - 105)<<FRACBITS)/7;
INT32 zoneh = V_LevelNameHeight(M_GetText("ZONE"));
zoney = (MIDZONEY + zoneh - MIDDIFF)*(FRACUNIT - z) - (zoneh<<FRACBITS);
lvlttly = ((MIDTTLY + MIDDIFF)<<FRACBITS) + ((200 - (MIDTTLY + MIDDIFF))*z);
}
#undef MIDTTLY
#undef MIDZONEY
#undef MIDDIFF
#else
// There's no consistent algorithm that can accurately define the old positions
// so I just ended up resorting to a single switch statement to define them
switch (titletime-3)
{
case 0: zoney = 200; lvlttly = 0; break;
case 1: zoney = 188; lvlttly = 12; break;
case 2: zoney = 176; lvlttly = 24; break;
case 3: zoney = 164; lvlttly = 36; break;
case 4: zoney = 152; lvlttly = 48; break;
case 5: zoney = 140; lvlttly = 60; break;
case 6: zoney = 128; lvlttly = 72; break;
case 105: zoney = 80; lvlttly = 104; break;
case 106: zoney = 56; lvlttly = 128; break;
case 107: zoney = 32; lvlttly = 152; break;
case 108: zoney = 8; lvlttly = 176; break;
case 109: zoney = 0; lvlttly = 200; break;
default: zoney = 104; lvlttly = 80; break;
}
#endif
if (actnum) if (actnum)
V_DrawLevelActNum(ttlnumxpos, zoney, V_PERPLAYER, actnum); {
if (!splitscreen)
V_DrawLevelTitle(lvlttlxpos, lvlttly, V_PERPLAYER, lvlttl); V_DrawScaledPatch(ttlnumxpos + ttlscroll, 104 - ttlscroll, 0, actpat);
V_DrawLevelActNum(ttlnumxpos + ttlscroll, 104, V_PERPLAYER, actnum);
}
V_DrawLevelTitle(lvlttlxpos - ttlscroll, 80, V_PERPLAYER, lvlttl);
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
V_DrawLevelTitle(zonexpos, zoney, V_PERPLAYER, M_GetText("ZONE")); V_DrawLevelTitle(zonexpos + ttlscroll, 104, V_PERPLAYER, M_GetText("Zone"));
V_DrawCenteredString(subttlxpos - ttlscroll, 135, V_PERPLAYER|V_ALLOWLOWERCASE, subttl);
if (lvlttly+48 < 200) lt_lasttic = lt_ticker;
V_DrawCenteredString(subttlxpos, lvlttly+48, V_PERPLAYER|V_ALLOWLOWERCASE, subttl);
#ifdef HAVE_BLUA
luahook:
LUAh_TitleCardHUD(stplyr);
#endif
}
//
// Drawer for G_PreLevelTitleCard.
//
void ST_preLevelTitleCardDrawer(tic_t ticker, boolean update)
{
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol);
if (ticker < PRELEVELTIME-1)
ST_drawWipeTitleCard();
I_OsPolling();
I_UpdateNoBlit();
if (update)
I_FinishUpdate(); // page flip or blit buffer
}
//
// Draw the title card while on a wipe.
// Also used in G_PreLevelTitleCard.
//
void ST_drawWipeTitleCard(void)
{
stplyr = &players[consoleplayer];
ST_preDrawTitleCard();
ST_drawTitleCard();
if (splitscreen)
{
stplyr = &players[secondarydisplayplayer];
ST_preDrawTitleCard();
ST_drawTitleCard();
}
} }
static void ST_drawPowerupHUD(void) static void ST_drawPowerupHUD(void)
@ -2106,7 +2236,7 @@ static void ST_drawTextHUD(void)
textHUDdraw(M_GetText("\x82""FIRE:""\x80 Enter game")) textHUDdraw(M_GetText("\x82""FIRE:""\x80 Enter game"))
} }
if (gametype == GT_COOP && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && stplyr->exiting) if (gametype == GT_COOP && (!stplyr->spectator || (!(maptol & TOL_NIGHTS) && G_IsSpecialStage(gamemap))) && (stplyr->exiting || (stplyr->pflags & PF_FINISHED)))
{ {
UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value); UINT8 numneeded = (G_IsSpecialStage(gamemap) ? 4 : cv_playersforexit.value);
if (numneeded) if (numneeded)
@ -2121,7 +2251,7 @@ static void ST_drawTextHUD(void)
continue; continue;
total++; total++;
if (players[i].exiting) if (players[i].exiting || (players[i].pflags & PF_FINISHED))
exiting++; exiting++;
} }
@ -2394,12 +2524,25 @@ static void ST_doItemFinderIconsAndSound(void)
S_StartSound(NULL, sfx_emfind); S_StartSound(NULL, sfx_emfind);
} }
//
// Draw the status bar overlay, customisable: the user chooses which // Draw the status bar overlay, customisable: the user chooses which
// kind of information to overlay // kind of information to overlay
// //
static void ST_overlayDrawer(void) static void ST_overlayDrawer(void)
{ {
//hu_showscores = auto hide score/time/rings when tab rankings are shown // Decide whether to draw the stage title or not
boolean stagetitle = false;
// Check for a valid level title
// If the HUD is enabled
// And, if Lua is running, if the HUD library has the stage title enabled
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOTITLECARD) && *mapheaderinfo[gamemap-1]->lvlttl != '\0' && !(hu_showscores && (netgame || multiplayer)))
{
stagetitle = true;
ST_preDrawTitleCard();
}
// hu_showscores = auto hide score/time/rings when tab rankings are shown
if (!(hu_showscores && (netgame || multiplayer))) if (!(hu_showscores && (netgame || multiplayer)))
{ {
if ((maptol & TOL_NIGHTS || G_IsSpecialStage(gamemap)) && if ((maptol & TOL_NIGHTS || G_IsSpecialStage(gamemap)) &&
@ -2550,12 +2693,8 @@ static void ST_overlayDrawer(void)
#endif #endif
// draw level title Tails // draw level title Tails
if (*mapheaderinfo[gamemap-1]->lvlttl != '\0' && !(hu_showscores && (netgame || multiplayer)) if (stagetitle && (!WipeInAction) && (!WipeStageTitle))
#ifdef HAVE_BLUA ST_drawTitleCard();
&& LUA_HudEnabled(hud_stagetitle)
#endif
)
ST_drawLevelTitle(timeinmap+70);
if (!hu_showscores && (netgame || multiplayer) if (!hu_showscores && (netgame || multiplayer)
#ifdef HAVE_BLUA #ifdef HAVE_BLUA
@ -2631,6 +2770,8 @@ void ST_Drawer(void)
} }
} }
st_translucency = cv_translucenthud.value;
if (st_overlay) if (st_overlay)
{ {
// No deadview! // No deadview!

View File

@ -24,7 +24,7 @@
// //
// Called by main loop. // Called by main loop.
void ST_Ticker(void); void ST_Ticker(boolean run);
// Called by main loop. // Called by main loop.
void ST_Drawer(void); void ST_Drawer(void);
@ -47,8 +47,16 @@ void ST_ReloadSkinFaceGraphics(void);
void ST_doPaletteStuff(void); void ST_doPaletteStuff(void);
// level title draw // title card
void ST_drawLevelTitle(tic_t titletime); void ST_startTitleCard(void);
void ST_runTitleCard(void);
void ST_drawTitleCard(void);
void ST_preDrawTitleCard(void);
void ST_preLevelTitleCardDrawer(tic_t ticker, boolean update);
void ST_drawWipeTitleCard(void);
extern tic_t lt_ticker, lt_lasttic;
extern tic_t lt_exitticker, lt_endtime;
// return if player a is in the same team as player b // return if player a is in the same team as player b
boolean ST_SameTeam(player_t *a, player_t *b); boolean ST_SameTeam(player_t *a, player_t *b);
@ -59,6 +67,7 @@ boolean ST_SameTeam(player_t *a, player_t *b);
extern boolean st_overlay; // sb overlay on or off when fullscreen extern boolean st_overlay; // sb overlay on or off when fullscreen
extern INT32 st_palette; // 0 is default, any others are special palettes. extern INT32 st_palette; // 0 is default, any others are special palettes.
extern INT32 st_translucency;
extern lumpnum_t st_borderpatchnum; extern lumpnum_t st_borderpatchnum;
// patches, also used in intermission // patches, also used in intermission

View File

@ -75,7 +75,7 @@ strcasestr (const char *s, const char *q)
if (!( (intptr_t)up|(intptr_t)lp )) if (!( (intptr_t)up|(intptr_t)lp ))
return 0; return 0;
if (!lp || up < lp) if (!lp || ( up && up < lp ))
{ {
ppa = &up; ppa = &up;
ppb = &lp; ppb = &lp;

View File

@ -18,7 +18,9 @@
#include "p_local.h" // stplyr #include "p_local.h" // stplyr
#include "g_game.h" // players #include "g_game.h" // players
#include "v_video.h" #include "v_video.h"
#include "st_stuff.h"
#include "hu_stuff.h" #include "hu_stuff.h"
#include "f_finale.h"
#include "r_draw.h" #include "r_draw.h"
#include "console.h" #include "console.h"
@ -574,11 +576,11 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca
if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT))) if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
{ {
if (alphalevel == 13) if (alphalevel == 13)
alphalevel = hudminusalpha[cv_translucenthud.value]; alphalevel = hudminusalpha[st_translucency];
else if (alphalevel == 14) else if (alphalevel == 14)
alphalevel = 10 - cv_translucenthud.value; alphalevel = 10 - st_translucency;
else if (alphalevel == 15) else if (alphalevel == 15)
alphalevel = hudplusalpha[cv_translucenthud.value]; alphalevel = hudplusalpha[st_translucency];
if (alphalevel >= 10) if (alphalevel >= 10)
return; // invis return; // invis
@ -874,11 +876,11 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_
if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT))) if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
{ {
if (alphalevel == 13) if (alphalevel == 13)
alphalevel = hudminusalpha[cv_translucenthud.value]; alphalevel = hudminusalpha[st_translucency];
else if (alphalevel == 14) else if (alphalevel == 14)
alphalevel = 10 - cv_translucenthud.value; alphalevel = 10 - st_translucency;
else if (alphalevel == 15) else if (alphalevel == 15)
alphalevel = hudplusalpha[cv_translucenthud.value]; alphalevel = hudplusalpha[st_translucency];
if (alphalevel >= 10) if (alphalevel >= 10)
return; // invis return; // invis
@ -1074,7 +1076,7 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_
// //
void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT8 skincolor) void V_DrawContinueIcon(INT32 x, INT32 y, INT32 flags, INT32 skinnum, UINT8 skincolor)
{ {
if (skinnum >= 0 && skinnum < numskins && skins[skinnum].sprites[SPR2_XTRA].numframes >= 4) if (skinnum >= 0 && skinnum < numskins && skins[skinnum].sprites[SPR2_XTRA].numframes > XTRA_CONTINUE)
{ {
spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA]; spritedef_t *sprdef = &skins[skinnum].sprites[SPR2_XTRA];
spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CONTINUE]; spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CONTINUE];
@ -1393,11 +1395,11 @@ void V_DrawFillConsoleMap(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
if ((alphalevel = ((c & V_ALPHAMASK) >> V_ALPHASHIFT))) if ((alphalevel = ((c & V_ALPHAMASK) >> V_ALPHASHIFT)))
{ {
if (alphalevel == 13) if (alphalevel == 13)
alphalevel = hudminusalpha[cv_translucenthud.value]; alphalevel = hudminusalpha[st_translucency];
else if (alphalevel == 14) else if (alphalevel == 14)
alphalevel = 10 - cv_translucenthud.value; alphalevel = 10 - st_translucency;
else if (alphalevel == 15) else if (alphalevel == 15)
alphalevel = hudplusalpha[cv_translucenthud.value]; alphalevel = hudplusalpha[st_translucency];
if (alphalevel >= 10) if (alphalevel >= 10)
return; // invis return; // invis
@ -1860,7 +1862,9 @@ void V_DrawFadeScreen(UINT16 color, UINT8 strength)
{ {
const UINT8 *fadetable = ((color & 0xFF00) // Color is not palette index? const UINT8 *fadetable = ((color & 0xFF00) // Color is not palette index?
? ((UINT8 *)colormaps + strength*256) // Do COLORMAP fade. ? ((UINT8 *)(((color & 0x0F00) == 0x0A00) ? fadecolormap // Do fadecolormap fade.
: (((color & 0x0F00) == 0x0B00) ? fadecolormap + (256 * FADECOLORMAPROWS) // Do white fadecolormap fade.
: colormaps)) + strength*256) // Do COLORMAP fade.
: ((UINT8 *)transtables + ((9-strength)<<FF_TRANSSHIFT) + color*256)); // Else, do TRANSMAP** fade. : ((UINT8 *)transtables + ((9-strength)<<FF_TRANSSHIFT) + color*256)); // Else, do TRANSMAP** fade.
const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height; const UINT8 *deststop = screens[0] + vid.rowbytes * vid.height;
UINT8 *buf = screens[0]; UINT8 *buf = screens[0];
@ -1898,14 +1902,15 @@ void V_DrawPromptBack(INT32 boxheight, INT32 color)
{ {
UINT8 *deststop, *buf; UINT8 *deststop, *buf;
boxheight = ((boxheight * 4) + (boxheight/2)*5);
if (color >= 256 && color < 512) if (color >= 256 && color < 512)
{ {
boxheight = ((boxheight * 4) + (boxheight/2)*5);
V_DrawFill((BASEVIDWIDTH-(vid.width/vid.dupx))/2, BASEVIDHEIGHT-boxheight, (vid.width/vid.dupx),boxheight, (color-256)|V_SNAPTOBOTTOM); V_DrawFill((BASEVIDWIDTH-(vid.width/vid.dupx))/2, BASEVIDHEIGHT-boxheight, (vid.width/vid.dupx),boxheight, (color-256)|V_SNAPTOBOTTOM);
return; return;
} }
boxheight *= vid.dupy;
if (color == INT32_MAX) if (color == INT32_MAX)
color = cons_backcolor.value; color = cons_backcolor.value;
@ -1947,7 +1952,7 @@ void V_DrawPromptBack(INT32 boxheight, INT32 color)
// heavily simplified -- we don't need to know x or y position, // heavily simplified -- we don't need to know x or y position,
// just the start and stop positions // just the start and stop positions
deststop = screens[0] + vid.rowbytes * vid.height; deststop = screens[0] + vid.rowbytes * vid.height;
buf = deststop - vid.rowbytes * boxheight * vid.dupy; // 4 lines of space plus gaps between and some leeway buf = deststop - vid.rowbytes * ((boxheight * 4) + (boxheight/2)*5); // 4 lines of space plus gaps between and some leeway
for (; buf < deststop; ++buf) for (; buf < deststop; ++buf)
*buf = promptbgmap[*buf]; *buf = promptbgmap[*buf];
} }
@ -2899,7 +2904,7 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string)
continue; continue;
} }
c = toupper(*ch) - LT_FONTSTART; c = *ch - LT_FONTSTART;
if (c < 0 || c >= LT_FONTSIZE || !lt_font[c]) if (c < 0 || c >= LT_FONTSIZE || !lt_font[c])
{ {
cx += 16*dupx; cx += 16*dupx;
@ -2934,7 +2939,7 @@ INT32 V_LevelNameWidth(const char *string)
{ {
if (string[i] & 0x80) if (string[i] & 0x80)
continue; continue;
c = toupper(string[i]) - LT_FONTSTART; c = string[i] - LT_FONTSTART;
if (c < 0 || c >= LT_FONTSIZE || !lt_font[c]) if (c < 0 || c >= LT_FONTSIZE || !lt_font[c])
w += 16; w += 16;
else else
@ -2953,7 +2958,7 @@ INT32 V_LevelNameHeight(const char *string)
for (i = 0; i < strlen(string); i++) for (i = 0; i < strlen(string); i++)
{ {
c = toupper(string[i]) - LT_FONTSTART; c = string[i] - LT_FONTSTART;
if (c < 0 || c >= LT_FONTSIZE || !lt_font[c]) if (c < 0 || c >= LT_FONTSIZE || !lt_font[c])
continue; continue;
@ -2964,7 +2969,7 @@ INT32 V_LevelNameHeight(const char *string)
return w; return w;
} }
// For ST_drawLevelTitle // For ST_drawTitleCard
// Returns the width of the act num patch // Returns the width of the act num patch
INT32 V_LevelActNumWidth(INT32 num) INT32 V_LevelActNumWidth(INT32 num)
{ {

View File

@ -106,6 +106,10 @@ extern RGBA_t *pMasterPalette;
#define V_HUDTRANSHALF 0x000D0000 #define V_HUDTRANSHALF 0x000D0000
#define V_HUDTRANS 0x000E0000 // draw the hud translucent #define V_HUDTRANS 0x000E0000 // draw the hud translucent
#define V_HUDTRANSDOUBLE 0x000F0000 #define V_HUDTRANSDOUBLE 0x000F0000
// Macros follow
#define V_USERHUDTRANSHALF ((10-(cv_translucenthud.value/2))<<V_ALPHASHIFT)
#define V_USERHUDTRANS ((10-cv_translucenthud.value)<<V_ALPHASHIFT)
#define V_USERHUDTRANSDOUBLE ((10-min(cv_translucenthud.value*2, 10))<<V_ALPHASHIFT)
#define V_AUTOFADEOUT 0x00100000 // used by CECHOs, automatic fade out when almost over #define V_AUTOFADEOUT 0x00100000 // used by CECHOs, automatic fade out when almost over
#define V_RETURN8 0x00200000 // 8 pixel return instead of 12 #define V_RETURN8 0x00200000 // 8 pixel return instead of 12

View File

@ -383,7 +383,7 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen
// read the header // read the header
if (fread(&header, 1, sizeof header, handle) < sizeof header) if (fread(&header, 1, sizeof header, handle) < sizeof header)
{ {
CONS_Alert(CONS_ERROR, M_GetText("Can't read wad header because %s\n"), strerror(ferror(handle))); CONS_Alert(CONS_ERROR, M_GetText("Can't read wad header because %s\n"), M_FileError(handle));
return NULL; return NULL;
} }
@ -406,7 +406,7 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen
if (fseek(handle, header.infotableofs, SEEK_SET) == -1 if (fseek(handle, header.infotableofs, SEEK_SET) == -1
|| fread(fileinfo, 1, i, handle) < i) || fread(fileinfo, 1, i, handle) < i)
{ {
CONS_Alert(CONS_ERROR, M_GetText("Corrupt wadfile directory (%s)\n"), strerror(ferror(handle))); CONS_Alert(CONS_ERROR, M_GetText("Corrupt wadfile directory (%s)\n"), M_FileError(handle));
free(fileinfov); free(fileinfov);
return NULL; return NULL;
} }
@ -427,7 +427,7 @@ static lumpinfo_t* ResGetLumpsWad (FILE* handle, UINT16* nlmp, const char* filen
handle) < sizeof realsize) handle) < sizeof realsize)
{ {
I_Error("corrupt compressed file: %s; maybe %s", /// \todo Avoid the bailout? I_Error("corrupt compressed file: %s; maybe %s", /// \todo Avoid the bailout?
filename, strerror(ferror(handle))); filename, M_FileError(handle));
} }
realsize = LONG(realsize); realsize = LONG(realsize);
if (realsize != 0) if (realsize != 0)
@ -565,7 +565,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp)
fseek(handle, -4, SEEK_CUR); fseek(handle, -4, SEEK_CUR);
if (fread(&zend, 1, sizeof zend, handle) < sizeof zend) if (fread(&zend, 1, sizeof zend, handle) < sizeof zend)
{ {
CONS_Alert(CONS_ERROR, "Corrupt central directory (%s)\n", strerror(ferror(handle))); CONS_Alert(CONS_ERROR, "Corrupt central directory (%s)\n", M_FileError(handle));
return NULL; return NULL;
} }
numlumps = zend.entries; numlumps = zend.entries;
@ -582,7 +582,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp)
if (fread(zentry, 1, sizeof(zentry_t), handle) < sizeof(zentry_t)) if (fread(zentry, 1, sizeof(zentry_t), handle) < sizeof(zentry_t))
{ {
CONS_Alert(CONS_ERROR, "Failed to read central directory (%s)\n", strerror(ferror(handle))); CONS_Alert(CONS_ERROR, "Failed to read central directory (%s)\n", M_FileError(handle));
Z_Free(lumpinfo); Z_Free(lumpinfo);
free(zentries); free(zentries);
return NULL; return NULL;
@ -602,7 +602,7 @@ static lumpinfo_t* ResGetLumpsZip (FILE* handle, UINT16* nlmp)
fullname = malloc(zentry->namelen + 1); fullname = malloc(zentry->namelen + 1);
if (fgets(fullname, zentry->namelen + 1, handle) != fullname) if (fgets(fullname, zentry->namelen + 1, handle) != fullname)
{ {
CONS_Alert(CONS_ERROR, "Unable to read lumpname (%s)\n", strerror(ferror(handle))); CONS_Alert(CONS_ERROR, "Unable to read lumpname (%s)\n", M_FileError(handle));
Z_Free(lumpinfo); Z_Free(lumpinfo);
free(zentries); free(zentries);
free(fullname); free(fullname);

View File

@ -371,6 +371,9 @@ void I_FinishUpdate(void)
if (cv_ticrate.value) if (cv_ticrate.value)
SCR_DisplayTicRate(); SCR_DisplayTicRate();
if (cv_showping.value && netgame && consoleplayer != serverplayer)
SCR_DisplayLocalPing();
// //
if (bDIBMode) if (bDIBMode)
{ {

View File

@ -166,13 +166,11 @@ static INT32 endtic = -1;
intertype_t intertype = int_none; intertype_t intertype = int_none;
static void Y_RescaleScreenBuffer(void); static void Y_RescaleScreenBuffer(void);
static void Y_CleanupScreenBuffer(void);
static void Y_AwardCoopBonuses(void); static void Y_AwardCoopBonuses(void);
static void Y_AwardSpecialStageBonus(void); static void Y_AwardSpecialStageBonus(void);
static void Y_CalculateCompetitionWinners(void); static void Y_CalculateCompetitionWinners(void);
static void Y_CalculateTimeRaceWinners(void); static void Y_CalculateTimeRaceWinners(void);
static void Y_CalculateMatchWinners(void); static void Y_CalculateMatchWinners(void);
static void Y_FollowIntermission(void);
static void Y_UnloadData(void); static void Y_UnloadData(void);
// Stuff copy+pasted from st_stuff.c // Stuff copy+pasted from st_stuff.c
@ -293,7 +291,7 @@ static void Y_RescaleScreenBuffer(void)
// //
// Free all related memory. // Free all related memory.
// //
static void Y_CleanupScreenBuffer(void) void Y_CleanupScreenBuffer(void)
{ {
// Who knows? // Who knows?
if (y_buffer == NULL) if (y_buffer == NULL)
@ -399,10 +397,13 @@ void Y_IntermissionDrawer(void)
// draw the "got through act" lines and act number // draw the "got through act" lines and act number
V_DrawLevelTitle(data.coop.passedx1, 49, 0, data.coop.passed1); V_DrawLevelTitle(data.coop.passedx1, 49, 0, data.coop.passed1);
V_DrawLevelTitle(data.coop.passedx2, 49+V_LevelNameHeight(data.coop.passed2)+2, 0, data.coop.passed2); {
INT32 h = V_LevelNameHeight(data.coop.passed2);
V_DrawLevelTitle(data.coop.passedx2, 49+h+2, 0, data.coop.passed2);
if (data.coop.actnum) if (data.coop.actnum)
V_DrawLevelActNum(244, 57, 0, data.coop.actnum); V_DrawLevelActNum(244, 42+h, 0, data.coop.actnum);
}
bonusy = 150; bonusy = 150;
// Total // Total
@ -485,10 +486,10 @@ void Y_IntermissionDrawer(void)
if (drawsection == 1) if (drawsection == 1)
{ {
const char *ringtext = "\x82" "50 RINGS, NO SHIELD"; const char *ringtext = "\x82" "50 rings, no shield";
const char *tut1text = "\x82" "PRESS " "\x80" "SPIN"; const char *tut1text = "\x82" "press " "\x80" "spin";
const char *tut2text = "\x82" "MID-" "\x80" "JUMP"; const char *tut2text = "\x82" "mid-" "\x80" "jump";
ttheight = 16; ttheight = 8;
V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1); V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1);
ttheight += V_LevelNameHeight(data.spec.passed3) + 2; ttheight += V_LevelNameHeight(data.spec.passed3) + 2;
V_DrawLevelTitle(data.spec.passedx3 + xoffset2, ttheight, 0, data.spec.passed3); V_DrawLevelTitle(data.spec.passedx3 + xoffset2, ttheight, 0, data.spec.passed3);
@ -497,9 +498,9 @@ void Y_IntermissionDrawer(void)
ttheight = 108; ttheight = 108;
V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset4 - (V_LevelNameWidth(ringtext)/2), ttheight, 0, ringtext); V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset4 - (V_LevelNameWidth(ringtext)/2), ttheight, 0, ringtext);
ttheight += V_LevelNameHeight(ringtext) + 2;
V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset5 - (V_LevelNameWidth(tut1text)/2), ttheight, 0, tut1text);
ttheight += V_LevelNameHeight(tut1text) + 2; ttheight += V_LevelNameHeight(tut1text) + 2;
V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset5 - (V_LevelNameWidth(tut1text)/2), ttheight, 0, tut1text);
ttheight += V_LevelNameHeight(tut2text) + 2;
V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset6 - (V_LevelNameWidth(tut2text)/2), ttheight, 0, tut2text); V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset6 - (V_LevelNameWidth(tut2text)/2), ttheight, 0, tut2text);
} }
else else
@ -816,7 +817,7 @@ void Y_IntermissionDrawer(void)
} }
} }
} }
else if (intertype == int_classicrace) else if (intertype == int_comp)
{ {
INT32 x = 4; INT32 x = 4;
INT32 y = 48; INT32 y = 48;
@ -950,7 +951,7 @@ void Y_Ticker(void)
if (!--timer) if (!--timer)
{ {
Y_EndIntermission(); Y_EndIntermission();
Y_FollowIntermission(); G_AfterIntermission();
return; return;
} }
} }
@ -958,7 +959,7 @@ void Y_Ticker(void)
else if (intertic == endtic) else if (intertic == endtic)
{ {
Y_EndIntermission(); Y_EndIntermission();
Y_FollowIntermission(); G_AfterIntermission();
return; return;
} }
@ -1142,7 +1143,7 @@ void Y_Ticker(void)
if (data.match.numplayers != D_NumPlayers()) if (data.match.numplayers != D_NumPlayers())
Y_CalculateMatchWinners(); Y_CalculateMatchWinners();
} }
else if (intertype == int_race || intertype == int_classicrace) // race else if (intertype == int_race || intertype == int_comp) // race
{ {
if (!intertic) // first time only if (!intertic) // first time only
S_ChangeMusicInternal("_inter", true); // loop it S_ChangeMusicInternal("_inter", true); // loop it
@ -1151,96 +1152,6 @@ void Y_Ticker(void)
} }
} }
//
// Y_UpdateRecordReplays
//
// Update replay files/data, etc. for Record Attack
// See G_SetNightsRecords for NiGHTS Attack.
//
static void Y_UpdateRecordReplays(void)
{
const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1;
char *gpath;
char lastdemo[256], bestdemo[256];
UINT8 earnedEmblems;
// Record new best time
if (!mainrecords[gamemap-1])
G_AllocMainRecordData(gamemap-1);
if (players[consoleplayer].score > mainrecords[gamemap-1]->score)
mainrecords[gamemap-1]->score = players[consoleplayer].score;
if ((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time))
mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
if ((UINT16)(players[consoleplayer].rings) > mainrecords[gamemap-1]->rings)
mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
if (data.coop.gotperfbonus)
mainrecords[gamemap-1]->gotperfect = true;
// Save demo!
bestdemo[255] = '\0';
lastdemo[255] = '\0';
G_SetDemoTime(players[consoleplayer].realtime, players[consoleplayer].score, (UINT16)(players[consoleplayer].rings));
G_CheckDemoStatus();
I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
I_mkdir(va("%s"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder), 0755);
if ((gpath = malloc(glen)) == NULL)
I_Error("Out of memory for replay filepath\n");
sprintf(gpath,"%s"PATHSEP"replay"PATHSEP"%s"PATHSEP"%s", srb2home, timeattackfolder, G_BuildMapName(gamemap));
snprintf(lastdemo, 255, "%s-%s-last.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (FIL_FileExists(lastdemo))
{
UINT8 *buf;
size_t len = FIL_ReadFile(lastdemo, &buf);
snprintf(bestdemo, 255, "%s-%s-time-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || G_CmpDemoTime(bestdemo, lastdemo) & 1)
{ // Better time, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW RECORD TIME!"), M_GetText("Saved replay as"), bestdemo);
}
snprintf(bestdemo, 255, "%s-%s-score-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<1)))
{ // Better score, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW HIGH SCORE!"), M_GetText("Saved replay as"), bestdemo);
}
snprintf(bestdemo, 255, "%s-%s-rings-best.lmp", gpath, skins[cv_chooseskin.value-1].name);
if (!FIL_FileExists(bestdemo) || (G_CmpDemoTime(bestdemo, lastdemo) & (1<<2)))
{ // Better rings, save this demo.
if (FIL_FileExists(bestdemo))
remove(bestdemo);
FIL_WriteFile(bestdemo, buf, len);
CONS_Printf("\x83%s\x80 %s '%s'\n", M_GetText("NEW MOST RINGS!"), M_GetText("Saved replay as"), bestdemo);
}
//CONS_Printf("%s '%s'\n", M_GetText("Saved replay as"), lastdemo);
Z_Free(buf);
}
free(gpath);
// Check emblems when level data is updated
if ((earnedEmblems = M_CheckLevelEmblems()))
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
// Update timeattack menu's replay availability.
Nextmap_OnChange();
}
// //
// Y_StartIntermission // Y_StartIntermission
// //
@ -1249,7 +1160,6 @@ static void Y_UpdateRecordReplays(void)
void Y_StartIntermission(void) void Y_StartIntermission(void)
{ {
INT32 i; INT32 i;
UINT8 completionEmblems = M_CompletionEmblems();
intertic = -1; intertic = -1;
@ -1262,10 +1172,7 @@ void Y_StartIntermission(void)
{ {
timer = 0; timer = 0;
if (G_IsSpecialStage(gamemap)) intertype = (G_IsSpecialStage(gamemap)) ? int_spec : int_coop;
intertype = (maptol & TOL_NIGHTS) ? int_nightsspec : int_spec;
else
intertype = (maptol & TOL_NIGHTS) ? int_nights : int_coop;
} }
else else
{ {
@ -1280,14 +1187,7 @@ void Y_StartIntermission(void)
} }
if (gametype == GT_COOP) if (gametype == GT_COOP)
{ intertype = (G_IsSpecialStage(gamemap)) ? int_spec : int_coop;
// Nights intermission is single player only
// Don't add it here
if (G_IsSpecialStage(gamemap))
intertype = int_spec;
else
intertype = int_coop;
}
else if (gametype == GT_TEAMMATCH) else if (gametype == GT_TEAMMATCH)
intertype = int_teammatch; intertype = int_teammatch;
else if (gametype == GT_MATCH else if (gametype == GT_MATCH
@ -1297,7 +1197,7 @@ void Y_StartIntermission(void)
else if (gametype == GT_RACE) else if (gametype == GT_RACE)
intertype = int_race; intertype = int_race;
else if (gametype == GT_COMPETITION) else if (gametype == GT_COMPETITION)
intertype = int_classicrace; intertype = int_comp;
else if (gametype == GT_CTF) else if (gametype == GT_CTF)
intertype = int_ctf; intertype = int_ctf;
} }
@ -1312,20 +1212,6 @@ void Y_StartIntermission(void)
switch (intertype) switch (intertype)
{ {
case int_nights:
// Can't fail
G_SetNightsRecords();
// Check records
{
UINT8 earnedEmblems = M_CheckLevelEmblems();
if (earnedEmblems)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for NiGHTS records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
}
// fall back into the coop intermission for now
intertype = int_coop;
/* FALLTHRU */
case int_coop: // coop or single player, normal level case int_coop: // coop or single player, normal level
{ {
// award time and ring bonuses // award time and ring bonuses
@ -1334,24 +1220,6 @@ void Y_StartIntermission(void)
// setup time data // setup time data
data.coop.tics = players[consoleplayer].realtime; data.coop.tics = players[consoleplayer].realtime;
if ((!modifiedgame || savemoddata) && !multiplayer && !demoplayback)
{
// Update visitation flags
mapvisited[gamemap-1] |= MV_BEATEN;
if (ALL7EMERALDS(emeralds))
mapvisited[gamemap-1] |= MV_ALLEMERALDS;
if (ultimatemode)
mapvisited[gamemap-1] |= MV_ULTIMATE;
if (data.coop.gotperfbonus)
mapvisited[gamemap-1] |= MV_PERFECT;
if (modeattacking == ATTACKING_RECORD)
Y_UpdateRecordReplays();
if (completionEmblems)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)completionEmblems, completionEmblems > 1 ? "s" : "");
}
for (i = 0; i < 4; ++i) for (i = 0; i < 4; ++i)
data.coop.bonuspatches[i] = W_CachePatchName(data.coop.bonuses[i].patch, PU_STATIC); data.coop.bonuspatches[i] = W_CachePatchName(data.coop.bonuses[i].patch, PU_STATIC);
data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_STATIC); data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_STATIC);
@ -1384,21 +1252,21 @@ void Y_StartIntermission(void)
// too long so just show "YOU GOT THROUGH THE ACT" // too long so just show "YOU GOT THROUGH THE ACT"
if (strlen(skins[players[consoleplayer].skin].realname) > 13) if (strlen(skins[players[consoleplayer].skin].realname) > 13)
{ {
strcpy(data.coop.passed1, "YOU GOT"); strcpy(data.coop.passed1, "you got");
strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "THROUGH ACT" : "THROUGH THE ACT"); strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "through act" : "through the act");
} }
// long enough that "X GOT" won't fit so use "X PASSED THE ACT" // long enough that "X GOT" won't fit so use "X PASSED THE ACT"
else if (strlen(skins[players[consoleplayer].skin].realname) > 8) else if (strlen(skins[players[consoleplayer].skin].realname) > 8)
{ {
strcpy(data.coop.passed1, skins[players[consoleplayer].skin].realname); strcpy(data.coop.passed1, skins[players[consoleplayer].skin].realname);
strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "PASSED ACT" : "PASSED THE ACT"); strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "passed act" : "passed the act");
} }
// length is okay for normal use // length is okay for normal use
else else
{ {
snprintf(data.coop.passed1, sizeof data.coop.passed1, "%s GOT", snprintf(data.coop.passed1, sizeof data.coop.passed1, "%s got",
skins[players[consoleplayer].skin].realname); skins[players[consoleplayer].skin].realname);
strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "THROUGH ACT" : "THROUGH THE ACT"); strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "through act" : "through the act");
} }
// set X positions // set X positions
@ -1418,40 +1286,8 @@ void Y_StartIntermission(void)
break; break;
} }
case int_nightsspec:
if (modeattacking && stagefailed)
{
// Nuh-uh. Get out of here.
Y_EndIntermission();
Y_FollowIntermission();
break;
}
if (!stagefailed)
G_SetNightsRecords();
// Check records
{
UINT8 earnedEmblems = M_CheckLevelEmblems();
if (earnedEmblems)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for NiGHTS records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
}
// fall back into the special stage intermission for now
intertype = int_spec;
/* FALLTHRU */
case int_spec: // coop or single player, special stage case int_spec: // coop or single player, special stage
{ {
// Update visitation flags?
if ((!modifiedgame || savemoddata) && !multiplayer && !demoplayback)
{
if (!stagefailed)
mapvisited[gamemap-1] |= MV_BEATEN;
// all emeralds/ultimate/perfect emblems won't be possible in ss, oh well?
if (completionEmblems)
CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for level completion.\n"), (UINT16)completionEmblems, completionEmblems > 1 ? "s" : "");
}
// give out ring bonuses // give out ring bonuses
Y_AwardSpecialStageBonus(); Y_AwardSpecialStageBonus();
@ -1498,7 +1334,7 @@ void Y_StartIntermission(void)
// set up the "got through act" message according to skin name // set up the "got through act" message according to skin name
if (stagefailed) if (stagefailed)
{ {
strcpy(data.spec.passed2, "SPECIAL STAGE"); strcpy(data.spec.passed2, "Special Stage");
data.spec.passed1[0] = '\0'; data.spec.passed1[0] = '\0';
} }
else if (ALL7EMERALDS(emeralds)) else if (ALL7EMERALDS(emeralds))
@ -1507,13 +1343,13 @@ void Y_StartIntermission(void)
sizeof data.spec.passed1, "%s", sizeof data.spec.passed1, "%s",
skins[players[consoleplayer].skin].realname); skins[players[consoleplayer].skin].realname);
data.spec.passed1[sizeof data.spec.passed1 - 1] = '\0'; data.spec.passed1[sizeof data.spec.passed1 - 1] = '\0';
strcpy(data.spec.passed2, "GOT THEM ALL!"); strcpy(data.spec.passed2, "got them all!");
if (players[consoleplayer].charflags & SF_SUPER) if (players[consoleplayer].charflags & SF_SUPER)
{ {
strcpy(data.spec.passed3, "CAN NOW BECOME"); strcpy(data.spec.passed3, "can now become");
snprintf(data.spec.passed4, snprintf(data.spec.passed4,
sizeof data.spec.passed4, "SUPER %s", sizeof data.spec.passed4, "Super %s",
skins[players[consoleplayer].skin].realname); skins[players[consoleplayer].skin].realname);
data.spec.passed4[sizeof data.spec.passed4 - 1] = '\0'; data.spec.passed4[sizeof data.spec.passed4 - 1] = '\0';
} }
@ -1523,13 +1359,13 @@ void Y_StartIntermission(void)
if (strlen(skins[players[consoleplayer].skin].realname) <= SKINNAMESIZE-5) if (strlen(skins[players[consoleplayer].skin].realname) <= SKINNAMESIZE-5)
{ {
snprintf(data.spec.passed1, snprintf(data.spec.passed1,
sizeof data.spec.passed1, "%s GOT", sizeof data.spec.passed1, "%s got",
skins[players[consoleplayer].skin].realname); skins[players[consoleplayer].skin].realname);
data.spec.passed1[sizeof data.spec.passed1 - 1] = '\0'; data.spec.passed1[sizeof data.spec.passed1 - 1] = '\0';
} }
else else
strcpy(data.spec.passed1, "YOU GOT"); strcpy(data.spec.passed1, "You got");
strcpy(data.spec.passed2, "A CHAOS EMERALD"); strcpy(data.spec.passed2, "a Chaos Emerald");
if (P_GetNextEmerald() > 6) if (P_GetNextEmerald() > 6)
{ {
data.spec.passed2[15] = '?'; data.spec.passed2[15] = '?';
@ -1637,7 +1473,7 @@ void Y_StartIntermission(void)
break; break;
} }
case int_classicrace: // classic (full race) case int_comp: // classic (full race)
{ {
// find out who won // find out who won
Y_CalculateCompetitionWinners(); Y_CalculateCompetitionWinners();
@ -2192,23 +2028,6 @@ void Y_EndIntermission(void)
usebuffer = false; usebuffer = false;
} }
//
// Y_FollowIntermission
//
static void Y_FollowIntermission(void)
{
if (modeattacking)
{
M_EndModeAttackRun();
return;
}
// This handles whether to play a post-level cutscene, end the game,
// or simply go to the next level.
// No need to duplicate the code here!
G_AfterIntermission();
}
#define UNLOAD(x) Z_ChangeTag(x, PU_CACHE); x = NULL #define UNLOAD(x) Z_ChangeTag(x, PU_CACHE); x = NULL
// //
@ -2221,8 +2040,6 @@ static void Y_UnloadData(void)
if (rendermode != render_soft) if (rendermode != render_soft)
return; return;
Y_CleanupScreenBuffer();
// unload the background patches // unload the background patches
UNLOAD(bgpatch); UNLOAD(bgpatch);
UNLOAD(widebgpatch); UNLOAD(widebgpatch);
@ -2258,7 +2075,7 @@ static void Y_UnloadData(void)
break; break;
default: default:
//without this default, //without this default,
//int_none, int_tag, int_chaos, and int_classicrace //int_none, int_tag, int_chaos, and int_comp
//are not handled //are not handled
break; break;
} }

View File

@ -16,6 +16,7 @@ void Y_Ticker(void);
void Y_StartIntermission(void); void Y_StartIntermission(void);
void Y_EndIntermission(void); void Y_EndIntermission(void);
void Y_ConsiderScreenBuffer(void); void Y_ConsiderScreenBuffer(void);
void Y_CleanupScreenBuffer(void);
typedef enum typedef enum
{ {
@ -26,9 +27,7 @@ typedef enum
// int_tag, // Tag // int_tag, // Tag
int_ctf, // CTF int_ctf, // CTF
int_spec, // Special Stage int_spec, // Special Stage
int_nights, // NiGHTS into Dreams
int_nightsspec,// NiGHTS special stage
int_race, // Race int_race, // Race
int_classicrace, // Competition int_comp, // Competition
} intertype_t; } intertype_t;
extern intertype_t intertype; extern intertype_t intertype;