diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg index f316cd032..7a8f31d5a 100644 --- a/extras/conf/SRB2-22.cfg +++ b/extras/conf/SRB2-22.cfg @@ -3,6 +3,7 @@ For Sonic Robo Blast 2 Version 2.2 Contributors (alphabetical): * Foxboy + * FuriousFox * JJames19119 * Kalaron * Kristos @@ -44,27 +45,32 @@ formatinterface = "SRB2MapSetIO"; //Sky textures for vanilla maps 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"; - SKY4 = "MAP04,MAP06,MAP51,MAPF8,MAPM1"; - SKY6 = "MAP05"; - SKY7 = "MAP07,MAP08,MAP09,MAP52,MAPM2,MAPM5"; - SKY10 = "MAP12,MAP53,MAPM3"; - SKY11 = "MAP10,MAP11,MAP16,MAP55,MAPF2,MAPF5,MAPF6,MAPF9,MAPM7"; - SKY13 = "MAP13,MAP54,MAPAS"; - SKY21 = "MAPAF,MAPF7,MAPM4"; - SKY22 = "MAP22,MAP23,MAP24,MAP25,MAP56,MAPAN,MAPAO,MAPF4,MAPM6"; - SKY29 = "MAP58,MAPAV"; + SKY1 = "MAP01,MAP02,MAP03,MAP33,MAP50,MAP60,MAPF0,MAPM0"; + SKY2 = "MAPM7,MAPMB"; + SKY4 = "MAP04,MAP06,MAP61,MAPF6,MAPM1"; + SKY6 = "MAP05,MAP51,MAPMA"; + SKY7 = "MAPM2,MAPM5"; + SKY8 = "MAP07,MAP08,MAP09,MAP52,MAP62,MAPF1"; + SKY10 = "MAP10,MAP12,MAP53,MAP63,MAPM3"; + SKY11 = "MAP11,MAPF7"; + SKY13 = "MAP13,MAP64"; + 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"; - SKY35 = "MAP41"; - SKY40 = "MAP40"; - SKY55 = "MAPF3,MAPM9"; - SKY66 = "MAPAT"; - SKY99 = "MAP57"; - SKY103 = "MAPA3,MAPA4,MAPAU"; - SKY107 = "MAPA7,MAPA8"; - SKY117 = "MAPAH,MAPAI"; - SKY127 = "MAPAR"; - SKY132 = "MAPAW"; + SKY31 = "MAP31"; + SKY35 = "MAP42"; + SKY40 = "MAP41,MAP71,MAPM9"; + SKY55 = "MAPF3,MAPM8"; + SKY68 = "MAPF8"; + SKY99 = "MAP57,MAPZ0"; + SKY159 = "MAP16"; + SKY172 = "MAP40"; + SKY300 = "MAP72"; + SKY301 = "MAP73"; } // Default lump name for new map @@ -90,9 +96,9 @@ skins Sonic; Tails; Knuckles; - Metalsonic; - Fang; Amy; + Fang; + Metalsonic; } // Gametypes @@ -3422,7 +3428,7 @@ thingtypes 121 { title = "Minus"; - sprite = "MNUSA1"; + sprite = "MNUSA0"; width = 24; height = 32; } @@ -3457,6 +3463,13 @@ thingtypes height = 34; flags8text = "[8] Start on fire"; } + 137 + { + title = "Dragonbomber"; + sprite = "DRABA1"; + width = 28; + height = 48; + } 105 { title = "Jetty-Syn Bomber"; @@ -5726,6 +5739,24 @@ thingtypes width = 24; 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 diff --git a/src/b_bot.c b/src/b_bot.c index 651aeb03d..895c8d18d 100644 --- a/src/b_bot.c +++ b/src/b_bot.c @@ -24,12 +24,47 @@ static boolean lastForward = false; static boolean lastBlocked = 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) { boolean forward=false, backward=false, left=false, right=false, jump=false, spin=false; - angle_t angle; - INT16 rangle; - fixed_t dist; + + player_t *player = sonic->player, *bot = tails->player; + 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! if (!sonic || sonic->health <= 0) @@ -58,46 +93,263 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm return; } - // Gather data about the environment - dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y); - if (tails->player->pflags & PF_STARTDASH) - angle = sonic->angle; + // Adapted from CobaltBW's tails_AI.wad + + // Check water + 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 - angle = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y); - - // Decide which direction to turn - 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); + { + anxiety = max(anxiety - 1, 0); + panic = false; } - // Decide to move forward if you're finished turning - if (abs(rangle) < 10) { // We're facing the right way? - left = right = false; // Stop turning - forward = true; // and walk forward instead. + // Orientation + if ((bot->pflags & (PF_SPINNING|PF_STARTDASH)) || flymode == 2) + { + 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... - if (forward && lastForward && blocked && lastBlocked) // We've been stopped by a wall or something - jump = true; // Try to jump up - } else if ((tails->player->pflags & (PF_JUMPDOWN|PF_JUMPED)) == (PF_JUMPDOWN|PF_JUMPED)) { // When we're already jumping... - if (lastForward && blocked) // We're still stuck on something? + // ******** + // FLY MODE + // spinmode check + if (spinmode || player->exiting) + thinkfly = false; + 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; - 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; } - // Decide when to spin - if (sonic->player->pflags & PF_STARTDASH - && (tails->player->pflags & PF_STARTDASH || (P_AproxDistance(tails->momx, tails->momy) < 2*FRACUNIT && !forward))) - spin = true; + // ******** + // HISTORY + jump_last = jump; + 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. 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) { // 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; // Turn the virtual keypresses into ticcmd_t. 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<>16; } } else { + angle_t angle; if (forward) cmd->forwardmove += MAXPLMOVE<>16; if (backward) @@ -191,6 +444,15 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward cmd->sidemove -= MAXPLMOVE<>16; if (straferight) cmd->sidemove += MAXPLMOVE<>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) 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 (sonic->player->pflags & (PF_GLIDING|PF_SLIDING|PF_BOUNCING) || (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; // Low ceiling, do not want! @@ -252,6 +514,8 @@ void B_RespawnBot(INT32 playernum) if (!sonic || sonic->health <= 0) return; + B_ResetAI(); + player->bot = 1; P_SpawnPlayer(playernum); tails = player->mo; diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 92196219d..635bd7ee9 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -90,6 +90,7 @@ SINT8 nodetoplayer[MAXNETNODES]; SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen) UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen 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 supposedtics[MAXNETNODES]; // nettics prevision for smaller packet 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->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->y = LONG(players[i].mo->y); 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. P_UnsetThingPosition(players[i].mo); 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->flags = LONG(rsp->flags); 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); - if (strcmp(mapheaderinfo[gamemap-1]->lvlttl, "")) - strncpy(netbuffer->u.serverinfo.maptitle, (char *)mapheaderinfo[gamemap-1]->lvlttl, 33); + if (*mapheaderinfo[gamemap-1]->lvlttl) + { + 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 - strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 33); + strncpy(netbuffer->u.serverinfo.maptitle, "UNKNOWN", 32); 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].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 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); if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) - CONS_Printf(M_GetText(" ZONE")); + CONS_Printf(M_GetText(" Zone")); if (actnum > 0) CONS_Printf(" %2d", actnum); } @@ -4208,10 +4234,12 @@ static void HandlePacketFromPlayer(SINT8 node) //Update client ping table from the server. if (client) { - INT32 i; + UINT8 i; for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i]) playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i]; + + servermaxping = (tic_t)netbuffer->u.pingtable[MAXPLAYERS]; } break; @@ -4591,7 +4619,7 @@ static void Local_Maketic(INT32 realtics) { I_OsPolling(); // I_Getevent 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 if (!dedicated) rendergametic = gametic; // 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) { INT32 i; @@ -4753,6 +4789,8 @@ static inline void PingUpdate(void) laggers[i] = true; numlaggers++; } + else + pingtimeout[i] = 0; } //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]) { - 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; - buf[1] = KICK_MSG_PING_HIGH; - SendNetXCmd(XD_KICK, &buf, 2); + pingtimeout[i] = 0; + + 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. } + // 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 for (i = 0; i < MAXNETNODES; i++) if (nodeingame[i]) - HSendPacket(i, true, 0, sizeof(INT32) * MAXPLAYERS); + HSendPacket(i, true, 0, sizeof(INT32) * (MAXPLAYERS+1)); pingmeasurecount = 1; //Reset count } @@ -4816,7 +4872,7 @@ void NetUpdate(void) if (server) { - if (netgame && !(gametime % 255)) + if (netgame && !(gametime % 35)) // update once per second. PingUpdate(); // update node latency values so we can take an average later. for (i = 0; i < MAXPLAYERS; i++) diff --git a/src/d_clisrv.h b/src/d_clisrv.h index d7c210895..7313c7355 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -255,6 +255,7 @@ typedef struct INT32 health; angle_t angle; + angle_t rollangle; fixed_t x; fixed_t y; fixed_t z; @@ -423,9 +424,9 @@ typedef struct serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...) askinfo_pak askinfo; // 61 bytes msaskinfo_pak msaskinfo; // 22 bytes - plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes (I'd say 36~38) - plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes (welp they ARE) - UINT32 pingtable[MAXPLAYERS]; // 128 bytes + plrinfo playerinfo[MAXPLAYERS]; // 576 bytes(?) + plrconfig playerconfig[MAXPLAYERS]; // (up to) 528 bytes(?) + UINT32 pingtable[MAXPLAYERS+1]; // 68 bytes } u; // This is needed to pack diff packet types data together } ATTRPACK doomdata_t; @@ -487,6 +488,7 @@ extern tic_t jointimeout; extern UINT16 pingmeasurecount; extern UINT32 realpingtable[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; diff --git a/src/d_main.c b/src/d_main.c index ebc618be8..5853fccf0 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -274,7 +274,7 @@ static void D_Display(void) && wipetypepre != UINT8_MAX) { F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeColorFill(31); F_WipeEndScreen(); F_RunWipe(wipetypepre, gamestate != GS_TIMEATTACK && gamestate != GS_TITLESCREEN); } @@ -291,8 +291,11 @@ static void D_Display(void) switch (gamestate) { case GS_TITLESCREEN: - F_TitleScreenDrawer(); - break; + if (!titlemapinaction || !curbghide) { + F_TitleScreenDrawer(); + break; + } + /* FALLTHRU */ case GS_LEVEL: if (!gametic) break; @@ -363,11 +366,56 @@ static void D_Display(void) // clean up border stuff // 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 - 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) { @@ -380,9 +428,14 @@ static void D_Display(void) lastdraw = false; } - ST_Drawer(); - F_TextPromptDrawer(); - HU_Drawer(); + if (gamestate == GS_LEVEL) + { + ST_Drawer(); + F_TextPromptDrawer(); + HU_Drawer(); + } + else + F_TitleScreenDrawer(); } } @@ -435,6 +488,15 @@ static void D_Display(void) if (rendermode != render_none) { 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); } @@ -444,7 +506,7 @@ static void D_Display(void) framecount = 0; demostarttime = I_GetTime(); } - + wipetypepost = -1; } 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 // ========================================================================= @@ -655,6 +667,7 @@ void D_SRB2Loop(void) // consoleplayer -> displayplayer (hear sounds from viewpoint) S_UpdateSounds(); // move positional sounds + S_UpdateClosedCaptions(); // check for media change, loop music.. I_UpdateCD(); @@ -720,6 +733,8 @@ void D_StartTitle(void) for (i = 0; i < MAXPLAYERS; i++) CL_ClearPlayer(i); + players[consoleplayer].availabilities = players[1].availabilities = R_GetSkinAvailabilities(); // players[1] is supposed to be for 2p + splitscreen = false; SplitScreen_OnChange(); botingame = false; diff --git a/src/d_main.h b/src/d_main.h index 65c51802a..d67a5bb49 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -54,7 +54,4 @@ const char *D_Home(void); void D_AdvanceDemo(void); void D_StartTitle(void); -/* Here for title maps */ -void D_Render(void); - #endif //__D_MAIN__ diff --git a/src/d_netcmd.c b/src/d_netcmd.c index d750c81f2..8fa4b2e0b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -87,6 +87,7 @@ static void JoinTimeout_OnChange(void); static void CoopStarposts_OnChange(void); static void CoopLives_OnChange(void); +static void ExitMove_OnChange(void); static void Ringslinger_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}}; 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}; + +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 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}; 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}}; 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}}; 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}}; 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_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_advancemap); CV_RegisterVar(&cv_playersforexit); + CV_RegisterVar(&cv_exitmove); CV_RegisterVar(&cv_timelimit); CV_RegisterVar(&cv_playbackspeed); CV_RegisterVar(&cv_forceskin); @@ -577,6 +590,8 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_skipmapcheck); CV_RegisterVar(&cv_sleep); CV_RegisterVar(&cv_maxping); + CV_RegisterVar(&cv_pingtimeout); + CV_RegisterVar(&cv_showping); #ifdef SEENAMES 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)) { - boolean notsame; + //boolean notsame; cv_skin.value = foundskin; - notsame = (cv_skin.value != players[consoleplayer].skin); + //notsame = (cv_skin.value != players[consoleplayer].skin); SetPlayerSkin(consoleplayer, cv_skin.string); CV_StealthSet(&cv_skin, skins[cv_skin.value].name); - if (notsame) + /*if (notsame) { CV_StealthSetValue(&cv_playercolor, skins[cv_skin.value].prefcolor); @@ -1230,7 +1245,7 @@ static void SendNameAndColor(void) if (players[consoleplayer].mo) players[consoleplayer].mo->color = (UINT8)players[consoleplayer].skincolor; - } + }*/ } else { @@ -1341,15 +1356,16 @@ static void SendNameAndColor2(void) } else if ((foundskin = R_SkinAvailable(cv_skin2.string)) != -1 && R_SkinUsable(secondplaya, foundskin)) { - boolean notsame; + //boolean notsame; cv_skin2.value = foundskin; - notsame = (cv_skin2.value != players[secondplaya].skin); + //notsame = (cv_skin2.value != players[secondplaya].skin); 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); @@ -1357,7 +1373,7 @@ static void SendNameAndColor2(void) if (players[secondplaya].mo) players[secondplaya].mo->color = players[secondplaya].skincolor; - } + }*/ } else { @@ -1924,6 +1940,17 @@ static void Command_Map_f(void) d = atoi(gametypename); if (d >= 0 && d < NUMGAMETYPES) 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 { @@ -2039,7 +2066,9 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) lastgametype = gametype; 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 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; /** 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++) CL_ClearPlayer(i); + players[consoleplayer].availabilities = players[1].availabilities = R_GetSkinAvailabilities(); // players[1] is supposed to be for 2p + splitscreen = false; SplitScreen_OnChange(); botingame = false; @@ -4310,9 +4352,9 @@ static void Command_Isgamemodified_f(void) if (savemoddata) CONS_Printf(M_GetText("modifiedgame is true, but you can save emblem and time data in this mod.\n")); 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 - 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) diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 4821aff46..c2e6f980d 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -94,7 +94,7 @@ extern consvar_t cv_recycler; 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_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_maxping; +extern consvar_t cv_pingtimeout; +extern consvar_t cv_showping; + extern consvar_t cv_skipmapcheck; diff --git a/src/d_netfil.c b/src/d_netfil.c index 5e7f59310..bd7089609 100644 --- a/src/d_netfil.c +++ b/src/d_netfil.c @@ -716,7 +716,7 @@ void SV_FileSendTicker(void) if (ram) M_Memcpy(p->data, &f->id.ram[transfer[i].position], 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); // Put flag so receiver knows the total 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 fseek(file->file, pos, SEEK_SET); 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; // Finished? diff --git a/src/d_player.h b/src/d_player.h index d3f84d0e6..6d0c5f5d1 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -151,6 +151,7 @@ typedef enum /*** misc ***/ PF_FORCESTRAFE = 1<<28, // Turning inputs are translated into strafing inputs 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 } pflags_t; diff --git a/src/dehacked.c b/src/dehacked.c index fff9dbee8..40ba0abca 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -1201,6 +1201,14 @@ static void readlevelheader(MYFILE *f, INT32 num) word2 = tmp += 2; 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 if (fastcmp(word, "SUBTITLE")) { @@ -1344,12 +1352,6 @@ static void readlevelheader(MYFILE *f, INT32 num) } // 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")) { deh_strlcpy(mapheaderinfo[num-1]->selectheading, word2, @@ -1573,6 +1575,20 @@ static void readlevelheader(MYFILE *f, INT32 num) else 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 else if (fastcmp(word, "HIDDEN")) @@ -2781,6 +2797,9 @@ static actionpointer_t actionpointers[] = {{A_PterabyteHover}, "A_PTERABYTEHOVER"}, {{A_RolloutSpawn}, "A_ROLLOUTSPAWN"}, {{A_RolloutRock}, "A_ROLLOUTROCK"}, + {{A_DragonbomberSpawn}, "A_DRAGONBOMERSPAWN"}, + {{A_DragonWing}, "A_DRAGONWING"}, + {{A_DragonSegment}, "A_DRAGONSEGMENT"}, {{NULL}, "NONE"}, // 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 word2 = tmp += 2; - strupr(word2); 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)); else if (fastcmp(word, "CONDITIONSET")) 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 - 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)); @@ -3243,7 +3265,6 @@ static void readunlockable(MYFILE *f, INT32 num) // Now get the part after word2 = tmp += 2; - strupr(word2); i = atoi(word2); // used for numerical settings @@ -3253,54 +3274,58 @@ static void readunlockable(MYFILE *f, INT32 num) else if (fastcmp(word, "OBJECTIVE")) deh_strlcpy(unlockables[num].objective, word2, 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 - 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 @@ -4950,22 +4975,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_MINUS_BURST4", "S_MINUS_BURST5", "S_MINUS_POPUP", - "S_MINUS_UPWARD1", - "S_MINUS_UPWARD2", - "S_MINUS_UPWARD3", - "S_MINUS_UPWARD4", - "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", + "S_MINUS_AERIAL1", + "S_MINUS_AERIAL2", + "S_MINUS_AERIAL3", + "S_MINUS_AERIAL4", // Minus dirt "S_MINUSDIRT1", @@ -5041,6 +5054,26 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_PTERABYTE_SWOOPDOWN", "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 "S_BOSSEXPLODE", @@ -5343,6 +5376,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FSGNA", "S_FSGNB", "S_FSGNC", + "S_FSGND", // Black Eggman (Boss 7) "S_BLACKEGG_STND", @@ -7139,6 +7173,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FOUR2", "S_FIVE2", + "S_FLIGHTINDICATOR", + "S_LOCKON1", "S_LOCKON2", "S_LOCKON3", @@ -7322,13 +7358,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_FIREFLOWER2", "S_FIREFLOWER3", "S_FIREFLOWER4", - "S_FIREBALL1", - "S_FIREBALL2", - "S_FIREBALL3", - "S_FIREBALL4", - "S_FIREBALLEXP1", - "S_FIREBALLEXP2", - "S_FIREBALLEXP3", + "S_FIREBALL", + "S_FIREBALLTRAIL1", + "S_FIREBALLTRAIL2", "S_SHELL", "S_PUMA_START1", "S_PUMA_START2", @@ -7497,6 +7529,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_POPHAT_SHOOT1", "S_POPHAT_SHOOT2", "S_POPHAT_SHOOT3", + "S_POPHAT_SHOOT4", + "S_POPSHOT", + "S_POPSHOT_TRAIL", "S_HIVEELEMENTAL_LOOK", "S_HIVEELEMENTAL_PREPARE1", @@ -7628,8 +7663,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_DUST3", "S_DUST4", - "S_WOODDEBRIS", - "S_ROCKSPAWN", "S_ROCKCRUMBLEA", @@ -7648,7 +7681,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit "S_ROCKCRUMBLEN", "S_ROCKCRUMBLEO", "S_ROCKCRUMBLEP", + "S_GFZDEBRIS", "S_BRICKDEBRIS", + "S_WOODDEBRIS", #ifdef SEENAMES "S_NAMECHECK", @@ -7712,6 +7747,10 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_PTERABYTESPAWNER", // Pterabyte spawner "MT_PTERABYTEWAYPOINT", // Pterabyte waypoint "MT_PTERABYTE", // Pterabyte + "MT_DRAGONBOMBER", // Dragonbomber + "MT_DRAGONWING", // Dragonbomber wing + "MT_DRAGONTAIL", // Dragonbomber tail segment + "MT_DRAGONMINE", // Dragonbomber mine // Generic Boss Items "MT_BOSSEXPLODE", @@ -8317,6 +8356,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_BLUEGOOMBA", "MT_FIREFLOWER", "MT_FIREBALL", + "MT_FIREBALLTRAIL", "MT_SHELL", "MT_PUMA", "MT_PUMATRAIL", @@ -8361,6 +8401,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_PENGUINATOR", "MT_POPHAT", "MT_POPSHOT", + "MT_POPSHOT_TRAIL", "MT_HIVEELEMENTAL", "MT_BUMBLEBORE", @@ -8398,7 +8439,6 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_EXPLODE", // Robot Explosion "MT_UWEXPLODE", // Underwater Explosion "MT_DUST", - "MT_WOODDEBRIS", "MT_ROCKSPAWNER", "MT_FALLINGROCK", "MT_ROCKCRUMBLE1", @@ -8417,7 +8457,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_ROCKCRUMBLE14", "MT_ROCKCRUMBLE15", "MT_ROCKCRUMBLE16", + "MT_GFZDEBRIS", "MT_BRICKDEBRIS", + "MT_WOODDEBRIS", #ifdef SEENAMES "MT_NAMECHECK", @@ -8570,6 +8612,7 @@ static const char *const PLAYERFLAG_LIST[] = { /*** misc ***/ "FORCESTRAFE", // Translate turn inputs into strafe inputs "CANCARRY", // Can carry? + "FINISHED", NULL // stop loop here. }; @@ -8856,7 +8899,7 @@ static const char *const MENUTYPES_LIST[] = { "OP_SCREENSHOTS", "OP_ERASEDATA", - // Secrets + // Extras "SR_MAIN", "SR_PANDORA", "SR_LEVELSELECT", @@ -9012,6 +9055,8 @@ struct { {"LF_NOZONE",LF_NOZONE}, {"LF_SAVEGAME",LF_SAVEGAME}, {"LF_MIXNIGHTSCOUNTDOWN",LF_MIXNIGHTSCOUNTDOWN}, + {"LF_NOTITLECARD",LF_NOTITLECARD}, + {"LF_WARNINGTITLE",LF_WARNINGTITLE}, // And map flags {"LF2_HIDEINMENU",LF2_HIDEINMENU}, {"LF2_HIDEINSTATS",LF2_HIDEINSTATS}, diff --git a/src/djgppdos/i_video.c b/src/djgppdos/i_video.c index 6a7641174..56570b7bf 100644 --- a/src/djgppdos/i_video.c +++ b/src/djgppdos/i_video.c @@ -98,6 +98,9 @@ void I_FinishUpdate (void) if (cv_ticrate.value) SCR_DisplayTicRate(); + if (cv_showping.value && netgame && consoleplayer != serverplayer) + SCR_DisplayLocalPing(); + //blast it to the screen // this code sucks //memcpy(dascreen,screens[0],screenwidth*screenheight); diff --git a/src/doomstat.h b/src/doomstat.h index 7d06f03e2..877f5b50b 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -349,6 +349,8 @@ typedef struct #define LF_NOZONE 16 ///< Don't include "ZONE" on level title #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_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_HIDEINSTATS 2 ///< Hide in the statistics screen @@ -433,7 +435,6 @@ typedef struct tic_t time; ///< Time in which the level was finished. UINT32 score; ///< Score when the level was finished. UINT16 rings; ///< Rings when the level was finished. - boolean gotperfect; ///< Got perfect bonus? } recorddata_t; /** Setup for one NiGHTS map. diff --git a/src/f_finale.c b/src/f_finale.c index 466d8b0f4..b8c9dd5f2 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -856,7 +856,7 @@ void F_IntroDrawer(void) if (rendermode != render_none) { F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeColorFill(31); F_WipeEndScreen(); F_RunWipe(99,true); } @@ -866,10 +866,11 @@ void F_IntroDrawer(void) else if (intro_scenenum == 10) { // The only fade to white in the entire damn game. + // (not true) if (rendermode != render_none) { F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0); + F_WipeColorFill(0); F_WipeEndScreen(); F_RunWipe(99,true); } @@ -879,7 +880,7 @@ void F_IntroDrawer(void) if (rendermode != render_none) { F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeColorFill(31); F_WipeEndScreen(); F_RunWipe(99,true); } @@ -926,7 +927,7 @@ void F_IntroDrawer(void) patch_t *radar = W_CachePatchName("RADAR", PU_CACHE); F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeColorFill(31); V_DrawScaledPatch(0, 0, 0, radar); W_UnlockCachedPatch(radar); V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext); @@ -939,7 +940,7 @@ void F_IntroDrawer(void) patch_t *grass = W_CachePatchName("SGRASS5", PU_CACHE); F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeColorFill(31); V_DrawScaledPatch(0, 0, 0, grass); W_UnlockCachedPatch(grass); V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext); @@ -952,7 +953,7 @@ void F_IntroDrawer(void) patch_t *confront = W_CachePatchName("CONFRONT", PU_CACHE); F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeColorFill(31); V_DrawSmallScaledPatch(0, 0, 0, confront); W_UnlockCachedPatch(confront); V_DrawString(8, 128, V_ALLOWLOWERCASE, cutscene_disptext); @@ -965,7 +966,7 @@ void F_IntroDrawer(void) patch_t *sdo = W_CachePatchName("SONICDO2", PU_CACHE); F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeColorFill(31); V_DrawSmallScaledPatch(0, 0, 0, sdo); W_UnlockCachedPatch(sdo); V_DrawString(224, 8, V_ALLOWLOWERCASE, cutscene_disptext); @@ -1385,13 +1386,16 @@ boolean F_CreditResponder(event_t *event) void F_StartGameEvaluation(void) { - // Credits option in secrets menu + // Credits option in extras menu if (cursaveslot == -1) { + S_FadeOutStopMusic(2*MUSICRATE); F_StartGameEnd(); return; } + S_FadeOutStopMusic(5*MUSICRATE); + G_SetGamestate(GS_EVALUATION); // Just in case they're open ... somehow @@ -1537,9 +1541,9 @@ void F_GameEvaluationDrawer(void) } } 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 - 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 } @@ -1586,7 +1590,7 @@ void F_GameEvaluationTicker(void) { HU_SetCEchoFlags(V_YELLOWMAP|V_RETURN8); HU_SetCEchoDuration(6); - HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Prizes only awarded in singleplayer!"); + HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Multiplayer games can't unlock extras!"); S_StartSound(NULL, sfx_s3k68); } else if (!modifiedgame || savemoddata) @@ -1608,7 +1612,7 @@ void F_GameEvaluationTicker(void) { HU_SetCEchoFlags(V_YELLOWMAP|V_RETURN8); HU_SetCEchoDuration(6); - HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Prizes not awarded in modified games!"); + HU_DoCEcho("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\Modified games can't unlock extras!"); S_StartSound(NULL, sfx_s3k68); } } @@ -1621,6 +1625,7 @@ void F_GameEvaluationTicker(void) // ========== #define INFLECTIONPOINT (6*TICRATE) +#define STOPPINGPOINT (14*TICRATE) #define SPARKLLOOPTIME 15 // must be odd void F_StartEnding(void) @@ -1638,7 +1643,7 @@ void F_StartEnding(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); - S_StopMusic(); // todo: placeholder + S_StopMusic(); S_StopSounds(); 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; spritedef_t *sprdef; 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]; // character head, skin specific @@ -1712,13 +1717,16 @@ void F_StartEnding(void) void F_EndingTicker(void) { - if (++finalecount > INFLECTIONPOINT*2) + if (++finalecount > STOPPINGPOINT) { F_StartCredits(); wipetypepre = INT16_MAX; return; } + if (finalecount == -8) + S_ChangeMusicInternal((goodending ? "_endg" : "_endb"), false); + if (goodending && finalecount == INFLECTIONPOINT) // time to swap some assets { endegrk[0] = W_CachePatchName("ENDEGRK2", PU_LEVEL); @@ -2105,26 +2113,26 @@ void F_EndingDrawer(void) if (finalecount < 10) 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; } - 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 V_DrawCenteredString(BASEVIDWIDTH/2, 8, V_ALLOWLOWERCASE|(trans<'|(trans<= (2*INFLECTIONPOINT)-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<"); + V_DrawString(40, ((finalecount == STOPPINGPOINT-(20+TICRATE)) ? 1 : 0)+BASEVIDHEIGHT-16, ((timesBeaten || finalecount >= STOPPINGPOINT-TICRATE) ? V_PURPLEMAP : V_BLUEMAP)|(trans<"); } - if (finalecount > (2*INFLECTIONPOINT)-(20+(2*TICRATE))) + if (finalecount > STOPPINGPOINT-(20+(2*TICRATE))) { INT32 trans2 = abs((5*FINECOSINE((FixedAngle((finalecount*5)<>ANGLETOFINESHIFT & FINEMASK)))>>FRACBITS)+2; if (!donttouch) { - trans = 10 + ((2*INFLECTIONPOINT)-(20+(2*TICRATE))) - finalecount; + trans = 10 + (STOPPINGPOINT-(20+(2*TICRATE))) - finalecount; if (trans > trans2) trans2 = trans; } @@ -2148,7 +2156,6 @@ void F_StartGameEnd(void) gameaction = ga_nothing; paused = false; CON_ToggleOff(); - S_StopMusic(); S_StopSounds(); // In case menus are still up?!! @@ -2582,9 +2589,7 @@ void F_TitleScreenDrawer(void) // Draw that sky! if (curbgcolor >= 0) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, curbgcolor); - else if (titlemapinaction && curbghide && ! hidetitlemap) - D_Render(); - else + else if (!curbghide || !titlemapinaction || gamestate == GS_WAITINGPLAYERS) F_SkyScroll(curbgxspeed, curbgyspeed, curbgname); // 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) finalecount++; + // don't trigger if doing anything besides idling on title + if (gameaction != ga_nothing || gamestate != GS_TITLESCREEN) + return; + // Execute the titlemap camera settings 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? if (!cv_rollingdemos.value || !numDemos) return; @@ -3546,7 +3551,7 @@ void F_ContinueDrawer(void) if (timetonext >= (11*TICRATE)+10) return; - V_DrawLevelTitle(x - (V_LevelNameWidth("CONTINUE")>>1), 16, 0, "CONTINUE"); + V_DrawLevelTitle(x - (V_LevelNameWidth("Continue?")>>1), 16, 0, "Continue?"); // Two stars... patch = W_CachePatchName("CONTSTAR", PU_CACHE); diff --git a/src/f_finale.h b/src/f_finale.h index 3fa7106a9..0002cb3cf 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -141,11 +141,42 @@ void F_MenuPresTicker(boolean run); #define FORCEWIPEOFF -2 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; +// 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_WipeEndScreen(void); 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); boolean F_WipeExists(UINT8 wipetype); diff --git a/src/f_wipe.c b/src/f_wipe.c index 05229f844..b88b39ef0 100644 --- a/src/f_wipe.c +++ b/src/f_wipe.c @@ -18,6 +18,8 @@ #include "r_draw.h" // transtable #include "p_pspr.h" // tr_transxxx +#include "p_local.h" +#include "st_stuff.h" #include "w_wad.h" #include "z_zone.h" @@ -25,8 +27,15 @@ #include "m_menu.h" #include "console.h" #include "d_main.h" +#include "g_game.h" #include "m_misc.h" // movie mode +#include "doomstat.h" + +#ifdef HAVE_BLUA +#include "lua_hud.h" // level title +#endif + #ifdef HWRENDER #include "hardware/hw_main.h" #endif @@ -82,8 +91,12 @@ UINT8 wipedefs[NUMWIPEDEFS] = { //-------------------------------------------------------------------------- boolean WipeInAction = false; +boolean WipeStageTitle = false; INT32 lastwipetic = 0; +wipestyle_t wipestyle = WIPESTYLE_NORMAL; +wipestyleflags_t wipestyleflags = WSF_CROSSFADE; + #ifndef NOWIPE static UINT8 *wipe_scr_start; //screen 3 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 pcolor = &pMasterPalette[*lump++]; - *mask++ = FixedDiv((pcolor->s.red+1)<>FRACBITS; + if (wipestyle == WIPESTYLE_LEVEL) + *mask++ = pcolor->s.red / FADECOLORMAPDIV; + else + *mask++ = FixedDiv((pcolor->s.red+1)<>FRACBITS; } fm.xscale = FixedDiv(vid.width<levelflags & LF_NOTITLECARD) + && *mapheaderinfo[gamemap-1]->lvlttl != '\0') + { + ST_runTitleCard(); + ST_drawWipeTitleCard(); + } +} + /** Wipe ticker * * \param fademask pixels to change @@ -251,7 +282,7 @@ static void F_DoWipe(fademask_t *fademask) relativepos += vid.width; } } - else if (*mask == 10) + else if (*mask >= ((wipestyle == WIPESTYLE_LEVEL) ? FADECOLORMAPROWS : 10)) { // shortcut - memcpy target to work while (draw_linestogo--) @@ -262,8 +293,25 @@ static void F_DoWipe(fademask_t *fademask) } else { - // pointer to transtable that this mask would use - transtbl = transtables + ((9 - *mask)< 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) { INT32 i; UINT32 totalscore = 0; tic_t totaltime = 0; + UINT8 earnedEmblems; const size_t glen = strlen(srb2home)+1+strlen("replay")+1+strlen(timeattackfolder)+1+strlen("MAPXX")+1; char *gpath; @@ -640,6 +733,9 @@ void G_SetNightsRecords(void) } 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 Nextmap_OnChange(); } @@ -923,11 +1019,11 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics) movebkey = PLAYER1INPUTDOWN(gc_backward); 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; 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 if (!thisjoyaiming && joyaiming) @@ -1252,11 +1348,11 @@ void G_BuildTiccmd2(ticcmd_t *cmd, INT32 realtics) movebkey = PLAYER2INPUTDOWN(gc_backward); 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; 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 if (!thisjoyaiming && joyaiming) @@ -1703,6 +1799,58 @@ void G_DoLoadLevel(boolean resetplayer) 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; boolean pausebreakkey = false; static INT32 camtoggledelay, camtoggledelay2 = 0; @@ -1868,7 +2016,7 @@ boolean G_Responder(event_t *ev) pausedelay = 1+(NEWTICRATE/2); else if (++pausedelay > 1+(NEWTICRATE/2)+(NEWTICRATE/3)) { - G_SetRetryFlag(); + G_SetModeAttackRetryFlag(); return true; } pausedelay++; // counteract subsequent subtraction this frame @@ -1992,7 +2140,7 @@ void G_Ticker(boolean run) if (titledemo) F_TitleDemoTicker(); P_Ticker(run); // tic the game - ST_Ticker(); + ST_Ticker(run); F_TextPromptTicker(); AM_Ticker(); HU_Ticker(); @@ -2175,6 +2323,9 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps) outofcoop = players[player].outofcoop; 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 if (!(netgame || multiplayer)) pflags |= (players[player].pflags & (PF_GODMODE|PF_NOCLIP|PF_INVIS)); @@ -2709,6 +2860,7 @@ void G_DoReborn(INT32 playernum) // Do a wipe wipegamestate = -1; + wipestyleflags = WSF_CROSSFADE; if (camera.chase) P_ResetCamera(&players[displayplayer], &camera); @@ -2829,10 +2981,35 @@ void G_AddPlayer(INT32 playernum) if (G_GametypeUsesLives() || ((netgame || multiplayer) && gametype == GT_COOP)) p->lives = cv_startinglives.value; - if (countplayers && !notexiting) + if ((countplayers && !notexiting) || G_IsSpecialStage(gamemap)) 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) { if (gamestate == GS_LEVEL) @@ -2925,7 +3102,7 @@ boolean G_GametypeUsesLives(void) // Coop, Competitive if ((gametype == GT_COOP || gametype == GT_COMPETITION) && !(modeattacking || metalrecording) // No lives in Time Attack - //&& !G_IsSpecialStage(gamemap) + && !G_IsSpecialStage(gamemap) && !(maptol & TOL_NIGHTS)) // No lives in NiGHTS return true; return false; @@ -3038,12 +3215,51 @@ static INT16 RandMap(INT16 tolflags, INT16 pprevmap) 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 // static void G_DoCompleted(void) { INT32 i; + boolean spec = G_IsSpecialStage(gamemap); tokenlist = 0; // Reset the list @@ -3076,14 +3292,14 @@ static void G_DoCompleted(void) nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1); // Remember last map for when you come out of the special stage. - if (!G_IsSpecialStage(gamemap)) + if (!spec) lastmap = nextmap; // 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 // the current gametype. (Helps avoid playing boss levels in Race, // for instance). - if (!token && !G_IsSpecialStage(gamemap) + if (!token && !spec && (nextmap >= 0 && nextmap < NUMMAPS)) { register INT16 cm = nextmap; @@ -3147,7 +3363,7 @@ static void G_DoCompleted(void) 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 automapactive = false; @@ -3166,17 +3382,29 @@ static void G_DoCompleted(void) if (nextmap < NUMMAPS && !mapheaderinfo[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(); + } else { G_SetGamestate(GS_INTERMISSION); Y_StartIntermission(); + G_UpdateVisited(); } } void G_AfterIntermission(void) { + Y_CleanupScreenBuffer(); + + if (modeattacking) + { + M_EndModeAttackRun(); + return; + } + HU_ClearCEcho(); if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene. @@ -3343,7 +3571,6 @@ void G_LoadGameData(void) UINT32 recscore; tic_t rectime; UINT16 recrings; - boolean gotperf; UINT8 recmares; INT32 curmare; @@ -3441,7 +3668,7 @@ void G_LoadGameData(void) recscore = READUINT32(save_p); rectime = (tic_t)READUINT32(save_p); recrings = READUINT16(save_p); - gotperf = (boolean)READUINT8(save_p); + save_p++; // compat if (recrings > 10000 || recscore > MAXSCORE) goto datacorrupt; @@ -3453,9 +3680,6 @@ void G_LoadGameData(void) mainrecords[i]->time = rectime; mainrecords[i]->rings = recrings; } - - if (gotperf) - mainrecords[i]->gotperfect = gotperf; } // Nights records @@ -3587,15 +3811,14 @@ void G_SaveGameData(void) WRITEUINT32(save_p, mainrecords[i]->score); WRITEUINT32(save_p, mainrecords[i]->time); WRITEUINT16(save_p, mainrecords[i]->rings); - WRITEUINT8(save_p, mainrecords[i]->gotperfect); } else { WRITEUINT32(save_p, 0); WRITEUINT32(save_p, 0); WRITEUINT16(save_p, 0); - WRITEUINT8(save_p, 0); } + WRITEUINT8(save_p, 0); // compat } // NiGHTS records @@ -4031,7 +4254,7 @@ char *G_BuildMapTitle(INT32 mapnum) len += strlen(mapheaderinfo[mapnum-1]->lvlttl); if (!(mapheaderinfo[mapnum-1]->levelflags & LF_NOZONE)) { - zonetext = M_GetText("ZONE"); + zonetext = M_GetText("Zone"); len += strlen(zonetext) + 1; // ' ' + zonetext } if (actnum > 0) @@ -4642,6 +4865,12 @@ void G_WriteGhostTic(mobj_t *ghost) 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); WRITEINT16(demo_p,temp); 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); WRITEUINT8(demo_p,(ghost->player->followmobj->frame & FF_FRAMEMASK)); 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; } @@ -5194,7 +5418,10 @@ void G_ReadMetalTic(mobj_t *metal) { // But wait, there's more! xziptic = READUINT8(metal_p); if (xziptic & EZT_FLIP) + { metal->eflags ^= MFE_VERTICALFLIP; + metal->flags2 ^= MF2_OBJECTFLIP; + } if (xziptic & EZT_SCALE) { metal->destscale = READFIXED(metal_p); @@ -6702,6 +6929,22 @@ boolean G_GetRetryFlag(void) 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 INT32 G_TicsToHours(tic_t tics) { diff --git a/src/g_game.h b/src/g_game.h index 2489ff097..0a575c099 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -140,7 +140,8 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost); void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, boolean SSSG, boolean FLS); void G_DoLoadLevel(boolean resetplayer); - +void G_StartTitleCard(void); +void G_PreLevelTitleCard(tic_t ticker, boolean update); void G_DeferedPlayDemo(const char *demo); // 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_PlatformGametype(void); boolean G_TagGametype(void); +boolean G_EnoughPlayersFinished(void); void G_ExitLevel(void); void G_NextLevel(void); void G_Continue(void); @@ -223,10 +225,14 @@ void G_AddPlayer(INT32 playernum); void G_SetExitGameFlag(void); void G_ClearExitGameFlag(void); boolean G_GetExitGameFlag(void); + void G_SetRetryFlag(void); void G_ClearRetryFlag(void); boolean G_GetRetryFlag(void); +void G_SetModeAttackRetryFlag(void); +void G_ClearModeAttackRetryFlag(void); +boolean G_GetModeAttackRetryFlag(void); void G_LoadGameData(void); void G_LoadGameSettings(void); diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 6eaafca6d..2d4704b8b 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -771,18 +771,25 @@ void HWR_InitTextureCache(void) gr_textures2 = NULL; } - // Callback function for HWR_FreeTextureCache. static void FreeMipmapColormap(INT32 patchnum, void *patch) { - GLPatch_t* const grpatch = patch; + GLPatch_t* const pat = patch; (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; - grpatch->mipmap->nextcolormap = grmip->nextcolormap; - if (grmip->grInfo.data) Z_Free(grmip->grInfo.data); - free(grmip); + // Confusing at first, but pat->mipmap->nextcolormap + // at the beginning of the loop is the first colormap + // from the linked list of colormaps + 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 - // 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++) M_AATreeIterate(wadfiles[i]->hwrcache, FreeMipmapColormap); diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index e493dafac..4519e1280 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -380,9 +380,9 @@ void HWR_DrawStretchyFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t { FSurfaceInfo Surf; 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]; - else if (alphalevel == 14) Surf.FlatColor.s.alpha = softwaretranstogl[cv_translucenthud.value]; - else if (alphalevel == 15) Surf.FlatColor.s.alpha = softwaretranstogl_hi[cv_translucenthud.value]; + if (alphalevel == 13) Surf.FlatColor.s.alpha = softwaretranstogl_lo[st_translucency]; + else if (alphalevel == 14) Surf.FlatColor.s.alpha = softwaretranstogl[st_translucency]; + else if (alphalevel == 15) Surf.FlatColor.s.alpha = softwaretranstogl_hi[st_translucency]; else Surf.FlatColor.s.alpha = softwaretranstogl[10-alphalevel]; flags |= PF_Modulated; 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; 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]; - else if (alphalevel == 14) Surf.FlatColor.s.alpha = softwaretranstogl[cv_translucenthud.value]; - else if (alphalevel == 15) Surf.FlatColor.s.alpha = softwaretranstogl_hi[cv_translucenthud.value]; + if (alphalevel == 13) Surf.FlatColor.s.alpha = softwaretranstogl_lo[st_translucency]; + else if (alphalevel == 14) Surf.FlatColor.s.alpha = softwaretranstogl[st_translucency]; + else if (alphalevel == 15) Surf.FlatColor.s.alpha = softwaretranstogl_hi[st_translucency]; else Surf.FlatColor.s.alpha = softwaretranstogl[10-alphalevel]; flags |= PF_Modulated; HWD.pfnDrawPolygon(&Surf, v, 4, flags); diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index 491cb739f..4df71d145 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -184,6 +184,7 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_CANG &lspr[NOLIGHT], // SPR_PYRE &lspr[NOLIGHT], // SPR_PTER + &lspr[NOLIGHT], // SPR_DRAB // Generic Boos Items &lspr[JETLIGHT_L], // SPR_JETF // Boss jet fumes @@ -505,6 +506,7 @@ light_t *t_lspr[NUMSPRITES] = // Game Indicators &lspr[NOLIGHT], // SPR_SCOR &lspr[NOLIGHT], // SPR_DRWN + &lspr[NOLIGHT], // SPR_FLII &lspr[NOLIGHT], // SPR_LCKN &lspr[NOLIGHT], // SPR_TTAG &lspr[NOLIGHT], // SPR_GFLG @@ -584,7 +586,6 @@ light_t *t_lspr[NUMSPRITES] = &lspr[SUPERSPARK_L], // SPR_BOM3 &lspr[NOLIGHT], // SPR_BOM4 &lspr[REDBALL_L], // SPR_BMNB - &lspr[NOLIGHT], // SPR_WDDB // Crumbly rocks &lspr[NOLIGHT], // SPR_ROIA @@ -604,8 +605,10 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_ROIO &lspr[NOLIGHT], // SPR_ROIP - // Bricks + // Level debris + &lspr[NOLIGHT], // SPR_GFZD &lspr[NOLIGHT], // SPR_BRIC + &lspr[NOLIGHT], // SPR_WDDB // Gravity Well Objects &lspr[NOLIGHT], // SPR_GWLG diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index f9e26733e..51c976973 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -40,6 +40,7 @@ #include "../st_stuff.h" #include "../i_system.h" #include "../m_cheat.h" +#include "../f_finale.h" #ifdef ESLOPE #include "../p_slopes.h" #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_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_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, @@ -1973,7 +1973,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac) { // Single sided line... Deal only with the middletexture (if one exists) 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; @@ -4068,6 +4068,7 @@ static gr_vissprite_t *HWR_NewVisSprite(void) return HWR_GetVisSprite(gr_visspritecount++); } +#ifdef GLBADSHADOWS // 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) { @@ -4098,6 +4099,7 @@ static fixed_t HWR_OpaqueFloorAtPos(fixed_t x, fixed_t y, fixed_t z, fixed_t hei return floorz; } +#endif //#ifdef GLBADSHADOWS // // HWR_DoCulling @@ -4139,6 +4141,7 @@ static boolean HWR_DoCulling(line_t *cullheight, line_t *viewcullheight, float v return false; } +#ifdef GLBADSHADOWS static void HWR_DrawSpriteShadow(gr_vissprite_t *spr, GLPatch_t *gpatch, float this_scale) { 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); } } +#endif //#ifdef GLBADSHADOWS // This is expecting a pointer to an array containing 4 wallVerts for a sprite 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 HWR_GetMappedPatch(gpatch, spr->colormap); +#ifdef GLBADSHADOWS // Draw shadow BEFORE sprite 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. @@ -4401,6 +4406,7 @@ static void HWR_SplitSprite(gr_vissprite_t *spr) //////////////////// HWR_DrawSpriteShadow(spr, gpatch, this_scale); } +#endif //#ifdef GLBADSHADOWS baseWallVerts[0].x = baseWallVerts[3].x = spr->x1; 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 HWR_GetMappedPatch(gpatch, spr->colormap); +#ifdef GLBADSHADOWS // Draw shadow BEFORE sprite 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. @@ -4803,6 +4810,7 @@ static void HWR_DrawSprite(gr_vissprite_t *spr) //////////////////// HWR_DrawSpriteShadow(spr, gpatch, this_scale); } +#endif //#ifdef GLBADSHADOWS // if it has a dispoffset, push it a little towards the camera if (spr->dispoffset) { @@ -7017,7 +7025,6 @@ void HWR_StartScreenWipe(void) void HWR_EndScreenWipe(void) { - HWRWipeCounter = 0.0f; //CONS_Debug(DBG_RENDER, "In HWR_EndScreenWipe()\n"); HWD.pfnEndScreenWipe(); } @@ -7027,38 +7034,55 @@ void HWR_DrawIntermissionBG(void) 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"; - lumpnum_t lumpnum; size_t lsize; - if (wipenum > 99 || scrnnum > 99) // not a valid wipe number - return; // shouldn't end up here really, the loop should've stopped running beforehand + // not a valid wipe number + 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 - sprintf(&lumpname[4], "%.2hu%.2hu", (UINT16)wipenum, (UINT16)scrnnum); - lumpnum = W_CheckNumForName(lumpname); + // puts the numbers into the wipename + lumpname[4] = '0'+(wipenum/10); + 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 - return; - - lsize = W_LumpLength(lumpnum); + // again, shouldn't be here really + if (wipelumpnum == LUMPERROR) + return false; + lsize = W_LumpLength(wipelumpnum); if (!(lsize == 256000 || lsize == 64000 || lsize == 16000 || lsize == 4000)) { 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(); +} - HWRWipeCounter += 0.05f; // increase opacity of end screen - - if (HWRWipeCounter > 1.0f) - HWRWipeCounter = 1.0f; +void HWR_DoTintedWipe(UINT8 wipenum, UINT8 scrnnum) +{ + // It does the same thing + HWR_DoWipe(wipenum, scrnnum); } void HWR_MakeScreenFinalTexture(void) diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index fa230289f..e19c557d0 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -66,6 +66,7 @@ void HWR_StartScreenWipe(void); void HWR_EndScreenWipe(void); void HWR_DrawIntermissionBG(void); void HWR_DoWipe(UINT8 wipenum, UINT8 scrnnum); +void HWR_DoTintedWipe(UINT8 wipenum, UINT8 scrnnum); void HWR_MakeScreenFinalTexture(void); void HWR_DrawScreenFinalTexture(int width, int height); diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index d208580a9..8c0ca7155 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -2587,7 +2587,6 @@ EXPORT void HWRAPI(DoScreenWipe)(void) tex_downloaded = endScreenWipe; } - // Create a texture from the screen. EXPORT void HWRAPI(MakeScreenTexture) (void) { diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 90f4ceedf..afca4c773 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -2322,13 +2322,14 @@ void HU_Erase(void) // // 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 barcolor = 35; // color we use for the bars (green, yellow or red) SINT8 i = 0; 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) { @@ -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. - 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 { - 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) - 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; } @@ -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 (!(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 // 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); } - 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); if (gametype == GT_RACE) @@ -2578,7 +2579,7 @@ static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer) if (!splitscreen) { 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 //V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); } @@ -2702,7 +2703,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer) if (!splitscreen) { 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 // 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); 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 // 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) 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); // 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 (!(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 // V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); } diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 55b61d4b7..47419d29a 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -28,7 +28,7 @@ // Level title font #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 CRED_FONTSTART '!' // the first font character @@ -113,7 +113,7 @@ void HU_Drawer(void); char HU_dequeueChatChar(void); void HU_Erase(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_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer); void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer); diff --git a/src/info.c b/src/info.c index 0583ac549..12dd61a84 100644 --- a/src/info.c +++ b/src/info.c @@ -72,6 +72,7 @@ char sprnames[NUMSPRITES + 1][5] = "CANG", // Canarivore gas "PYRE", // Pyre Fly "PTER", // Pterabyte + "DRAB", // Dragonbomber // Generic Boss Items "JETF", // Boss jet fumes @@ -402,6 +403,7 @@ char sprnames[NUMSPRITES + 1][5] = // Game Indicators "SCOR", // Score logo "DRWN", // Drowning Timer + "FLII", // Flight indicator "LCKN", // Target "TTAG", // Tag Sign "GFLG", // Got Flag sign @@ -481,7 +483,6 @@ char sprnames[NUMSPRITES + 1][5] = "BOM3", // Boss Explosion 2 "BOM4", // Underwater Explosion "BMNB", // Mine Explosion - "WDDB", // Wood Debris // Crumbly rocks "ROIA", @@ -501,8 +502,10 @@ char sprnames[NUMSPRITES + 1][5] = "ROIO", "ROIP", - // Bricks - "BRIC", + // Level debris + "GFZD", // GFZ debris + "BRIC", // Bricks + "WDDB", // Wood Debris // Gravity Well Objects "GWLG", @@ -632,7 +635,7 @@ playersprite_t spr2defaults[NUMPLAYERSPRITES] = { 0, // SPR2_TRNS, 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) SPR2_NFLY, // SPR2_NDRL, 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, 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_MNUS, 0, 1, {A_MinusPopup}, 0, 0, S_MINUS_UPWARD1}, // S_MINUS_POPUP - {SPR_MNUS, 0, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD2}, // S_MINUS_UPWARD1 - {SPR_MNUS, 1, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD3}, // S_MINUS_UPWARD2 - {SPR_MNUS, 2, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD4}, // S_MINUS_UPWARD3 - {SPR_MNUS, 3, 1, {A_MinusCheck}, 0, 0, S_MINUS_UPWARD5}, // S_MINUS_UPWARD4 - {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_MNUS, 3, 1, {A_MinusPopup}, 0, 0, S_MINUS_AERIAL1}, // S_MINUS_POPUP + {SPR_MNUS, 0, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL2}, // S_MINUS_AERIAL1 + {SPR_MNUS, 1, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL3}, // S_MINUS_AERIAL2 + {SPR_MNUS, 2, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL4}, // S_MINUS_AERIAL3 + {SPR_MNUS, 3, 1, {A_MinusCheck}, 0, 1, S_MINUS_AERIAL1}, // S_MINUS_AERIAL4 {SPR_MNUD, FF_ANIMATE, 6, {NULL}, 1, 5, S_MINUSDIRT2}, // S_MINUSDIRT1 {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, 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 {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, 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, 3|FF_PAPERSPRITE, -1, {NULL}, 0, 0, S_NULL}, // S_FSGND // Black Eggman (Boss 7) {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, 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, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON2 {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 // Thrown Mario Fireball - {SPR_FBLL, FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_FIREBALL2}, // S_FIREBALL1 - {SPR_FBLL, FF_FULLBRIGHT|1, 3, {NULL}, 0, 0, S_FIREBALL3}, // S_FIREBALL2 - {SPR_FBLL, FF_FULLBRIGHT|2, 3, {NULL}, 0, 0, S_FIREBALL4}, // S_FIREBALL3 - {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 + {SPR_FBLL, FF_FULLBRIGHT, 1, {A_SpawnObjectRelative}, 0, MT_FIREBALLTRAIL, S_FIREBALL}, // S_FIREBALL + {SPR_FBLL, 1|FF_FULLBRIGHT|FF_TRANS50, 1, {A_SetScale}, FRACUNIT*3/4, 0, S_FIREBALLTRAIL2}, // S_FIREBALLTRAIL1 + {SPR_FBLL, 1|FF_FULLBRIGHT|FF_TRANS50, 8, {A_SetScale}, FRACUNIT/6, 1, S_NULL}, // S_FIREBALLTRAIL2 // Turtle 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_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, 2, 1, {NULL}, 0, 0, S_POPHAT_SHOOT3}, // S_POPHAT_SHOOT2 - {SPR_POPH, 0, 57, {NULL}, 0, 0, S_POPHAT_LOOK}, // S_POPHAT_SHOOT3 + {SPR_POPH, 1, 0, {A_MultiShotDist}, (MT_SPINDUST<<16)|4, 24, S_POPHAT_SHOOT2}, // S_POPHAT_SHOOT1 + {SPR_POPH, 1, 2, {A_LobShot}, MT_POPSHOT, (70<<16)|60, S_POPHAT_SHOOT3}, // S_POPHAT_SHOOT2 + {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, 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, 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_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_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_WDDB, FF_ANIMATE, -1, {A_DebrisRandom}, 7, 2, S_NULL}, // S_WOODDEBRIS #ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK @@ -5238,6 +5252,114 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 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 -1, // doomednum S_BOSSEXPLODE, // spawnstate @@ -6115,11 +6237,11 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // meleestate S_NULL, // missilestate S_FSGNC, // deathstate - S_NULL, // xdeathstate + S_FSGND, // xdeathstate sfx_None, // deathsound 0, // speed - 124*FRACUNIT, // radius - 640*FRACUNIT, // height + 74*FRACUNIT, // radius + 320*FRACUNIT, // height 0, // display offset 0, // mass 0, // damage @@ -13247,7 +13369,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 100, // mass 0, // damage sfx_None, // activesound - MF_SPECIAL|MF_PAIN|MF_NOGRAVITY, // flags + MF_SPECIAL|MF_PAIN|MF_NOGRAVITY|MF_FIRE, // flags S_NULL // raisestate }, @@ -13315,7 +13437,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // attacksound S_NULL, // painstate 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, // missilestate S_NULL, // deathstate @@ -18910,31 +19032,58 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_FIREBALL -1, // doomednum - S_FIREBALL1, // spawnstate + S_FIREBALL, // spawnstate 1000, // spawnhealth - S_FIREBALLEXP1, // seestate + S_NULL, // seestate sfx_None, // seesound 8, // reactiontime sfx_None, // attacksound S_NULL, // painstate 0, // painchance sfx_None, // painsound - S_FIREBALLEXP1, // meleestate - S_FIREBALLEXP1, // missilestate - S_FIREBALLEXP1, // deathstate - S_FIREBALLEXP1, // xdeathstate - sfx_mario1, // deathsound - 10*FRACUNIT, // speed + S_NULL, // meleestate + S_NULL, // missilestate + S_NULL, // deathstate + S_NULL, // xdeathstate + sfx_None, // deathsound + 40*FRACUNIT, // speed 4*FRACUNIT, // radius 8*FRACUNIT, // height 0, // display offset DMG_FIRE, // mass 1, // damage sfx_None, // activesound - MF_NOBLOCKMAP|MF_FIRE|MF_MISSILE, // flags + MF_FIRE|MF_BOUNCE|MF_MISSILE, // flags 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 1804, // doomednum S_SHELL, // spawnstate @@ -19989,10 +20138,10 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = { // MT_POPSHOT -1, // doomednum - S_ROCKCRUMBLEI, // spawnstate + S_POPSHOT, // spawnstate 1, // spawnhealth S_NULL, // seestate - sfx_cannon, // seesound + sfx_kc4c, // seesound 0, // reactiontime sfx_None, // attacksound S_NULL, // painstate @@ -20000,9 +20149,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate - S_XPLD1, // deathstate + S_SONIC3KBOSSEXPLOSION1, // deathstate S_NULL, // xdeathstate - sfx_pop, // deathsound + sfx_cybdth, // deathsound 0, // speed 16*FRACUNIT, // radius 32*FRACUNIT, // height @@ -20014,6 +20163,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 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 127, // doomednum S_HIVEELEMENTAL_LOOK, // spawnstate @@ -20773,33 +20949,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 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 1202, // doomednum S_ROCKSPAWN, // spawnstate @@ -21286,16 +21435,16 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, - { // MT_BRICKDEBRIS + { // MT_GFZDEBRIS -1, // doomednum - S_BRICKDEBRIS, // spawnstate - 1, // spawnhealth + S_GFZDEBRIS, // spawnstate + 1, // spawnhealth S_NULL, // seestate - sfx_None, // seesound + sfx_None, // seesound 0, // reactiontime sfx_None, // attacksound S_NULL, // painstate - 0, // painchance + 0, // painchance sfx_None, // painsound S_NULL, // meleestate S_NULL, // missilestate @@ -21303,16 +21452,70 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_None, // deathsound 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 0, // display offset - 100, // mass + 100, // mass 0, // damage - sfx_None, // activesound + sfx_None, // activesound MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_RUNSPAWNFUNC|MF_NOCLIPHEIGHT|MF_SCENERY, // flags 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 { // MT_NAMECHECK -1, // doomednum diff --git a/src/info.h b/src/info.h index bbb6a21fc..f3f50fab7 100644 --- a/src/info.h +++ b/src/info.h @@ -282,6 +282,9 @@ void A_SpawnPterabytes(); void A_PterabyteHover(); void A_RolloutSpawn(); 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 #define NUMMOBJFREESLOTS 512 @@ -334,6 +337,7 @@ typedef enum sprite SPR_CANG, // Canarivore gas SPR_PYRE, // Pyre Fly SPR_PTER, // Pterabyte + SPR_DRAB, // Dragonbomber // Generic Boss Items SPR_JETF, // Boss jet fumes @@ -664,6 +668,7 @@ typedef enum sprite // Game Indicators SPR_SCOR, // Score logo SPR_DRWN, // Drowning Timer + SPR_FLII, // AI flight indicator SPR_LCKN, // Target SPR_TTAG, // Tag Sign SPR_GFLG, // Got Flag sign @@ -743,7 +748,6 @@ typedef enum sprite SPR_BOM3, // Boss Explosion 2 SPR_BOM4, // Underwater Explosion SPR_BMNB, // Mine Explosion - SPR_WDDB, // Wood Debris // Crumbly rocks SPR_ROIA, @@ -763,8 +767,10 @@ typedef enum sprite SPR_ROIO, SPR_ROIP, - // Bricks - SPR_BRIC, + // Level debris + SPR_GFZD, // GFZ debris + SPR_BRIC, // Bricks + SPR_WDDB, // Wood Debris // Gravity Well Objects SPR_GWLG, @@ -1264,22 +1270,10 @@ typedef enum state S_MINUS_BURST4, S_MINUS_BURST5, S_MINUS_POPUP, - S_MINUS_UPWARD1, - S_MINUS_UPWARD2, - S_MINUS_UPWARD3, - S_MINUS_UPWARD4, - 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, + S_MINUS_AERIAL1, + S_MINUS_AERIAL2, + S_MINUS_AERIAL3, + S_MINUS_AERIAL4, // Minus dirt S_MINUSDIRT1, @@ -1355,6 +1349,26 @@ typedef enum state S_PTERABYTE_SWOOPDOWN, 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 S_BOSSEXPLODE, @@ -1657,6 +1671,7 @@ typedef enum state S_FSGNA, S_FSGNB, S_FSGNC, + S_FSGND, // Black Eggman (Boss 7) S_BLACKEGG_STND, @@ -3454,6 +3469,8 @@ typedef enum state S_FOUR2, S_FIVE2, + S_FLIGHTINDICATOR, + S_LOCKON1, S_LOCKON2, S_LOCKON3, @@ -3637,13 +3654,9 @@ typedef enum state S_FIREFLOWER2, S_FIREFLOWER3, S_FIREFLOWER4, - S_FIREBALL1, - S_FIREBALL2, - S_FIREBALL3, - S_FIREBALL4, - S_FIREBALLEXP1, - S_FIREBALLEXP2, - S_FIREBALLEXP3, + S_FIREBALL, + S_FIREBALLTRAIL1, + S_FIREBALLTRAIL2, S_SHELL, S_PUMA_START1, S_PUMA_START2, @@ -3812,6 +3825,9 @@ typedef enum state S_POPHAT_SHOOT1, S_POPHAT_SHOOT2, S_POPHAT_SHOOT3, + S_POPHAT_SHOOT4, + S_POPSHOT, + S_POPSHOT_TRAIL, S_HIVEELEMENTAL_LOOK, S_HIVEELEMENTAL_PREPARE1, @@ -3943,8 +3959,6 @@ typedef enum state S_DUST3, S_DUST4, - S_WOODDEBRIS, - S_ROCKSPAWN, S_ROCKCRUMBLEA, @@ -3964,8 +3978,10 @@ typedef enum state S_ROCKCRUMBLEO, S_ROCKCRUMBLEP, - // Bricks + // Level debris + S_GFZDEBRIS, S_BRICKDEBRIS, + S_WOODDEBRIS, #ifdef SEENAMES S_NAMECHECK, @@ -4049,6 +4065,10 @@ typedef enum mobj_type MT_PTERABYTESPAWNER, // Pterabyte spawner MT_PTERABYTEWAYPOINT, // Pterabyte waypoint MT_PTERABYTE, // Pterabyte + MT_DRAGONBOMBER, // Dragonbomber + MT_DRAGONWING, // Dragonbomber wing + MT_DRAGONTAIL, // Dragonbomber tail segment + MT_DRAGONMINE, // Dragonbomber mine // Generic Boss Items MT_BOSSEXPLODE, @@ -4654,6 +4674,7 @@ typedef enum mobj_type MT_BLUEGOOMBA, MT_FIREFLOWER, MT_FIREBALL, + MT_FIREBALLTRAIL, MT_SHELL, MT_PUMA, MT_PUMATRAIL, @@ -4698,6 +4719,7 @@ typedef enum mobj_type MT_PENGUINATOR, MT_POPHAT, MT_POPSHOT, + MT_POPSHOT_TRAIL, MT_HIVEELEMENTAL, MT_BUMBLEBORE, @@ -4735,7 +4757,6 @@ typedef enum mobj_type MT_EXPLODE, // Robot Explosion MT_UWEXPLODE, // Underwater Explosion MT_DUST, - MT_WOODDEBRIS, MT_ROCKSPAWNER, MT_FALLINGROCK, MT_ROCKCRUMBLE1, @@ -4755,8 +4776,10 @@ typedef enum mobj_type MT_ROCKCRUMBLE15, MT_ROCKCRUMBLE16, - // Bricks + // Level debris + MT_GFZDEBRIS, MT_BRICKDEBRIS, + MT_WOODDEBRIS, #ifdef SEENAMES MT_NAMECHECK, diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 2e14b3f26..03f1aff18 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -1165,6 +1165,17 @@ static int lib_pElementalFire(lua_State *L) 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) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); @@ -2674,6 +2685,13 @@ static int lib_gSetCustomExitVars(lua_State *L) return 0; } +static int lib_gEnoughPlayersFinished(lua_State *L) +{ + INLEVEL + lua_pushboolean(L, G_EnoughPlayersFinished()); + return 1; +} + static int lib_gExitLevel(lua_State *L) { int n = lua_gettop(L); // Num arguments @@ -2869,6 +2887,7 @@ static luaL_Reg lib[] = { {"P_DoBubbleBounce",lib_pDoBubbleBounce}, {"P_BlackOw",lib_pBlackOw}, {"P_ElementalFire",lib_pElementalFire}, + {"P_DoPlayerFinish",lib_pDoPlayerFinish}, {"P_DoPlayerExit",lib_pDoPlayerExit}, {"P_InstaThrust",lib_pInstaThrust}, {"P_ReturnThrustX",lib_pReturnThrustX}, @@ -2981,6 +3000,7 @@ static luaL_Reg lib[] = { {"G_BuildMapName",lib_gBuildMapName}, {"G_DoReborn",lib_gDoReborn}, {"G_SetCustomExitVars",lib_gSetCustomExitVars}, + {"G_EnoughPlayersFinished",lib_gEnoughPlayersFinished}, {"G_ExitLevel",lib_gExitLevel}, {"G_IsSpecialStage",lib_gIsSpecialStage}, {"G_GametypeUsesLives",lib_gGametypeUsesLives}, diff --git a/src/lua_hud.h b/src/lua_hud.h index 7f928f7c4..d1adef7dc 100644 --- a/src/lua_hud.h +++ b/src/lua_hud.h @@ -43,3 +43,4 @@ boolean LUA_HudEnabled(enum hud option); void LUAh_GameHUD(player_t *stplyr); void LUAh_ScoresHUD(void); void LUAh_TitleHUD(void); +void LUAh_TitleCardHUD(player_t *stplyr); diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 62be61283..9b12dd3c0 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -92,12 +92,14 @@ static const char *const patch_opt[] = { enum hudhook { hudhook_game = 0, hudhook_scores, - hudhook_title + hudhook_title, + hudhook_titlecard }; static const char *const hudhook_opt[] = { "game", "scores", "title", + "titlecard", NULL}; // alignment types for v.drawString @@ -910,9 +912,17 @@ static int libd_RandomChance(lua_State *L) 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 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 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}, {"renderer", libd_renderer}, {"localTransFlag", libd_getlocaltransflag}, + {"userTransFlag", libd_getusertransflag}, {NULL, NULL} }; @@ -1043,6 +1054,9 @@ int LUA_HudLib(lua_State *L) lua_newtable(L); 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"); luaL_newmetatable(L, META_HUDINFO); @@ -1180,4 +1194,38 @@ void LUAh_TitleHUD(void) hud_running = false; } +void LUAh_TitleCardHUD(player_t *stplayr) +{ + if (!gL || !(hudAvailable & (1<= 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]); return 1; } @@ -756,7 +756,7 @@ static int power_set(lua_State *L) powertype_t p = luaL_checkinteger(L, 2); UINT16 i = (UINT16)luaL_checkinteger(L, 3); 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) return luaL_error(L, "Do not alter player_t in HUD rendering code!"); powers[p] = i; diff --git a/src/m_argv.c b/src/m_argv.c index 117ec7833..bb43ad57c 100644 --- a/src/m_argv.c +++ b/src/m_argv.c @@ -16,6 +16,7 @@ #include "doomdef.h" #include "command.h" #include "m_argv.h" +#include "m_misc.h" /** \brief number of arg */ @@ -161,7 +162,7 @@ void M_FindResponseFile(void) if (!file) I_Error("No more free memory for the response file"); 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); // keep all the command line arguments following @responsefile diff --git a/src/m_cond.c b/src/m_cond.c index b7520aba7..8c4e3c0b7 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -284,6 +284,8 @@ void M_SilentUpdateUnlockablesAndEmblems(void) continue; 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 diff --git a/src/m_fixed.h b/src/m_fixed.h index 370633c1f..08bc95884 100644 --- a/src/m_fixed.h +++ b/src/m_fixed.h @@ -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) { - const boolean n = x < 0; - x = abs(x); - while (x >= y) - x -= y; - if (n) - return -x; - else - return x; + return x % y; } /** \brief The FixedSqrt function diff --git a/src/m_menu.c b/src/m_menu.c index 47ca7ccd4..7c635a137 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -312,9 +312,7 @@ menu_t OP_VideoOptionsDef, OP_VideoModeDef, OP_ColorOptionsDef; menu_t OP_OpenGLOptionsDef, OP_OpenGLFogDef, OP_OpenGLColorDef; #endif menu_t OP_SoundOptionsDef; -#ifdef HAVE_MIXERX menu_t OP_SoundAdvancedDef; -#endif //Misc 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[] = { - {IT_STRING|IT_CALL, NULL, "Secrets", M_SecretsMenu, 76}, - {IT_STRING|IT_CALL, NULL, "1 player", M_SinglePlayerMenu, 84}, + {IT_STRING|IT_CALL, NULL, "1 Player", M_SinglePlayerMenu, 76}, #ifndef NONET - {IT_STRING|IT_SUBMENU, NULL, "multiplayer", &MP_MainDef, 92}, + {IT_STRING|IT_SUBMENU, NULL, "Multiplayer", &MP_MainDef, 84}, #else - {IT_STRING|IT_CALL, NULL, "multiplayer", M_StartSplitServerMenu, 92}, + {IT_STRING|IT_CALL, NULL, "Multiplayer", M_StartSplitServerMenu, 84}, #endif - {IT_STRING|IT_CALL, NULL, "options", M_Options, 100}, - {IT_CALL |IT_STRING, NULL, "addons", M_Addons, 108}, - {IT_STRING|IT_CALL, NULL, "quit game", M_QuitSRB2, 116}, + {IT_STRING|IT_CALL, NULL, "Extras", M_SecretsMenu, 92}, + {IT_CALL |IT_STRING, NULL, "Addons", M_Addons, 100}, + {IT_STRING|IT_CALL, NULL, "Options", M_Options, 108}, + {IT_STRING|IT_CALL, NULL, "Quit Game", M_QuitSRB2, 116}, }; typedef enum { - secrets = 0, - singleplr, + singleplr = 0, multiplr, - options, + secrets, addons, + options, quitdoom } main_e; @@ -661,7 +659,7 @@ static menuitem_t SR_PandorasBox[] = // Sky Room Custom Unlocks 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}, // Custom2 {IT_DISABLED, NULL, "", NULL, 0}, // Custom3 @@ -726,19 +724,19 @@ static menuitem_t SR_EmblemHintMenu[] = // Single Player Main static menuitem_t SP_MainMenu[] = { - {IT_CALL | IT_STRING, NULL, "Tutorial", M_StartTutorial, 84}, - {IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 92}, - {IT_SECRET, NULL, "Record Attack", M_TimeAttack, 100}, - {IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 108}, - {IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 116}, + {IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 84}, + {IT_SECRET, NULL, "Record Attack", M_TimeAttack, 92}, + {IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 100}, + {IT_CALL | IT_STRING, NULL, "Tutorial", M_StartTutorial, 108}, + {IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 116} }; enum { - sptutorial, sploadgame, sprecordattack, spnightsmode, + sptutorial, spstatistics }; @@ -1224,32 +1222,33 @@ static menuitem_t OP_VideoOptionsMenu[] = NULL, "HUD Transparency", &cv_translucenthud, 66}, {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, "Local ping display", &cv_showping, 81}, // shows ping next to framerate if we want to. #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 - {IT_HEADER, NULL, "Console", NULL, 90}, - {IT_STRING | IT_CVAR, NULL, "Background color", &cons_backcolor, 96}, - {IT_STRING | IT_CVAR, NULL, "Text Size", &cv_constextsize, 101}, + {IT_HEADER, NULL, "Console", NULL, 95}, + {IT_STRING | IT_CVAR, NULL, "Background color", &cons_backcolor, 101}, + {IT_STRING | IT_CVAR, NULL, "Text Size", &cv_constextsize, 106}, - {IT_HEADER, NULL, "Chat", NULL, 110}, - {IT_STRING | IT_CVAR, NULL, "Chat Mode", &cv_consolechat, 116}, - {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Width", &cv_chatwidth, 121}, - {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Chat Box Height", &cv_chatheight, 126}, - {IT_STRING | IT_CVAR, NULL, "Message Fadeout Time", &cv_chattime, 131}, - {IT_STRING | IT_CVAR, NULL, "Chat Notifications", &cv_chatnotifications, 136}, - {IT_STRING | IT_CVAR, NULL, "Spam Protection", &cv_chatspamprotection, 141}, - {IT_STRING | IT_CVAR, NULL, "Chat background tint", &cv_chatbacktint, 146}, + {IT_HEADER, NULL, "Chat", NULL, 115}, + {IT_STRING | IT_CVAR, NULL, "Chat Mode", &cv_consolechat, 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, 131}, + {IT_STRING | IT_CVAR, NULL, "Message Fadeout Time", &cv_chattime, 136}, + {IT_STRING | IT_CVAR, NULL, "Chat Notifications", &cv_chatnotifications, 141}, + {IT_STRING | IT_CVAR, NULL, "Spam Protection", &cv_chatspamprotection, 146}, + {IT_STRING | IT_CVAR, NULL, "Chat background tint", &cv_chatbacktint, 151}, - {IT_HEADER, NULL, "Level", NULL, 155}, - {IT_STRING | IT_CVAR, NULL, "Draw Distance", &cv_drawdist, 161}, - {IT_STRING | IT_CVAR, NULL, "Weather Draw Dist.", &cv_drawdist_precip, 166}, - {IT_STRING | IT_CVAR, NULL, "NiGHTS Hoop Draw Dist.", &cv_drawdist_nights, 171}, + {IT_HEADER, NULL, "Level", NULL, 160}, + {IT_STRING | IT_CVAR, NULL, "Draw Distance", &cv_drawdist, 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, 176}, - {IT_HEADER, NULL, "Diagnostic", NULL, 180}, - {IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 186}, - {IT_STRING | IT_CVAR, NULL, "Clear Before Redraw", &cv_homremoval, 191}, - {IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 196}, + {IT_HEADER, NULL, "Diagnostic", NULL, 184}, + {IT_STRING | IT_CVAR, NULL, "Show FPS", &cv_ticrate, 190}, + {IT_STRING | IT_CVAR, NULL, "Clear Before Redraw", &cv_homremoval, 195}, + {IT_STRING | IT_CVAR, NULL, "Show \"FOCUS LOST\"", &cv_showfocuslost, 200}, }; static menuitem_t OP_VideoModeMenu[] = @@ -1347,29 +1346,22 @@ static menuitem_t OP_OpenGLColorMenu[] = static menuitem_t OP_SoundOptionsMenu[] = { {IT_HEADER, NULL, "Game Audio", NULL, 0}, - {IT_STRING | IT_CVAR, NULL, "Sound Effects", &cv_gamesounds, 6}, - {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Sound Volume", &cv_soundvolume, 11}, + {IT_STRING | IT_CVAR, NULL, "Sound Effects", &cv_gamesounds, 12}, + {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 | IT_CV_SLIDER, NULL, "Digital Music Volume", &cv_digmusicvolume, 26}, + {IT_STRING | IT_CVAR, NULL, "Digital Music", &cv_gamedigimusic, 42}, + {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 | IT_CV_SLIDER, NULL, "MIDI Music Volume", &cv_midimusicvolume, 41}, + {IT_STRING | IT_CVAR, NULL, "MIDI Music", &cv_gamemidimusic, 72}, + {IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "MIDI Music Volume", &cv_midimusicvolume, 82}, - {IT_HEADER, NULL, "Accessibility", NULL, 50}, - {IT_STRING | IT_CVAR, NULL, "Closed Captioning", &cv_closedcaptioning, 56}, - {IT_STRING | IT_CVAR, NULL, "Reset Music Upon Dying", &cv_resetmusic, 61}, + {IT_HEADER, NULL, "Miscellaneous", NULL, 102}, + {IT_STRING | IT_CVAR, NULL, "Closed Captioning", &cv_closedcaptioning, 114}, + {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_CVAR, NULL, "Play Music if Unfocused", &cv_playmusicifunfocused, 76}, - -#ifdef HAVE_MIXERX - {IT_STRING | IT_SUBMENU, NULL, "Advanced Settings...", &OP_SoundAdvancedDef, 94}, -#endif + {IT_STRING | IT_SUBMENU, NULL, "Advanced Settings...", &OP_SoundAdvancedDef, 144}, }; -#ifdef HAVE_MIXERX - #ifdef HAVE_OPENMPT #define OPENMPT_MENUOFFSET 32 #else @@ -1385,24 +1377,25 @@ static menuitem_t OP_SoundOptionsMenu[] = static menuitem_t OP_SoundAdvancedMenu[] = { #ifdef HAVE_OPENMPT - {IT_HEADER, NULL, "OpenMPT Settings", NULL, 10}, - {IT_STRING | IT_CVAR, NULL, "Instrument Filter", &cv_modfilter, 22}, + {IT_HEADER, NULL, "OpenMPT Settings", NULL, 0}, + {IT_STRING | IT_CVAR, NULL, "Instrument Filter", &cv_modfilter, 12}, #endif #ifdef HAVE_MIXERX - {IT_HEADER, NULL, "MIDI Settings", NULL, OPENMPT_MENUOFFSET+10}, - {IT_STRING | IT_CVAR, NULL, "MIDI Player", &cv_midiplayer, OPENMPT_MENUOFFSET+22}, - {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, "TiMidity++ Config Folder", &cv_miditimiditypath, OPENMPT_MENUOFFSET+61}, + {IT_HEADER, NULL, "MIDI Settings", NULL, OPENMPT_MENUOFFSET}, + {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+24}, + {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "TiMidity++ Config Folder", &cv_miditimiditypath, OPENMPT_MENUOFFSET+51}, #endif - {IT_HEADER, NULL, "Miscellaneous", NULL, OPENMPT_MENUOFFSET+MIXERX_MENUOFFSET+10}, - {IT_STRING | IT_CVAR, NULL, "Let Levels Force Reset Music", &cv_resetmusicbyheader, OPENMPT_MENUOFFSET+MIXERX_MENUOFFSET+22}, + {IT_HEADER, NULL, "Miscellaneous", NULL, OPENMPT_MENUOFFSET+MIXERX_MENUOFFSET}, + {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 MIXERX_MENUOFFSET -#endif static menuitem_t OP_DataOptionsMenu[] = { @@ -1454,7 +1447,7 @@ enum static menuitem_t OP_EraseDataMenu[] = { {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}, }; @@ -1665,7 +1658,7 @@ menu_t SP_MainDef = //CENTERMENUSTYLE(NULL, SP_MainMenu, &MainDef, 72); SP_MainMenu, M_DrawCenteredMenu, BASEVIDWIDTH/2, 72, - 1, // start at "Start Game" on first entry + 0, NULL }; @@ -1991,12 +1984,10 @@ menu_t OP_ColorOptionsDef = 0, NULL }; -menu_t OP_SoundOptionsDef = DEFAULTSCROLLMENUSTYLE( +menu_t OP_SoundOptionsDef = DEFAULTMENUSTYLE( MN_OP_MAIN + (MN_OP_SOUND << 6), "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); -#endif menu_t OP_ServerOptionsDef = DEFAULTSCROLLMENUSTYLE( MN_OP_MAIN + (MN_OP_SERVER << 6), @@ -3363,13 +3354,15 @@ boolean M_Responder(event_t *ev) // void M_Drawer(void) { + boolean wipe = WipeInAction; + if (currentMenu == &MessageDef) menuactive = true; if (menuactive) { // 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); if (currentMenu->drawroutine) @@ -3431,6 +3424,8 @@ void M_StartControlPanel(void) if (!Playing()) { // 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); currentMenu = &MainDef; @@ -3532,6 +3527,7 @@ void M_StartControlPanel(void) void M_EndModeAttackRun(void) { + G_ClearModeAttackRetryFlag(); M_ModeAttackEndGame(0); } @@ -6692,7 +6688,7 @@ static void M_DrawChecklist(void) || !unlockables[i].conditionset || unlockables[i].conditionset > MAXCONDITIONSETS) 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++) { @@ -7175,7 +7171,7 @@ static void M_DrawSoundTest(void) titl = va("%s - ", curplaying->title); } else - titl = "NONE - "; + titl = "None - "; i = V_LevelNameWidth(titl); @@ -7189,7 +7185,7 @@ static void M_DrawSoundTest(void) while (x > y) { x -= i; - V_DrawLevelTitle(x, 24, 0, titl); + V_DrawLevelTitle(x, 22, 0, titl); } if (curplaying) @@ -8338,16 +8334,12 @@ static void M_SetupChoosePlayer(INT32 choice) { INT32 skinnum; UINT8 i; - UINT8 firstvalid = 255; - UINT8 lastvalid = 0; + UINT8 firstvalid = 255, lastvalid = 255; boolean allowed = false; char *and; (void)choice; - if (!(mapheaderinfo[startmap-1] - && (mapheaderinfo[startmap-1]->forcecharacter[0] != '\0' - || (mapheaderinfo[startmap-1]->typeoflevel & TOL_NIGHTS)) // remove this later when everyone gets their own nights sprites, maybe - )) + if (!mapheaderinfo[startmap-1] || mapheaderinfo[startmap-1]->forcecharacter[0] == '\0') { for (i = 0; i < 32; i++) // Handle charsels, availability, and unlocks. { @@ -8357,6 +8349,8 @@ static void M_SetupChoosePlayer(INT32 choice) if (and) { 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)); firstskin[(and - description[i].skinname)] = '\0'; description[i].skinnum[0] = R_SkinAvailable(firstskin); @@ -8385,7 +8379,7 @@ static void M_SetupChoosePlayer(INT32 choice) 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]; spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_CHARSEL]; @@ -8410,17 +8404,16 @@ static void M_SetupChoosePlayer(INT32 choice) } } - if (firstvalid != 255) - { // 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. + if (firstvalid == lastvalid) // We're being forced into a specific character, so might as well just skip it. { - M_ChoosePlayer(-1); + M_ChoosePlayer(firstvalid); 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); /* the menus suck -James */ @@ -8747,7 +8740,7 @@ static void M_ChoosePlayer(INT32 choice) UINT8 skinnum; // skip this if forcecharacter or no characters available - if (choice == -1) + if (choice == 255) { skinnum = botskin = 0; botingame = false; @@ -8859,9 +8852,9 @@ static void M_DrawStatsMaps(int location) M_DrawMapEmblems(mnum+1, 292, y); 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 - V_DrawString(20, y, V_YELLOWMAP, mapheaderinfo[mnum]->lvlttl); + V_DrawString(20, y, V_YELLOWMAP|V_ALLOWLOWERCASE, mapheaderinfo[mnum]->lvlttl); y += 8; @@ -8905,7 +8898,7 @@ static void M_DrawStatsMaps(int location) else 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; @@ -9116,7 +9109,7 @@ void M_DrawTimeAttackMenu(void) // 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]; 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:"); - if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->gotperfect) - 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+64, 73+lsheadingheight/2, V_ALLOWLOWERCASE|((mapvisited[cv_nextmap.value-1] & MV_PERFECT) ? V_YELLOWMAP : 0), beststr); V_DrawRightAlignedString(104+72, 83+lsheadingheight/2, V_ALLOWLOWERCASE, reqrings); } @@ -9395,6 +9385,7 @@ void M_DrawNightsAttackMenu(void) { emblem_t *em; INT32 yHeight; + INT32 xpos; patch_t *PictureOfLevel; lumpnum_t lumpnum; char beststr[40]; @@ -9454,17 +9445,23 @@ void M_DrawNightsAttackMenu(void) { switch (em->type) { - case ET_NGRADE: yHeight = 48; break; - case ET_NTIME: yHeight = 68; break; + case ET_NGRADE: + xpos = 104+38; + yHeight = 48; + break; + case ET_NTIME: + xpos = 104+76; + yHeight = 68; + break; default: goto skipThisOne; } 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)); 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: em = M_GetLevelEmblems(-1); @@ -10905,7 +10902,7 @@ static void M_EraseData(INT32 choice) if (choice == 0) eschoice = M_GetText("Record Attack data"); else if (choice == 1) - eschoice = M_GetText("Secrets data"); + eschoice = M_GetText("Extras data"); else eschoice = M_GetText("ALL game data"); diff --git a/src/m_menu.h b/src/m_menu.h index ec7915cc2..ce7198d75 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -99,7 +99,7 @@ typedef enum MN_OP_SCREENSHOTS, MN_OP_ERASEDATA, - // Secrets + // Extras MN_SR_MAIN, MN_SR_PANDORA, MN_SR_LEVELSELECT, diff --git a/src/m_misc.c b/src/m_misc.c index 20d5f1de4..ca9b3a8e1 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -23,6 +23,8 @@ #include #endif +#include + // Extended map support. #include @@ -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') snprintf(lvlttltext, 48, "%s%s%s", 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) : ""); else snprintf(lvlttltext, 48, "Unknown"); @@ -2441,3 +2443,13 @@ void M_SetupMemcpy(void) M_Memcpy = cpu_cpy; #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"; +} diff --git a/src/m_misc.h b/src/m_misc.h index 7038e3e48..c37865ff3 100644 --- a/src/m_misc.h +++ b/src/m_misc.h @@ -94,6 +94,8 @@ void strcatbf(char *s1, const char *s2, const char *s3); void M_SetupMemcpy(void); +const char *M_FileError(FILE *handle); + // counting bits, for weapon ammo code, usually FUNCMATH UINT8 M_CountBits(UINT32 num, UINT8 size); diff --git a/src/p_enemy.c b/src/p_enemy.c index eedbecaa7..785d19fb4 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -312,6 +312,9 @@ void A_SpawnPterabytes(mobj_t *actor); void A_PterabyteHover(mobj_t *actor); void A_RolloutSpawn(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 @@ -2473,12 +2476,8 @@ void A_VultureBlast(mobj_t *actor) void A_VultureFly(mobj_t *actor) { fixed_t speedmax = 18*FRACUNIT; - angle_t angledif = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) - actor->angle; - fixed_t dx = actor->target->x - actor->x; - 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; + angle_t angledif; + fixed_t dx, dy, dz, dxy, dm; mobj_t *dust; fixed_t momm; @@ -2487,6 +2486,18 @@ void A_VultureFly(mobj_t *actor) return; #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) S_StartSound(actor, actor->info->activesound); @@ -4070,19 +4081,28 @@ bossjustdie: mobj_t *pole = P_SpawnMobj( 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->floorz + 4*FRACUNIT, + mo->tracer->floorz + (256+1)*FRACUNIT, MT_FSGNB); 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->y + P_ReturnThrustY(pole, mo->tracer->angle, FRACUNIT), pole->z + 256*FRACUNIT, MT_FSGNA)); - pole->angle = mo->tracer->angle; - pole->tracer->angle = pole->angle - ANGLE_90; + pole->tracer->flags |= MF_NOCLIPTHING; + 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->momy = P_ReturnThrustY(pole, pole->angle, speed); pole->tracer->momx = pole->momx; pole->tracer->momy = pole->momy; + pole->tracer->tracer->momx = pole->momx; + pole->tracer->tracer->momy = pole->momy; } } else @@ -5121,7 +5141,7 @@ void A_SignPlayer(mobj_t *actor) return; #endif - if (actor->tracer == NULL || locvar1 < -3 || locvar1 >= numskins) + if (actor->tracer == NULL || locvar1 < -3 || locvar1 >= numskins || signcolor >= MAXTRANSLATIONS) return; // if no face overlay, spawn one @@ -5148,26 +5168,9 @@ void A_SignPlayer(mobj_t *actor) if (signcolor) ; else if ((actor->target->player->skincolor == skin->prefcolor) && (skin->prefoppositecolor)) // Set it as the skin's preferred oppositecolor? - { 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. - { signcolor = Color_Opposite[actor->target->player->skincolor - 1][0]; - signframe += (15 - Color_Opposite[actor->target->player->skincolor - 1][1]); - } else signcolor = SKINCOLOR_NONE; } @@ -5188,10 +5191,10 @@ void A_SignPlayer(mobj_t *actor) skinnum = P_RandomKey(skincount); for (skincount = 0; skincount < numskins; skincount++) { - if (skincheck(skincount)) - skinnum++; if (skincount > skinnum) break; + if (skincheck(skincount)) + skinnum++; } } else // otherwise, advance 1 skin @@ -5203,42 +5206,46 @@ void A_SignPlayer(mobj_t *actor) skin = &skins[skinnum]; } else // specific skin - { skin = &skins[locvar1]; - } facecolor = skin->prefcolor; if (signcolor) ; else if (skin->prefoppositecolor) - { signcolor = skin->prefoppositecolor; - } - else - { + else if (facecolor) 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->skin = skin; P_SetMobjState(ov, actor->info->seestate); // S_PLAY_SIGN - actor->tracer->color = signcolor; - actor->tracer->frame = signframe; } else // Eggman face { ov->color = SKINCOLOR_NONE; P_SetMobjState(ov, actor->info->meleestate); // S_EGGMANSIGN - if (signcolor) - actor->tracer->color = signcolor; - else - actor->tracer->color = signcolor = SKINCOLOR_CARBON; - actor->tracer->frame = signframe += (15 - Color_Opposite[Color_Opposite[signcolor - 1][0] - 1][1]); + if (!signcolor) + signcolor = SKINCOLOR_CARBON; } + + 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 @@ -5665,10 +5672,10 @@ void A_MinusPopup(mobj_t *actor) S_StartSound(actor, sfx_s3k82); 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); - rock->momz = 3*FRACUNIT; - P_SetScale(rock, FRACUNIT/3); + P_SetObjectMomZ(rock, 3*FRACUNIT, false); + P_SetScale(rock, rock->scale/3); } P_RadiusAttack(actor, actor, 2*actor->radius, 0); 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. // // 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) { INT32 locvar1 = var1; + INT32 locvar2 = var2; #ifdef HAVE_BLUA 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); 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 + if (actor->eflags & MFE_JUSTHITFLOOR) + S_StartSound(actor, actor->info->painsound); + if (actor->threshold) actor->threshold--; @@ -14588,6 +14611,9 @@ void A_RolloutRock(mobj_t *actor) 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 actor->fuse = 0; 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; } + +// 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); +} diff --git a/src/p_inter.c b/src/p_inter.c index b0a401b10..469cec33e 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -148,15 +148,19 @@ void P_ResetStarposts(void) // boolean P_CanPickupItem(player_t *player, boolean weapon) { - if (player->bot && weapon) + if (!player->mo || player->mo->health <= 0) 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) return false; - if (player->mo && player->mo->health <= 0) - return false; - 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)) ; - 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()) { 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; 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: { mobj_t *mo2; @@ -2741,7 +2756,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget case MT_EGGTRAP: // Time for birdies! Yaaaaaaaay! - target->fuse = TICRATE*2; + target->fuse = TICRATE; break; case MT_MINECART: @@ -2803,13 +2818,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if (flip) momz *= -1; #define makechunk(angtweak, xmov, ymov) \ - chunk = P_SpawnMobj(target->x, target->y, target->z, MT_SPIKE);\ - chunk->eflags |= flip;\ + chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SPIKE);\ P_SetMobjState(chunk, target->info->xdeathstate);\ chunk->health = 0;\ chunk->angle = angtweak;\ - chunk->destscale = scale;\ - P_SetScale(chunk, scale);\ P_UnsetThingPosition(chunk);\ chunk->flags = MF_NOCLIP;\ chunk->x += xmov;\ @@ -2828,14 +2840,10 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if (flip) momz *= -1; - chunk = P_SpawnMobj(target->x, target->y, target->z, MT_SPIKE); - chunk->eflags |= flip; - + chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_SPIKE); P_SetMobjState(chunk, target->info->deathstate); chunk->health = 0; chunk->angle = ang + ANGLE_180; - chunk->destscale = scale; - P_SetScale(chunk, scale); P_UnsetThingPosition(chunk); chunk->flags = MF_NOCLIP; 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); #define makechunk(angtweak, xmov, ymov) \ - chunk = P_SpawnMobj(target->x, target->y, target->z, MT_WALLSPIKE);\ - chunk->eflags |= flip;\ + chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_WALLSPIKE);\ P_SetMobjState(chunk, target->info->xdeathstate);\ chunk->health = 0;\ chunk->angle = target->angle;\ - chunk->destscale = scale;\ - P_SetScale(chunk, scale);\ P_UnsetThingPosition(chunk);\ chunk->flags = MF_NOCLIP;\ 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); - chunk = P_SpawnMobj(target->x, target->y, target->z, MT_WALLSPIKE); - chunk->eflags |= flip; + chunk = P_SpawnMobjFromMobj(target, 0, 0, 0, MT_WALLSPIKE); P_SetMobjState(chunk, target->info->deathstate); chunk->health = 0; chunk->angle = target->angle; - chunk->destscale = scale; - P_SetScale(chunk, scale); P_UnsetThingPosition(chunk); chunk->flags = MF_NOCLIP; 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); S_StartSound(target, sfx_nghurt); +#ifdef ROTSPRITE + player->mo->rollangle = 0; +#endif + if (oldnightstime > 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. 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; } diff --git a/src/p_local.h b/src/p_local.h index 17a1c32d2..646fa70f2 100644 --- a/src/p_local.h +++ b/src/p_local.h @@ -172,6 +172,7 @@ void P_ElementalFire(player_t *player, boolean cropcircle); void P_DoPityCheck(player_t *player); void P_PlayerThink(player_t *player); void P_PlayerAfterThink(player_t *player); +void P_DoPlayerFinish(player_t *player); void P_DoPlayerExit(player_t *player); 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); 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); void P_Attract(mobj_t *source, mobj_t *enemy, boolean nightsgrab); diff --git a/src/p_map.c b/src/p_map.c index bb56a50b1..8220f3818 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -592,7 +592,7 @@ static void P_DoTailsCarry(player_t *sonic, player_t *tails) if (!(tails->pflags & PF_CANCARRY)) return; - if (tails->bot == 1) + if (sonic->pflags & PF_FINISHED) return; 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; momy2 = fang->momy/dist; - pole->tracer->momx = momx1 + (dist-1)*momx2; - pole->tracer->momy = momy1 + (dist-1)*momy2; + pole->tracer->tracer->momx = momx1 + (dist-1)*momx2; + pole->tracer->tracer->momy = momy1 + (dist-1)*momy2; fang->momx = (dist-1)*momx1 + momx2; fang->momy = (dist-1)*momy1 + momy2; #undef dist - P_SetMobjState(pole, pole->info->deathstate); - - P_SetObjectMomZ(pole->tracer, 6*FRACUNIT, false); - pole->tracer->flags &= ~(MF_NOGRAVITY|MF_NOCLIP); - pole->tracer->movedir = ANGLE_67h; - 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(pole->tracer->tracer, 6*FRACUNIT, false); + pole->tracer->tracer->flags &= ~(MF_NOGRAVITY|MF_NOCLIP); + pole->tracer->tracer->movedir = ANGLE_67h; + if ((R_PointToAngle(fang->x - pole->tracer->tracer->x, fang->y - pole->tracer->tracer->y) - pole->angle) > ANGLE_180) + pole->tracer->tracer->movedir = InvAngle(pole->tracer->movedir); P_SetObjectMomZ(fang, 14*FRACUNIT, false); fang->flags |= MF_NOGRAVITY|MF_NOCLIP; 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; - A_Scream(pole->tracer); + A_Scream(pole->tracer->tracer); S_StartSound(fang, sfx_altdi1); + P_SetTarget(&pole->tracer->tracer, NULL); + P_SetMobjState(pole->tracer, pole->info->xdeathstate); P_SetTarget(&pole->tracer, NULL); + P_SetMobjState(pole, pole->info->deathstate); } 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 || thing->type == MT_WALLSPIKE) { - mobjtype_t type = thing->type; + mobj_t *iter; if (thing->flags & MF_SOLID) S_StartSound(tmthing, thing->info->deathsound); - for (thing = thing->subsector->sector->thinglist; thing; thing = thing->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)) - P_KillMobj(thing, tmthing, tmthing, 0); + for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext) + 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(iter, tmthing, tmthing, 0); } else { @@ -822,12 +823,12 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->type == MT_SPIKE || thing->type == MT_WALLSPIKE) { - mobjtype_t type = thing->type; + mobj_t *iter; if (thing->flags & MF_SOLID) S_StartSound(tmthing, thing->info->deathsound); - for (thing = thing->subsector->sector->thinglist; thing; thing = thing->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)) - P_KillMobj(thing, tmthing, tmthing, 0); + for (iter = thing->subsector->sector->thinglist; iter; iter = iter->snext) + 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(iter, tmthing, tmthing, 0); } else { @@ -1020,7 +1021,6 @@ static boolean PIT_CheckThing(mobj_t *thing) if ((thing->flags & MF_PUSHABLE) // not carrying a player && (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something && ((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) && ((!(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)))) @@ -1032,7 +1032,8 @@ static boolean PIT_CheckThing(mobj_t *thing) P_SetPlayerMobjState(tmthing, S_PLAY_WALK); tmthing->player->powers[pw_carry] = CR_ROLLOUT; P_SetTarget(&tmthing->tracer, thing); - P_SetObjectMomZ(thing, tmthing->momz, true); + if (!P_IsObjectOnGround(thing)) + thing->momz += tmthing->momz; return true; } } @@ -1063,6 +1064,7 @@ static boolean PIT_CheckThing(mobj_t *thing) thing->momy = tmthing->momy; tmthing->momx = tempmomx; tmthing->momy = tempmomy; + S_StartSound(thing, thing->info->painsound); } } @@ -1089,7 +1091,7 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; // overhead if (thing->z + thing->height < tmthing->z) return true; // underneath - if (!thing->tracer) + if (!thing->tracer || !thing->tracer->tracer) return true; P_SlapStick(tmthing, thing); // 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); } + // 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 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)) - ; // springs and gas jets should never be able to step up onto a player + if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player)) + ; // springs, gas jets and springs should never be able to step up onto a player // z checking at last // Treat noclip things as non-solid! else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID @@ -3464,7 +3471,7 @@ isblocking: } // see about climbing on the wall - if (!(checkline->flags & ML_NOCLIMB)) + if (!(checkline->flags & ML_NOCLIMB) && checkline->special != HORIZONSPECIAL) { boolean canclimb; angle_t climbangle, climbline; @@ -3751,6 +3758,33 @@ void P_SlideMove(mobj_t *mo) v2.x = tmhitthing->x + cosradius; 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.v2 = &v2; junk.dx = 2*cosradius; // v2.x - v1.x; diff --git a/src/p_mobj.c b/src/p_mobj.c index 5d6a8a10f..6a250a783 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -246,6 +246,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) { case S_PLAY_STND: case S_PLAY_WAIT: + case S_PLAY_NIGHTS_STAND: player->panim = PA_IDLE; break; case S_PLAY_EDGE: @@ -269,6 +270,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) break; case S_PLAY_ROLL: //case S_PLAY_SPINDASH: -- everyone can ROLL thanks to zoom tubes... + case S_PLAY_NIGHTS_ATTACK: player->panim = PA_ROLL; break; case S_PLAY_JUMP: @@ -278,6 +280,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) player->panim = PA_SPRING; break; case S_PLAY_FALL: + case S_PLAY_NIGHTS_FLOAT: player->panim = PA_FALL; break; case S_PLAY_FLY: @@ -3405,7 +3408,7 @@ void P_MobjCheckWater(mobj_t *mobj) // Drown timer setting 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 || (mariomode)) // Or in Mario mode... { @@ -3721,17 +3724,10 @@ void P_DestroyRobots(void) } } -// P_CameraThinker -// -// Process the mobj-ish required functions of the camera -boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled) +// 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) { - boolean itsatwodlevel = false; 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) postimg = postimg_flip; @@ -3759,13 +3755,27 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled postimg = postimg_heat; } - if (postimg != postimg_none) - { - if (splitscreen && player == &players[secondarydisplayplayer]) - postimgtype2 = postimg; - else - postimgtype = postimg; - } + if (postimg == postimg_none) + return; + + if (splitscreen && player == &players[secondarydisplayplayer]) + postimgtype2 = 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) { @@ -4535,23 +4545,29 @@ static void P_Boss3Thinker(mobj_t *mobj) } 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. 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 mobj->movecount--; // limited number of shots before diving again if (mobj->movecount) 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 { @@ -4666,13 +4682,10 @@ static void P_Boss3Thinker(mobj_t *mobj) S_StartSound(mobj, shock->info->seesound); // look for a new target - P_BossTargetPlayer(mobj, false); + P_BossTargetPlayer(mobj, true); if (mobj->target && mobj->target->player) - { - A_FaceTarget(mobj); P_SetMobjState(mobj, mobj->info->missilestate); - } } 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); P_SetMobjState(seg, (statenum_t)(S_MINECARTSEG_FRONT + i)); if (i >= 2) - seg->extravalue1 = (i == 2) ? -18 : 18; + seg->extravalue1 = (i == 2) ? -20 : 20; else { seg->extravalue2 = (i == 0) ? 24 : -24; @@ -8227,7 +8240,7 @@ void P_MobjThinker(mobj_t *mobj) mobj->flags2 ^= MF2_DONTDRAW; break; 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; fixed_t x,y,z; @@ -8236,9 +8249,9 @@ void P_MobjThinker(mobj_t *mobj) mobj_t *flicky; z = mobj->subsector->sector->floorheight + FRACUNIT + (P_RandomKey(64)<x + FixedMul(FINESINE(fa),ns); y = mobj->y + FixedMul(FINECOSINE(fa),ns); @@ -8690,6 +8703,13 @@ void P_MobjThinker(mobj_t *mobj) case MT_KOOPA: P_KoopaThinker(mobj); 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: if (((mobj->z < mobj->floorz) || (mobj->z + mobj->height > mobj->ceilingz)) && mobj->flags & MF_MISSILE) @@ -9622,6 +9642,90 @@ void P_MobjThinker(mobj_t *mobj) } 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: if (mobj->flags & MF_NOGRAVITY) { @@ -11207,7 +11311,7 @@ void P_SpawnPlayer(INT32 playernum) mobj->radius = FixedMul(skins[p->skin].radius, mobj->scale); 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) { diff --git a/src/p_setup.c b/src/p_setup.c index 461c81b05..2216334e7 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2704,6 +2704,7 @@ boolean P_SetupLevel(boolean skipprecip) // Cancel all d_main.c fadeouts (keep fade in though). wipegamestate = FORCEWIPEOFF; + wipestyleflags = 0; // Special stage fade to white // 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) 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_RunWipe(wipedefs[wipe_speclevel_towhite], false); + I_OsPolling(); + I_FinishUpdate(); // page flip or blit buffer + if (moviemode) + M_SaveFrame(); + nowtime = lastwipetic; // Hold on white for extra effect. @@ -2745,6 +2757,13 @@ boolean P_SetupLevel(boolean skipprecip) ranspecialwipe = 1; } + if (G_GetModeAttackRetryFlag()) + { + if (modeattacking) + wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE); + G_ClearModeAttackRetryFlag(); + } + // Make sure all sounds are stopped before Z_FreeTags. S_StopSounds(); S_ClearSfx(); @@ -2762,7 +2781,13 @@ boolean P_SetupLevel(boolean skipprecip) if (rendermode != render_none && !ranspecialwipe) { 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(); // for titlemap: run a specific wipe if specified @@ -2787,12 +2812,12 @@ boolean P_SetupLevel(boolean skipprecip) { // Don't include these in the fade! 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", mapheaderinfo[gamemap-1]->lvlttl, - (mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE", - (mapheaderinfo[gamemap-1]->actnum > 0) ? va(", Act %d",mapheaderinfo[gamemap-1]->actnum) : ""); - V_DrawSmallString(1, 195, V_ALLOWLOWERCASE, tx); + (mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " Zone", + (mapheaderinfo[gamemap-1]->actnum > 0) ? va(" %d",mapheaderinfo[gamemap-1]->actnum) : ""); + V_DrawSmallString(1, 195, V_ALLOWLOWERCASE|V_TRANSLUCENT|V_SNAPTOLEFT|V_SNAPTOBOTTOM, tx); I_UpdateNoVsync(); } @@ -3177,7 +3202,7 @@ boolean P_SetupLevel(boolean skipprecip) // Remove the loading shit from the screen if (rendermode != render_none && !titlemapinaction) - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); + F_WipeColorFill(levelfadecol); if (precache || dedicated) R_PrecacheLevel(); @@ -3226,44 +3251,22 @@ boolean P_SetupLevel(boolean skipprecip) #endif } - // Stage title! - if (rendermode != render_none - && (!titlemapinaction) - && 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; + // No render mode, stop here. + if (rendermode == render_none) + return true; - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, levelfadecol); - stplyr = &players[consoleplayer]; - ST_drawLevelTitle(nowtime - starttime); - if (splitscreen) - { - stplyr = &players[secondarydisplayplayer]; - ST_drawLevelTitle(nowtime - starttime); - } + // Title card! + G_StartTitleCard(); - I_OsPolling(); - I_UpdateNoBlit(); - I_FinishUpdate(); // page flip or blit buffer + // Can the title card actually run, though? + if (!WipeStageTitle) + return true; + if (ranspecialwipe == 2) + return true; - if (moviemode) // make sure we save frames for the white hold too - M_SaveFrame(); - } - } + // If so... + if ((!(mapheaderinfo[gamemap-1]->levelflags & LF_NOTITLECARD)) && (*mapheaderinfo[gamemap-1]->lvlttl != '\0')) + G_PreLevelTitleCard(lt_ticker, true); return true; } diff --git a/src/p_spec.c b/src/p_spec.c index 355f5e728..605638bd3 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -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 { + // Play sounds from tagged sectors' origins. if (line->flags & ML_EFFECT5) // Repeat Midtexture { // 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); } } - 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; 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; - if (mo->z > P_GetSpecialTopZ(mo, sectors + rover->secnum, mo->subsector->sector)) - continue; + if (foundit || (camobj->subsector->sector->tag == line->tag)) + { + foundit = true; + break; + } - if (mo->z + mo->height < P_GetSpecialBottomZ(mo, sectors + rover->secnum, mo->subsector->sector)) - continue; + // Only trigger if mobj is touching the tag + 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) - foundit = true; - - if (!foundit) - return; + if (foundit) + S_StartSound(NULL, sfxnum); } } 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 /// \todo check continues for proper splitscreen support? 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) - ; - else if (sector->flags & SF_TRIGGERSPECIAL_TOUCH) + msecnode_t *node; + for (node = players[i].mo->touching_sectorlist; node; node = node->m_sectorlist_next) { - boolean insector = false; - msecnode_t *node; - 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; + if (P_ThingIsOnThe3DFloor(players[i].mo, sector, node->m_sector)) + break; } - else + if (!node) 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; } else - { - 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; + goto DoneSection2; - if (special == 3 && !P_MobjReadyToTrigger(players[i].mo, sector)) - goto DoneSection2; - } + if (special == 3 && !P_MobjReadyToTrigger(players[i].mo, sector)) + goto DoneSection2; } + } /* FALLTHRU */ case 4: // Linedef executor that doesn't require touching floor case 5: // Linedef executor @@ -4675,7 +4686,7 @@ DoneSection2: { INT32 lineindex; - P_DoPlayerExit(player); + P_DoPlayerFinish(player); P_SetupSignExit(player); // important: use sector->tag on next line instead of player->mo->subsector->tag diff --git a/src/p_tick.c b/src/p_tick.c index e02b11f49..237d6b593 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -481,6 +481,9 @@ static inline void P_DoSpecialStageStuff(void) tic_t oldnightstime = players[i].nightstime; countspheres += players[i].spheres; + if (!oldnightstime) + continue; + // If in water, deplete timer 6x as fast. if (players[i].mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER) && !(players[i].powers[pw_shield] & SH_PROTECTWATER)) players[i].nightstime -= 5; @@ -506,12 +509,11 @@ static inline void P_DoSpecialStageStuff(void) { // Halt all the players 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].exiting = (14*TICRATE)/5 + 1; } - sstimer = 0; P_GiveEmerald(true); P_RestoreMusic(&players[consoleplayer]); diff --git a/src/p_user.c b/src/p_user.c index 3c481e7e2..37f1a5ec5 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -343,13 +343,15 @@ void P_GiveEmerald(boolean spawnObj) continue; 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_SetMobjState(emmo, mobjinfo[MT_GOTEMERALD].meleestate + em); P_SetTarget(&players[i].mo->tracer, emmo); if (pnum == 255) { - i = pnum; + pnum = i; continue; } @@ -636,6 +638,10 @@ static void P_DeNightserizePlayer(player_t *player) player->marebonuslap = 0; player->flyangle = 0; player->anotherflyangle = 0; +#ifdef ROTSPRITE + player->mo->rollangle = 0; +#endif + P_SetTarget(&player->mo->target, 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->flyangle = 0; player->anotherflyangle = 0; +#ifdef ROTSPRITE + player->mo->rollangle = 0; +#endif player->powers[pw_shield] = SH_NONE; 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) { + UINT8 prevlives = player->lives; if (!player) return; @@ -1232,10 +1242,9 @@ void P_GivePlayerLives(player_t *player, INT32 numlives) if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) { - UINT8 prevlives = player->lives; P_GivePlayerRings(player, 100*numlives); if (player->lives - prevlives >= numlives) - return; + goto docooprespawn; numlives = (numlives + prevlives - player->lives); } @@ -1249,6 +1258,15 @@ void P_GivePlayerLives(player_t *player, INT32 numlives) player->lives = 99; else if (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) @@ -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 } +/** 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 // @@ -2158,12 +2204,14 @@ void P_DoPlayerExit(player_t *player) player->pflags |= P_GetJumpFlags(player); 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_spacetime] = 0; 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 @@ -2263,7 +2311,7 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) else if (!player->skidtime) 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) { @@ -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) { + 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->charflags & SF_DASHMODE && player->dashmode >= DASHMODE_THRESHOLD && player->panim != PA_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)) P_SetPlayerMobjState(player->mo, S_PLAY_RUN); 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) 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)) P_SetPlayerMobjState(player->mo, S_PLAY_RUN); 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]; - if (player->exiting) + if (player->exiting || (player->pflags & PF_FINISHED)) 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 @@ -3478,7 +3538,7 @@ static void P_DoClimbing(player_t *player) { P_SetObjectMomZ(player->mo, 2*FRACUNIT, true); if (cmd->forwardmove) - P_SetObjectMomZ(player->mo, 2*player->mo->momz/3, false); + player->mo->momz = 2*player->mo->momz/3; } if (thrust) 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))) 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; 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); + P_SetWeaponDelay(player, TICRATE); // Short delay between fireballs so you can't spam them everywhere return; } @@ -4017,8 +4079,8 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd) mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING); - if (mo) - mo->fuse = 3*TICRATE; // Bounce Ring time + if (mo) + mo->fuse = 3*TICRATE; // Bounce Ring time } // Rail ring 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) { - 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->mo->tracer->flags |= MF_PUSHABLE; P_SetTarget(&player->mo->tracer->tracer, NULL); @@ -4562,6 +4628,13 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) // Revving 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) { #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(); } } - // If not moving up or down, and travelling faster than a speed of five while not holding // down the spin button and not spinning. // 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 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))) - 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 { 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->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; case CA_GLIDEANDCLIMB: @@ -5496,7 +5571,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) else potentialmomz = ((player->speed < 10*player->mo->scale) ? (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) player->mo->momz = P_MobjFlip(player->mo)*potentialmomz; player->pflags &= ~PF_SPINNING; @@ -5891,6 +5966,8 @@ static void P_3dMovement(player_t *player) // When sliding, don't allow forward/back if (player->pflags & PF_SLIDING) cmd->forwardmove = 0; + else if (onground && player->mo->state == states+S_PLAY_PAIN) + P_SetPlayerMobjState(player->mo, S_PLAY_WALK); player->aiming = cmd->aiming<mo->floorz; + if (cropcircle) + ground += P_MobjFlip(player->mo); + if (cropcircle) { #define numangles 8 @@ -7882,6 +7962,11 @@ static void P_MovePlayer(player_t *player) cmd = &player->cmd; 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.) runspd = FixedMul(runspd, player->mo->movefactor); @@ -8364,7 +8449,7 @@ static void P_MovePlayer(player_t *player) // Tails Put-Put noise if (player->charability == CA_FLY - && player->bot != 1 + && (player->pflags & PF_CANCARRY) && !(player->mo->eflags & MFE_UNDERWATER) && leveltime % 10 == 0 && !player->spectator) @@ -9420,7 +9505,6 @@ static void P_DeathThink(player_t *player) } else if ((netgame || multiplayer) && player->deadtimer >= 8*TICRATE) { - INT32 i, deadtimercheck = INT32_MAX; // 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; - if (player->exiting && mo->target && mo->target->type == MT_SIGN) - sign = mo->target; + if (player->playerstate == PST_REBORN) + { + 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!! @@ -9671,7 +9772,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall camorbit = (!stricmp(cv_cam_orbit.defaultvalue, "off")) ? false : true; camrotate = atoi(cv_cam_rotate.defaultvalue); 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) { @@ -9680,7 +9781,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall camorbit = cv_cam_orbit.value; camrotate = cv_cam_rotate.value; 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 { @@ -9689,9 +9790,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall camorbit = cv_cam2_orbit.value; camrotate = cv_cam2_rotate.value; 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 if (P_AnalogMove(player) && (player->cmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) == (BT_CAMLEFT|BT_CAMRIGHT)) { camstill = true; @@ -9802,9 +9906,10 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall dist <<= 1; } + if (!(twodlevel || (mo->flags2 & MF2_TWOD)) && !(player->powers[pw_carry] == CR_NIGHTSMODE)) + dist = FixedMul(dist, player->camerascale); - - checkdist = (dist = FixedMul(dist, player->camerascale)); + checkdist = dist; if (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); - if (mo->eflags & MFE_VERTICALFLIP) - z = mo->z + mo->height - pviewheight - camheight + distz; + if (sign) + { + if (mo->eflags & MFE_VERTICALFLIP) + z = sign->ceilingz - pviewheight - camheight; + else + z = sign->floorz + pviewheight + camheight; + } 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 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) 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)) thiscam->angle = angle; @@ -10166,9 +10270,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall dist = FixedHypot(f1, f2); 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 - 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) 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; } +// 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) { 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 P_MobjCheckWater(player->mo); @@ -12276,7 +12389,7 @@ void P_PlayerAfterThink(player_t *player) player->mo->momz = tails->momz; } - if (gametype == GT_COOP) + if (gametype == GT_COOP && (!tails->player || tails->player->bot != 1)) { player->mo->angle = tails->angle; diff --git a/src/r_data.c b/src/r_data.c index d9d8a8f30..574c4e194 100644 --- a/src/r_data.c +++ b/src/r_data.c @@ -24,6 +24,7 @@ #include "z_zone.h" #include "p_setup.h" // levelflats #include "v_video.h" // pMasterPalette +#include "f_finale.h" // wipes #include "byteptr.h" #include "dehacked.h" @@ -113,6 +114,7 @@ INT32 *texturetranslation; sprcache_t *spritecachedinfo; lighttable_t *colormaps; +lighttable_t *fadecolormap; // for debugging/info purposes size_t flatmemory, spritememory, texturememory; @@ -1455,18 +1457,111 @@ static void R_InitSpriteLumps(void) 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 // static void R_InitColormaps(void) { + size_t len; lumpnum_t lump; // Load in the light tables 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); + // Make colormap for fades + R_CreateFadeColormaps(); + // Init Boom colormaps. R_ClearColormaps(); #ifdef EXTRACOLORMAPLUMPS @@ -1495,6 +1590,9 @@ void R_ReInitColormaps(UINT16 num) } W_ReadLumpHeader(lump, colormaps, W_LumpLength(basecolormaplump), 0U); + if (fadecolormap) + Z_Free(fadecolormap); + R_CreateFadeColormaps(); // Init Boom colormaps. R_ClearColormaps(); diff --git a/src/r_defs.h b/src/r_defs.h index 4d41f4f70..6d0b10b70 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -410,6 +410,8 @@ typedef enum ST_NEGATIVE } slopetype_t; +#define HORIZONSPECIAL 41 + typedef struct line_s { // Vertices, from v1 to v2. diff --git a/src/r_main.c b/src/r_main.c index 3ed509af5..5e758e52a 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -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_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}; +#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}; +#endif //#ifdef GLBADSHADOWS 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_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_chasecam2); +#if defined(FLOORSPLATS) || defined(GLBADSHADOWS) CV_RegisterVar(&cv_shadow); +#endif //#if defined(FLOORSPLATS) || defined(GLBADSHADOWS) +#ifdef GLBADSHADOWS CV_RegisterVar(&cv_shadowoffs); +#endif //#ifdef GLBADSHADOWS CV_RegisterVar(&cv_skybox); CV_RegisterVar(&cv_cam_dist); diff --git a/src/r_main.h b/src/r_main.h index 2c9b5cc3d..ae74ee067 100644 --- a/src/r_main.h +++ b/src/r_main.h @@ -76,7 +76,12 @@ extern consvar_t cv_showhud, cv_translucenthud; extern consvar_t cv_homremoval; extern consvar_t cv_chasecam, cv_chasecam2; 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_drawdist, cv_drawdist_nights, cv_drawdist_precip; extern consvar_t cv_skybox; diff --git a/src/r_plane.c b/src/r_plane.c index a3075c9d8..f21f49101 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -1007,6 +1007,8 @@ void R_DrawSinglePlane(visplane_t *pl) R_CheckFlatLength(W_LumpLength(levelflat->u.flat.lumpnum)); // Raw flats always have dimensions that are powers-of-two numbers. ds_powersoftwo = true; + if (spanfunc == basespanfunc) + spanfunc = mmxspanfunc; break; default: switch (type) diff --git a/src/r_segs.c b/src/r_segs.c index 6eb81ce7a..ee62bfc73 100644 --- a/src/r_segs.c +++ b/src/r_segs.c @@ -2694,7 +2694,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) worldbottomslope >>= 4; #endif - if (linedef->special == 41) { // HORIZON LINES + if (linedef->special == HORIZONSPECIAL) { // HORIZON LINES topstep = bottomstep = 0; topfrac = bottomfrac = (centeryfrac>>4); topfrac++; // Prevent 1px HOM @@ -2825,7 +2825,7 @@ void R_StoreWallRange(INT32 start, INT32 stop) #ifdef ESLOPE ffloor[i].f_pos_slope >>= 4; #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_frac = (centeryfrac>>4); diff --git a/src/r_state.h b/src/r_state.h index da9425bdf..977384926 100644 --- a/src/r_state.h +++ b/src/r_state.h @@ -38,6 +38,7 @@ typedef struct extern sprcache_t *spritecachedinfo; extern lighttable_t *colormaps; +extern lighttable_t *fadecolormap; // Boom colormaps. extern extracolormap_t *extra_colormaps; diff --git a/src/r_things.c b/src/r_things.c index ae3c47db4..9763648cd 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1137,8 +1137,6 @@ static void R_ProjectSprite(mobj_t *thing) UINT32 rollangle = AngleFixed(arollangle)>>FRACBITS; #endif - fixed_t ang_scale = FRACUNIT; - // transform the origin point tr_x = thing->x - viewx; tr_y = thing->y - viewy; @@ -1196,20 +1194,20 @@ static void R_ProjectSprite(mobj_t *thing) #ifdef ROTSPRITE sprinfo = NULL; #endif - } - 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) + if (rot >= sprdef->numframes) { - thing->state->sprite = thing->sprite; - thing->state->frame = thing->frame; + CONS_Alert(CONS_ERROR, M_GetText("R_ProjectSprite: invalid sprite frame %s/%s for %s\n"), + 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) { 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) @@ -1286,24 +1282,11 @@ static void R_ProjectSprite(mobj_t *thing) else offset = -spr_offset; 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); - tx += FixedMul(offset2, ang_scale); - x2 = ((centerxfrac + FixedMul (tx,xscale)) >> FRACBITS) - (papersprite ? 2 : 1); - - // off the left side - if (x2 < 0) - return; if (papersprite) { - fixed_t yscale2, cosmul, sinmul, tz2; + fixed_t xscale2, yscale2, cosmul, sinmul, tz2; INT32 range; if (ang >= ANGLE_180) @@ -1323,6 +1306,16 @@ static void R_ProjectSprite(mobj_t *thing) yscale = FixedDiv(projectiony, tz); 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_y += FixedMul(offset2, sinmul); gxt = FixedMul(tr_x, viewcos); @@ -1331,15 +1324,25 @@ static void R_ProjectSprite(mobj_t *thing) yscale2 = FixedDiv(projectiony, tz2); 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 return; - if (x2 > x1) - range = (x2 - x1); - else + if ((range = x2 - x1) <= 0) range = 1; - scalestep = (yscale2 - yscale)/range ?: 1; + scalestep = (yscale2 - yscale)/range; + scalestep = scalestep ? scalestep : 1; + xscale = FixedDiv(range<>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) { diff --git a/src/s_sound.c b/src/s_sound.c index ef673c9af..2f88349f1 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -860,7 +860,6 @@ static INT32 actualmidimusicvolume; void S_UpdateSounds(void) { INT32 audible, cnum, volume, sep, pitch; - UINT8 i; channel_t *c; listener_t listener; @@ -1017,28 +1016,30 @@ void S_UpdateSounds(void) notinlevel: 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()); - for (i = 0; i < NUMCAPTIONS; i++) // update captions + if (!closedcaptions[i].s) + continue; + + if (i == 0 && (closedcaptions[0].s-S_sfx == sfx_None) && gamestopped) + continue; + + if (!(--closedcaptions[i].t)) { - if (!closedcaptions[i].s) - continue; - - if (i == 0 && (closedcaptions[0].s-S_sfx == sfx_None) && gamestopped) - continue; - - if (!(--closedcaptions[i].t)) - { - 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; - } + 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; if (def->soundtestcond > 0 && !(mapvisited[def->soundtestcond-1] & MV_BEATEN)) continue; - if (def->soundtestcond < 0 && !M_Achieved(1-def->soundtestcond)) + if (def->soundtestcond < 0 && !M_Achieved(-1-def->soundtestcond)) continue; def->allowed = true; } diff --git a/src/s_sound.h b/src/s_sound.h index d1551df0b..f9bbf6767 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -303,6 +303,7 @@ boolean S_FadeOutStopMusic(UINT32 ms); // Updates music & sounds // 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); diff --git a/src/screen.c b/src/screen.c index f1d91de66..5005118b6 100644 --- a/src/screen.c +++ b/src/screen.c @@ -421,13 +421,24 @@ void SCR_DisplayTicRate(void) else if (totaltics == TICRATE) ticcntcolor = V_GREENMAP; 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, - ticcntcolor|V_NOSCALESTART|V_HUDTRANS, va("%02d/%02u", totaltics, TICRATE)); + ticcntcolor|V_NOSCALESTART|V_USERHUDTRANS, va("%02d/%02u", totaltics, TICRATE)); 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) { UINT8 i; diff --git a/src/screen.h b/src/screen.h index 79f21e8e4..6f03612c4 100644 --- a/src/screen.h +++ b/src/screen.h @@ -167,5 +167,6 @@ FUNCMATH boolean SCR_IsAspectCorrect(INT32 width, INT32 height); // move out to main code for consistency void SCR_DisplayTicRate(void); void SCR_ClosedCaptions(void); +void SCR_DisplayLocalPing(void); #undef DNWH #endif //__SCREEN_H__ diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index fb0f4b2ba..95ddab3cc 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1176,11 +1176,11 @@ void I_FinishUpdate(void) if (cv_closedcaptioning.value) 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]) { diff --git a/src/sounds.c b/src/sounds.c index b067903b1..596ed74f2 100644 --- a/src/sounds.c +++ b/src/sounds.c @@ -92,7 +92,7 @@ sfxinfo_t S_sfx[NUMSFX] = {"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 {"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"}, {"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"}, {"shrpgo", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Launch"}, {"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"}, {"corkp", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, "Cork fired"}, {"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, ""}, {"kc4a", 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"}, {"kc4e", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, {"kc4f", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR, ""}, diff --git a/src/st_stuff.c b/src/st_stuff.c index 3fbee27e4..8b3ceac9d 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -23,6 +23,7 @@ #include "v_video.h" #include "z_zone.h" #include "hu_stuff.h" +#include "console.h" #include "s_sound.h" #include "i_system.h" #include "m_menu.h" @@ -186,14 +187,18 @@ boolean ST_SameTeam(player_t *a, player_t *b) static boolean st_stopped = true; -void ST_Ticker(void) +void ST_Ticker(boolean run) { if (st_stopped) return; + + if (run) + ST_runTitleCard(); } // 0 is default, any others are special palettes. INT32 st_palette = 0; +INT32 st_translucency = 10; void ST_doPaletteStuff(void) { @@ -348,12 +353,12 @@ void ST_LoadGraphics(void) // made separate so that skins code can reload custom face graphics 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]; spriteframe_t *sprframe = &sprdef->spriteframes[XTRA_LIFEPIC]; 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]; sprframe = &sprdef->spriteframes[0]; @@ -818,7 +823,7 @@ static void ST_drawLivesArea(void) face = superprefix[stplyr->skin]; V_DrawSmallMappedPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y, 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; 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!!"); } -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 *subttl = mapheaderinfo[gamemap-1]->subttl; INT32 actnum = mapheaderinfo[gamemap-1]->actnum; - INT32 lvlttly, zoney, lvlttlxpos, ttlnumxpos, zonexpos; + INT32 lvlttlxpos, ttlnumxpos, zonexpos; 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; +#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)); @@ -1177,72 +1308,71 @@ void ST_drawLevelTitle(tic_t titletime) lvlttlxpos -= V_LevelActNumWidth(actnum); ttlnumxpos = lvlttlxpos + V_LevelNameWidth(lvlttl); - zonexpos = ttlnumxpos - V_LevelNameWidth(M_GetText("ZONE")); + zonexpos = ttlnumxpos - V_LevelNameWidth(M_GetText("Zone")); ttlnumxpos++; if (lvlttlxpos < 0) lvlttlxpos = 0; -#if 0 // toaster's experiment. srb2&toast.exe one day, maybe? Requires stuff below to be converted to fixed point. -#define MIDTTLY 79 -#define MIDZONEY 105 -#define MIDDIFF 4 - - if (titletime < 10) + if (!splitscreen || (splitscreen && stplyr == &players[displayplayer])) { - fixed_t z = ((titletime - 3)<height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag); + V_DrawScaledPatch(FixedInt(lt_zigzag), (zigzag->height-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag); + 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)<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) - V_DrawCenteredString(subttlxpos, lvlttly+48, V_PERPLAYER|V_ALLOWLOWERCASE, subttl); + lt_lasttic = lt_ticker; + +#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) @@ -2106,7 +2236,7 @@ static void ST_drawTextHUD(void) 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); if (numneeded) @@ -2121,7 +2251,7 @@ static void ST_drawTextHUD(void) continue; total++; - if (players[i].exiting) + if (players[i].exiting || (players[i].pflags & PF_FINISHED)) exiting++; } @@ -2394,12 +2524,25 @@ static void ST_doItemFinderIconsAndSound(void) S_StartSound(NULL, sfx_emfind); } +// // Draw the status bar overlay, customisable: the user chooses which // kind of information to overlay // 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 ((maptol & TOL_NIGHTS || G_IsSpecialStage(gamemap)) && @@ -2550,12 +2693,8 @@ static void ST_overlayDrawer(void) #endif // draw level title Tails - if (*mapheaderinfo[gamemap-1]->lvlttl != '\0' && !(hu_showscores && (netgame || multiplayer)) -#ifdef HAVE_BLUA - && LUA_HudEnabled(hud_stagetitle) -#endif - ) - ST_drawLevelTitle(timeinmap+70); + if (stagetitle && (!WipeInAction) && (!WipeStageTitle)) + ST_drawTitleCard(); if (!hu_showscores && (netgame || multiplayer) #ifdef HAVE_BLUA @@ -2631,6 +2770,8 @@ void ST_Drawer(void) } } + st_translucency = cv_translucenthud.value; + if (st_overlay) { // No deadview! diff --git a/src/st_stuff.h b/src/st_stuff.h index aaf01ca15..33ffa957a 100644 --- a/src/st_stuff.h +++ b/src/st_stuff.h @@ -24,7 +24,7 @@ // // Called by main loop. -void ST_Ticker(void); +void ST_Ticker(boolean run); // Called by main loop. void ST_Drawer(void); @@ -47,8 +47,16 @@ void ST_ReloadSkinFaceGraphics(void); void ST_doPaletteStuff(void); -// level title draw -void ST_drawLevelTitle(tic_t titletime); +// title card +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 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 INT32 st_palette; // 0 is default, any others are special palettes. +extern INT32 st_translucency; extern lumpnum_t st_borderpatchnum; // patches, also used in intermission diff --git a/src/strcasestr.c b/src/strcasestr.c index 2077dc3ff..86c7ec5b0 100644 --- a/src/strcasestr.c +++ b/src/strcasestr.c @@ -75,7 +75,7 @@ strcasestr (const char *s, const char *q) if (!( (intptr_t)up|(intptr_t)lp )) return 0; - if (!lp || up < lp) + if (!lp || ( up && up < lp )) { ppa = &up; ppb = &lp; diff --git a/src/v_video.c b/src/v_video.c index 44e80c9f5..5813a451b 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -18,7 +18,9 @@ #include "p_local.h" // stplyr #include "g_game.h" // players #include "v_video.h" +#include "st_stuff.h" #include "hu_stuff.h" +#include "f_finale.h" #include "r_draw.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 == 13) - alphalevel = hudminusalpha[cv_translucenthud.value]; + alphalevel = hudminusalpha[st_translucency]; else if (alphalevel == 14) - alphalevel = 10 - cv_translucenthud.value; + alphalevel = 10 - st_translucency; else if (alphalevel == 15) - alphalevel = hudplusalpha[cv_translucenthud.value]; + alphalevel = hudplusalpha[st_translucency]; if (alphalevel >= 10) 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 == 13) - alphalevel = hudminusalpha[cv_translucenthud.value]; + alphalevel = hudminusalpha[st_translucency]; else if (alphalevel == 14) - alphalevel = 10 - cv_translucenthud.value; + alphalevel = 10 - st_translucency; else if (alphalevel == 15) - alphalevel = hudplusalpha[cv_translucenthud.value]; + alphalevel = hudplusalpha[st_translucency]; if (alphalevel >= 10) 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) { - 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]; 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 == 13) - alphalevel = hudminusalpha[cv_translucenthud.value]; + alphalevel = hudminusalpha[st_translucency]; else if (alphalevel == 14) - alphalevel = 10 - cv_translucenthud.value; + alphalevel = 10 - st_translucency; else if (alphalevel == 15) - alphalevel = hudplusalpha[cv_translucenthud.value]; + alphalevel = hudplusalpha[st_translucency]; if (alphalevel >= 10) return; // invis @@ -1860,7 +1862,9 @@ void V_DrawFadeScreen(UINT16 color, UINT8 strength) { 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)<= 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); return; } + boxheight *= vid.dupy; + if (color == INT32_MAX) 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, // just the start and stop positions 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) *buf = promptbgmap[*buf]; } @@ -2899,7 +2904,7 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string) continue; } - c = toupper(*ch) - LT_FONTSTART; + c = *ch - LT_FONTSTART; if (c < 0 || c >= LT_FONTSIZE || !lt_font[c]) { cx += 16*dupx; @@ -2934,7 +2939,7 @@ INT32 V_LevelNameWidth(const char *string) { if (string[i] & 0x80) continue; - c = toupper(string[i]) - LT_FONTSTART; + c = string[i] - LT_FONTSTART; if (c < 0 || c >= LT_FONTSIZE || !lt_font[c]) w += 16; else @@ -2953,7 +2958,7 @@ INT32 V_LevelNameHeight(const char *string) 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]) continue; @@ -2964,7 +2969,7 @@ INT32 V_LevelNameHeight(const char *string) return w; } -// For ST_drawLevelTitle +// For ST_drawTitleCard // Returns the width of the act num patch INT32 V_LevelActNumWidth(INT32 num) { diff --git a/src/v_video.h b/src/v_video.h index cd32ac5f8..e3dbb75dc 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -106,6 +106,10 @@ extern RGBA_t *pMasterPalette; #define V_HUDTRANSHALF 0x000D0000 #define V_HUDTRANS 0x000E0000 // draw the hud translucent #define V_HUDTRANSDOUBLE 0x000F0000 +// Macros follow +#define V_USERHUDTRANSHALF ((10-(cv_translucenthud.value/2))<namelen + 1); 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); free(zentries); free(fullname); diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c index 11c7a6744..39a60cd93 100644 --- a/src/win32/win_vid.c +++ b/src/win32/win_vid.c @@ -371,6 +371,9 @@ void I_FinishUpdate(void) if (cv_ticrate.value) SCR_DisplayTicRate(); + if (cv_showping.value && netgame && consoleplayer != serverplayer) + SCR_DisplayLocalPing(); + // if (bDIBMode) { diff --git a/src/y_inter.c b/src/y_inter.c index 21e4bc56e..32548d263 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -166,13 +166,11 @@ static INT32 endtic = -1; intertype_t intertype = int_none; static void Y_RescaleScreenBuffer(void); -static void Y_CleanupScreenBuffer(void); static void Y_AwardCoopBonuses(void); static void Y_AwardSpecialStageBonus(void); static void Y_CalculateCompetitionWinners(void); static void Y_CalculateTimeRaceWinners(void); static void Y_CalculateMatchWinners(void); -static void Y_FollowIntermission(void); static void Y_UnloadData(void); // Stuff copy+pasted from st_stuff.c @@ -293,7 +291,7 @@ static void Y_RescaleScreenBuffer(void) // // Free all related memory. // -static void Y_CleanupScreenBuffer(void) +void Y_CleanupScreenBuffer(void) { // Who knows? if (y_buffer == NULL) @@ -399,10 +397,13 @@ void Y_IntermissionDrawer(void) // draw the "got through act" lines and act number 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) - V_DrawLevelActNum(244, 57, 0, data.coop.actnum); + if (data.coop.actnum) + V_DrawLevelActNum(244, 42+h, 0, data.coop.actnum); + } bonusy = 150; // Total @@ -485,10 +486,10 @@ void Y_IntermissionDrawer(void) if (drawsection == 1) { - const char *ringtext = "\x82" "50 RINGS, NO SHIELD"; - const char *tut1text = "\x82" "PRESS " "\x80" "SPIN"; - const char *tut2text = "\x82" "MID-" "\x80" "JUMP"; - ttheight = 16; + const char *ringtext = "\x82" "50 rings, no shield"; + const char *tut1text = "\x82" "press " "\x80" "spin"; + const char *tut2text = "\x82" "mid-" "\x80" "jump"; + ttheight = 8; V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1); ttheight += V_LevelNameHeight(data.spec.passed3) + 2; V_DrawLevelTitle(data.spec.passedx3 + xoffset2, ttheight, 0, data.spec.passed3); @@ -497,9 +498,9 @@ void Y_IntermissionDrawer(void) ttheight = 108; 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; + 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); } else @@ -816,7 +817,7 @@ void Y_IntermissionDrawer(void) } } } - else if (intertype == int_classicrace) + else if (intertype == int_comp) { INT32 x = 4; INT32 y = 48; @@ -950,7 +951,7 @@ void Y_Ticker(void) if (!--timer) { Y_EndIntermission(); - Y_FollowIntermission(); + G_AfterIntermission(); return; } } @@ -958,7 +959,7 @@ void Y_Ticker(void) else if (intertic == endtic) { Y_EndIntermission(); - Y_FollowIntermission(); + G_AfterIntermission(); return; } @@ -1142,7 +1143,7 @@ void Y_Ticker(void) if (data.match.numplayers != D_NumPlayers()) 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 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 // @@ -1249,7 +1160,6 @@ static void Y_UpdateRecordReplays(void) void Y_StartIntermission(void) { INT32 i; - UINT8 completionEmblems = M_CompletionEmblems(); intertic = -1; @@ -1262,10 +1172,7 @@ void Y_StartIntermission(void) { timer = 0; - if (G_IsSpecialStage(gamemap)) - intertype = (maptol & TOL_NIGHTS) ? int_nightsspec : int_spec; - else - intertype = (maptol & TOL_NIGHTS) ? int_nights : int_coop; + intertype = (G_IsSpecialStage(gamemap)) ? int_spec : int_coop; } else { @@ -1280,14 +1187,7 @@ void Y_StartIntermission(void) } if (gametype == GT_COOP) - { - // Nights intermission is single player only - // Don't add it here - if (G_IsSpecialStage(gamemap)) - intertype = int_spec; - else - intertype = int_coop; - } + intertype = (G_IsSpecialStage(gamemap)) ? int_spec : int_coop; else if (gametype == GT_TEAMMATCH) intertype = int_teammatch; else if (gametype == GT_MATCH @@ -1297,7 +1197,7 @@ void Y_StartIntermission(void) else if (gametype == GT_RACE) intertype = int_race; else if (gametype == GT_COMPETITION) - intertype = int_classicrace; + intertype = int_comp; else if (gametype == GT_CTF) intertype = int_ctf; } @@ -1312,20 +1212,6 @@ void Y_StartIntermission(void) 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 { // award time and ring bonuses @@ -1334,24 +1220,6 @@ void Y_StartIntermission(void) // setup time data 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) data.coop.bonuspatches[i] = W_CachePatchName(data.coop.bonuses[i].patch, 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" if (strlen(skins[players[consoleplayer].skin].realname) > 13) { - strcpy(data.coop.passed1, "YOU GOT"); - strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "THROUGH ACT" : "THROUGH THE ACT"); + strcpy(data.coop.passed1, "you got"); + 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" else if (strlen(skins[players[consoleplayer].skin].realname) > 8) { 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 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); - 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 @@ -1418,40 +1286,8 @@ void Y_StartIntermission(void) 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 { - // 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 Y_AwardSpecialStageBonus(); @@ -1498,7 +1334,7 @@ void Y_StartIntermission(void) // set up the "got through act" message according to skin name if (stagefailed) { - strcpy(data.spec.passed2, "SPECIAL STAGE"); + strcpy(data.spec.passed2, "Special Stage"); data.spec.passed1[0] = '\0'; } else if (ALL7EMERALDS(emeralds)) @@ -1507,13 +1343,13 @@ void Y_StartIntermission(void) sizeof data.spec.passed1, "%s", skins[players[consoleplayer].skin].realname); 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) { - strcpy(data.spec.passed3, "CAN NOW BECOME"); + strcpy(data.spec.passed3, "can now become"); snprintf(data.spec.passed4, - sizeof data.spec.passed4, "SUPER %s", + sizeof data.spec.passed4, "Super %s", skins[players[consoleplayer].skin].realname); 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) { snprintf(data.spec.passed1, - sizeof data.spec.passed1, "%s GOT", + sizeof data.spec.passed1, "%s got", skins[players[consoleplayer].skin].realname); data.spec.passed1[sizeof data.spec.passed1 - 1] = '\0'; } else - strcpy(data.spec.passed1, "YOU GOT"); - strcpy(data.spec.passed2, "A CHAOS EMERALD"); + strcpy(data.spec.passed1, "You got"); + strcpy(data.spec.passed2, "a Chaos Emerald"); if (P_GetNextEmerald() > 6) { data.spec.passed2[15] = '?'; @@ -1637,7 +1473,7 @@ void Y_StartIntermission(void) break; } - case int_classicrace: // classic (full race) + case int_comp: // classic (full race) { // find out who won Y_CalculateCompetitionWinners(); @@ -2192,23 +2028,6 @@ void Y_EndIntermission(void) 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 // @@ -2221,8 +2040,6 @@ static void Y_UnloadData(void) if (rendermode != render_soft) return; - Y_CleanupScreenBuffer(); - // unload the background patches UNLOAD(bgpatch); UNLOAD(widebgpatch); @@ -2258,7 +2075,7 @@ static void Y_UnloadData(void) break; 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 break; } diff --git a/src/y_inter.h b/src/y_inter.h index ccb48dbd4..b47f3b157 100644 --- a/src/y_inter.h +++ b/src/y_inter.h @@ -16,6 +16,7 @@ void Y_Ticker(void); void Y_StartIntermission(void); void Y_EndIntermission(void); void Y_ConsiderScreenBuffer(void); +void Y_CleanupScreenBuffer(void); typedef enum { @@ -26,9 +27,7 @@ typedef enum // int_tag, // Tag int_ctf, // CTF int_spec, // Special Stage - int_nights, // NiGHTS into Dreams - int_nightsspec,// NiGHTS special stage int_race, // Race - int_classicrace, // Competition + int_comp, // Competition } intertype_t; extern intertype_t intertype;