diff --git a/src/d_main.c b/src/d_main.c index 7a8a85f255e96db1f7e4f5ad3c86963131df7325..d1936d6bb1c75b98c26201d450ba60abca336433 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -301,7 +301,7 @@ static void D_Display(void) if (rendermode != render_none) { // Fade to black first - if (gamestate != GS_LEVEL // fades to black on its own timing, always + if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction)) // fades to black on its own timing, always && wipedefs[wipedefindex] != UINT8_MAX) { F_WipeStartScreen(); @@ -317,6 +317,12 @@ static void D_Display(void) // do buffered drawing switch (gamestate) { + case GS_TITLESCREEN: + if (!titlemapinaction) { + F_TitleScreenDrawer(); + break; + } + // Intentional fall-through case GS_LEVEL: if (!gametic) break; @@ -365,10 +371,6 @@ static void D_Display(void) HU_Drawer(); break; - case GS_TITLESCREEN: - F_TitleScreenDrawer(); - break; - case GS_WAITINGPLAYERS: // The clientconnect drawer is independent... case GS_DEDICATEDSERVER: @@ -378,9 +380,10 @@ 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)) { // draw the view directly + if (!automapactive && !dedicated && cv_renderview.value) { if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD) @@ -438,9 +441,13 @@ static void D_Display(void) lastdraw = false; } - ST_Drawer(); - - HU_Drawer(); + if (gamestate == GS_LEVEL) + { + ST_Drawer(); + HU_Drawer(); + } + else + F_TitleScreenDrawer(); } // change gamma if needed @@ -680,6 +687,9 @@ void D_AdvanceDemo(void) void D_StartTitle(void) { INT32 i; + + S_StopMusic(); + if (netgame) { if (gametype == GT_COOP) @@ -1345,6 +1355,19 @@ void D_SRB2Main(void) ultimatemode = true; } + // rei/miru: bootmap (Idea: starts the game on a predefined map) + if (bootmap && !(M_CheckParm("-warp") && M_IsNextParm())) + { + pstartmap = bootmap; + + if (pstartmap < 1 || pstartmap > NUMMAPS) + I_Error("Cannot warp to map %d (out of range)\n", pstartmap); + else + { + autostart = true; + } + } + if (autostart || netgame || M_CheckParm("+connect") || M_CheckParm("-connect")) { gameaction = ga_nothing; diff --git a/src/dehacked.c b/src/dehacked.c index 719476543d95d99aebfdceb2d81957074a0d917d..89f44cb3b6f310dc139017e4dfcb37c322c8c445 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -77,6 +77,8 @@ boolean deh_loaded = false; static int dbg_line; static boolean gamedataadded = false; +static boolean titlechanged = false; +static boolean introchanged = false; ATTRINLINE static FUNCINLINE char myfget_color(MYFILE *f) { @@ -2667,14 +2669,38 @@ static void readmaincfg(MYFILE *f) // range check, you morons. if (introtoplay > 128) introtoplay = 128; + introchanged = true; } else if (fastcmp(word, "LOOPTITLE")) { looptitle = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "TITLEMAP")) + { + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = M_MapNumber(word2[0], word2[1]); + else + value = get_number(word2); + + DEH_WriteUndoline(word, va("%d", titlemap), UNDO_NONE); + titlemap = (INT16)value; + titlechanged = true; + } + else if (fastcmp(word, "HIDETITLEPICS")) + { + DEH_WriteUndoline(word, va("%d", hidetitlepics), UNDO_NONE); + hidetitlepics = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; } else if (fastcmp(word, "TITLESCROLLSPEED")) { titlescrollspeed = get_number(word2); + titlechanged = true; } else if (fastcmp(word, "CREDITSCUTSCENE")) { @@ -2690,14 +2716,17 @@ static void readmaincfg(MYFILE *f) else if (fastcmp(word, "NUMDEMOS")) { numDemos = (UINT8)get_number(word2); + titlechanged = true; } else if (fastcmp(word, "DEMODELAYTIME")) { demoDelayTime = get_number(word2); + titlechanged = true; } else if (fastcmp(word, "DEMOIDLETIME")) { demoIdleTime = get_number(word2); + titlechanged = true; } else if (fastcmp(word, "USE1UPSOUND")) { @@ -2731,14 +2760,32 @@ static void readmaincfg(MYFILE *f) strlcat(savegamename, "%u.ssg", sizeof(savegamename)); gamedataadded = true; + titlechanged = true; } else if (fastcmp(word, "RESETDATA")) { P_ResetData(value); + titlechanged = true; } else if (fastcmp(word, "CUSTOMVERSION")) { strlcpy(customversionstring, word2, sizeof (customversionstring)); + //titlechanged = true; + } + else if (fastcmp(word, "BOOTMAP")) + { + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = M_MapNumber(word2[0], word2[1]); + else + value = get_number(word2); + + DEH_WriteUndoline(word, va("%d", bootmap), UNDO_NONE); + bootmap = (INT16)value; + //titlechanged = true; } else deh_warning("Maincfg: unknown word '%s'", word); @@ -2935,7 +2982,7 @@ static void DEH_LoadDehackedFile(MYFILE *f) deh_num_warning = 0; - gamedataadded = false; + gamedataadded = titlechanged = introchanged = false; // it doesn't test the version of SRB2 and version of dehacked file dbg_line = -1; // start at -1 so the first line is 0. @@ -3236,6 +3283,14 @@ static void DEH_LoadDehackedFile(MYFILE *f) if (gamedataadded) G_LoadGameData(); + if (gamestate == GS_TITLESCREEN) + { + if (introchanged) + COM_BufAddText("playintro"); + else if (titlechanged) + COM_BufAddText("exitgame"); // Command_ExitGame_f() but delayed + } + dbg_line = -1; if (deh_num_warning) { @@ -8119,6 +8174,12 @@ static inline int lib_getenum(lua_State *L) } else if (fastcmp(word,"paused")) { lua_pushboolean(L, paused); return 1; + } else if (fastcmp(word,"titlemap")) { + lua_pushinteger(L, titlemap); + return 1; + } else if (fastcmp(word,"titlemapinaction")) { + lua_pushboolean(L, (titlemapinaction != TITLEMAP_OFF)); + return 1; } else if (fastcmp(word,"gametype")) { lua_pushinteger(L, gametype); return 1; diff --git a/src/doomstat.h b/src/doomstat.h index a24bad79d73dc21dbd1b7b5b63728e4efb4862f0..04930eff516d6876590a1b8de9c1a2e1d6adb64e 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -125,6 +125,10 @@ extern INT16 spstage_start; extern INT16 sstage_start; extern INT16 sstage_end; +extern INT16 titlemap; +extern boolean hidetitlepics; +extern INT16 bootmap; //bootmap for loading a map on startup + extern boolean looptitle; extern boolean useNightsSS; diff --git a/src/f_finale.c b/src/f_finale.c index db497daf7f26d59057e24c32f27c45fb521c73de..89df1446774922fe257d827985e15334b7374c2c 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -31,11 +31,18 @@ #include "m_random.h" #include "y_inter.h" #include "m_cond.h" +#include "p_local.h" +#include "p_setup.h" + +#ifdef HAVE_BLUA +#include "lua_hud.h" +#endif // Stage of animation: // 0 = text, 1 = art screen static INT32 finalecount; INT32 titlescrollspeed = 80; +UINT8 titlemapinaction = TITLEMAP_OFF; static INT32 timetonext; // Delay between screen changes static INT32 continuetime; // Short delay when continuing @@ -217,17 +224,19 @@ static void F_SkyScroll(INT32 scrollspeed) { INT32 scrolled, x, mx, fakedwidth; patch_t *pat; + INT16 patwidth; pat = W_CachePatchName("TITLESKY", PU_CACHE); - animtimer = ((finalecount*scrollspeed)/16) % SHORT(pat->width); + patwidth = SHORT(pat->width); + animtimer = ((finalecount*scrollspeed)/16 + patwidth) % patwidth; fakedwidth = vid.width / vid.dupx; if (rendermode == render_soft) { // if only hardware rendering could be this elegant and complete - scrolled = (SHORT(pat->width) - animtimer) - 1; - for (x = 0, mx = scrolled; x < fakedwidth; x++, mx = (mx+1)%SHORT(pat->width)) + scrolled = (patwidth - animtimer) - 1; + for (x = 0, mx = scrolled; x < fakedwidth; x++, mx = (mx+1)%patwidth) F_DrawPatchCol(x, pat, mx); } #ifdef HWRENDER @@ -235,8 +244,8 @@ static void F_SkyScroll(INT32 scrollspeed) { // if only software rendering could be this simple and retarded scrolled = animtimer; if (scrolled > 0) - V_DrawScaledPatch(scrolled - SHORT(pat->width), 0, 0, pat); - for (x = 0; x < fakedwidth; x += SHORT(pat->width)) + V_DrawScaledPatch(scrolled - patwidth, 0, 0, pat); + for (x = 0; x < fakedwidth; x += patwidth) V_DrawScaledPatch(x + scrolled, 0, 0, pat); } #endif @@ -278,6 +287,8 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset void F_StartIntro(void) { + S_StopMusic(); + if (introtoplay) { if (!cutscenes[introtoplay - 1]) @@ -998,7 +1009,7 @@ static const char *credits[] = { "", "\1Sprite Artists", "Odi \"Iceman404\" Atunzu", - "Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D: + "Victor \"VAdaPEGA\" Ara\x1Fjo", // Araújo -- sorry for our limited font! D: "Jim \"MotorRoach\" DeMello", "Desmond \"Blade\" DesJardins", "Sherman \"CoatRack\" DesJardins", @@ -1415,17 +1426,72 @@ void F_GameEndTicker(void) // ============== void F_StartTitleScreen(void) { + S_ChangeMusicInternal("_title", looptitle); + if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS) finalecount = 0; else wipegamestate = GS_TITLESCREEN; + + if (titlemap) + { + mapthing_t *startpos; + + gamestate_t prevwipegamestate = wipegamestate; + titlemapinaction = TITLEMAP_LOADING; + gamemap = titlemap; + + if (!mapheaderinfo[gamemap-1]) + P_AllocMapHeader(gamemap-1); + + maptol = mapheaderinfo[gamemap-1]->typeoflevel; + globalweather = mapheaderinfo[gamemap-1]->weather; + + G_DoLoadLevel(true); + if (!titlemap) + return; + + players[displayplayer].playerstate = PST_DEAD; // Don't spawn the player in dummy (I'm still a filthy cheater) + + // Set Default Position + if (playerstarts[0]) + startpos = playerstarts[0]; + else if (deathmatchstarts[0]) + startpos = deathmatchstarts[0]; + else + startpos = NULL; + + if (startpos) + { + camera.x = startpos->x << FRACBITS; + camera.y = startpos->y << FRACBITS; + camera.subsector = R_PointInSubsector(camera.x, camera.y); + camera.z = camera.subsector->sector->floorheight + ((startpos->options >> ZSHIFT) << FRACBITS); + camera.angle = (startpos->angle % 360)*ANG1; + camera.aiming = 0; + } + else + { + camera.x = camera.y = camera.z = camera.angle = camera.aiming = 0; + camera.subsector = NULL; // toast is filthy too + } + + camera.chase = true; + camera.height = 0; + + wipegamestate = prevwipegamestate; + } + else + { + titlemapinaction = TITLEMAP_OFF; + gamemap = 1; // g_game.c + CON_ClearHUD(); + } + G_SetGamestate(GS_TITLESCREEN); - CON_ClearHUD(); // IWAD dependent stuff. - S_ChangeMusicInternal("_title", looptitle); - animtimer = 0; demoDelayLeft = demoDelayTime; @@ -1455,12 +1521,21 @@ void F_TitleScreenDrawer(void) return; // We likely came here from retrying. Don't do a damn thing. // Draw that sky! - F_SkyScroll(titlescrollspeed); + if (!titlemapinaction) + F_SkyScroll(titlescrollspeed); // Don't draw outside of the title screewn, or if the patch isn't there. if (!ttwing || (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS)) return; + // rei|miru: use title pics? + if (hidetitlepics) +#ifdef HAVE_BLUA + goto luahook; +#else + return; +#endif + V_DrawScaledPatch(30, 14, 0, ttwing); if (finalecount < 57) @@ -1497,6 +1572,11 @@ void F_TitleScreenDrawer(void) } V_DrawScaledPatch(48, 142, 0,ttbanner); + +#ifdef HAVE_BLUA +luahook: + LUAh_TitleHUD(); +#endif } // (no longer) De-Demo'd Title Screen @@ -1509,6 +1589,46 @@ void F_TitleScreenTicker(boolean run) if (gameaction != ga_nothing || gamestate != GS_TITLESCREEN) return; + // Execute the titlemap camera settings + if (titlemapinaction) + { + thinker_t *th; + mobj_t *mo2; + mobj_t *cameraref = NULL; + + for (th = thinkercap.next; th != &thinkercap; th = th->next) + { + if (th->function.acp1 != (actionf_p1)P_MobjThinker) // Not a mobj thinker + continue; + + mo2 = (mobj_t *)th; + + if (!mo2) + continue; + + if (mo2->type != MT_ALTVIEWMAN) + continue; + + cameraref = mo2; + break; + } + + if (cameraref) + { + camera.x = cameraref->x; + camera.y = cameraref->y; + camera.z = cameraref->z; + camera.angle = cameraref->angle; + camera.aiming = cameraref->cusval; + camera.subsector = cameraref->subsector; + } + else + { + // Default behavior: Do a lil' camera spin if a title map is loaded; + camera.angle += titlescrollspeed*ANG1/64; + } + } + // no demos to play? or, are they disabled? if (!cv_rollingdemos.value || !numDemos) return; diff --git a/src/f_finale.h b/src/f_finale.h index 1f23643bec2bd52a91cd3477ded284eb2a842484..aadc64ad08e8abb9547155c8ada4420b49468146 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -62,6 +62,15 @@ void F_ContinueDrawer(void); extern INT32 titlescrollspeed; +typedef enum +{ + TITLEMAP_OFF = 0, + TITLEMAP_LOADING, + TITLEMAP_RUNNING +} titlemap_enum; + +extern UINT8 titlemapinaction; + // // WIPE // diff --git a/src/g_game.c b/src/g_game.c index e996938ab30ac44cc2d655406e8008e46aaca748..96908de739cd4fdbac47b67bc66fbf65bea5174a 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -121,6 +121,10 @@ INT16 spstage_start; INT16 sstage_start; INT16 sstage_end; +INT16 titlemap = 0; +boolean hidetitlepics = false; +INT16 bootmap; //bootmap for loading a map on startup + boolean looptitle = false; boolean useNightsSS = false; @@ -1633,6 +1637,21 @@ void G_DoLoadLevel(boolean resetplayer) if (gamestate == GS_INTERMISSION) Y_EndIntermission(); + // cleanup + if (titlemapinaction == TITLEMAP_LOADING) + { + if (W_CheckNumForName(G_BuildMapName(gamemap)) == LUMPERROR) + { + titlemap = 0; // let's not infinite recursion ok + Command_ExitGame_f(); + return; + } + + titlemapinaction = TITLEMAP_RUNNING; + } + else + titlemapinaction = TITLEMAP_OFF; + G_SetGamestate(GS_LEVEL); for (i = 0; i < MAXPLAYERS; i++) @@ -1642,7 +1661,7 @@ void G_DoLoadLevel(boolean resetplayer) } // Setup the level. - if (!P_SetupLevel(false)) + if (!P_SetupLevel(false)) // this never returns false? { // fail so reset game stuff Command_ExitGame_f(); @@ -1991,6 +2010,7 @@ void G_Ticker(boolean run) break; case GS_TITLESCREEN: + if (titlemapinaction) P_Ticker(run); // then intentionally fall through case GS_WAITINGPLAYERS: F_TitleScreenTicker(run); break; diff --git a/src/lua_hud.h b/src/lua_hud.h index ba0a1d8941deff4c5b19e40cae1368c19ad86b18..beaca7883e4425818539cbd9d0866e4e6cf3b8cc 100644 --- a/src/lua_hud.h +++ b/src/lua_hud.h @@ -42,3 +42,4 @@ boolean LUA_HudEnabled(enum hud option); void LUAh_GameHUD(player_t *stplyr); void LUAh_ScoresHUD(void); +void LUAh_TitleHUD(void); diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 8175f1b9beacb2ac9e6920a66922c125d474500d..68a69cd1d2934e3af83f24c804aad7368b6f7359 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -88,11 +88,13 @@ static const char *const patch_opt[] = { enum hudhook { hudhook_game = 0, - hudhook_scores + hudhook_scores, + hudhook_title }; static const char *const hudhook_opt[] = { "game", "scores", + "title", NULL}; // alignment types for v.drawString @@ -808,6 +810,9 @@ int LUA_HudLib(lua_State *L) lua_newtable(L); lua_rawseti(L, -2, 3); // HUD[2] = scores rendering functions array + + lua_newtable(L); + lua_rawseti(L, -2, 4); // HUD[3] = title rendering functions array lua_setfield(L, LUA_REGISTRYINDEX, "HUD"); luaL_newmetatable(L, META_HUDINFO); @@ -920,4 +925,29 @@ void LUAh_ScoresHUD(void) hud_running = false; } +void LUAh_TitleHUD(void) +{ + if (!gL || !(hudAvailable & (1<<hudhook_title))) + return; + + hud_running = true; + lua_pop(gL, -1); + + lua_getfield(gL, LUA_REGISTRYINDEX, "HUD"); + I_Assert(lua_istable(gL, -1)); + lua_rawgeti(gL, -1, 4); // HUD[4] = rendering funcs + I_Assert(lua_istable(gL, -1)); + + lua_rawgeti(gL, -2, 1); // HUD[1] = lib_draw + I_Assert(lua_istable(gL, -1)); + lua_remove(gL, -3); // pop HUD + lua_pushnil(gL); + while (lua_next(gL, -3) != 0) { + lua_pushvalue(gL, -3); // graphics library (HUD[1]) + LUA_Call(gL, 1); + } + lua_pop(gL, -1); + hud_running = false; +} + #endif diff --git a/src/p_mobj.c b/src/p_mobj.c index 8f9c44fdbf5789995e83f582f155173dd48b5740..c201830b0276e3c65d8baa440f82d9f2bf3d79a5 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -34,6 +34,7 @@ #ifdef ESLOPE #include "p_slopes.h" #endif +#include "f_finale.h" // protos. static CV_PossibleValue_t viewheight_cons_t[] = {{16, "MIN"}, {56, "MAX"}, {0, NULL}}; @@ -8415,6 +8416,9 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) #endif switch (mobj->type) { + case MT_ALTVIEWMAN: + if (titlemapinaction) mobj->flags &= ~MF_NOTHINK; + break; case MT_CYBRAKDEMON_NAPALM_BOMB_LARGE: mobj->fuse = mobj->info->mass; break; diff --git a/src/p_setup.c b/src/p_setup.c index 9c4bede7474571667eff241d2736e683418ea46b..20c212212c420fb3f54dea2bba1822abab3f0c5c 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2667,7 +2667,9 @@ boolean P_SetupLevel(boolean skipprecip) // As oddly named as this is, this handles music only. // We should be fine starting it here. - S_Start(); + /// ... as long as this isn't a titlemap transition, that is + if (!titlemapinaction) + S_Start(); // Let's fade to black here // But only if we didn't do the special stage wipe @@ -2681,7 +2683,7 @@ boolean P_SetupLevel(boolean skipprecip) } // Print "SPEEDING OFF TO [ZONE] [ACT 1]..." - if (rendermode != render_none) + if (!titlemapinaction && rendermode != render_none) { // Don't include these in the fade! char tx[64]; diff --git a/src/p_user.c b/src/p_user.c index 09cafa0b34ad2d914a6b74416e82edc48379d529..956f6fdd7447f44d6a4c74f171c354769bae2be3 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -165,7 +165,7 @@ fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move) boolean P_AutoPause(void) { // Don't pause even on menu-up or focus-lost in netgames or record attack - if (netgame || modeattacking) + if (netgame || modeattacking || gamestate == GS_TITLESCREEN) return false; return (menuactive || window_notinfocus); diff --git a/src/r_main.c b/src/r_main.c index c998a7d93e3eef39addb34b9e39b5a5f70880c75..cabefed14e7d3714bccd4a57c03925e549b01979 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -914,7 +914,7 @@ void R_SetupFrame(player_t *player, boolean skybox) chasecam = (cv_chasecam.value != 0); } - if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD) + if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN) chasecam = true; // force chasecam on else if (player->spectator) // no spectator chasecam chasecam = false; // force chasecam off diff --git a/src/r_sky.c b/src/r_sky.c index 5162518cba3f4de3d4461700817b916cd1bc9bf9..898424a9925d1f1fadb89f208eaf4c4a6d9d88f8 100644 --- a/src/r_sky.c +++ b/src/r_sky.c @@ -64,10 +64,6 @@ void R_SetupSkyDraw(void) // the horizon line in a 256x128 sky texture skytexturemid = (textures[skytexture]->height/2)<<FRACBITS; - // get the right drawer, it was set by screen.c, depending on the - // current video mode bytes per pixel (quick fix) - wallcolfunc = walldrawerfunc; - R_SetSkyScale(); } diff --git a/src/screen.c b/src/screen.c index 2e3d2e0f419545492d1660e4479b3bf335edfa92..8c1811d5d9d0d1122441b41583587ed879ad54eb 100644 --- a/src/screen.c +++ b/src/screen.c @@ -173,6 +173,9 @@ void SCR_SetMode(void) if (SCR_IsAspectCorrect(vid.width, vid.height)) CONS_Alert(CONS_WARNING, M_GetText("Resolution is not aspect-correct!\nUse a multiple of %dx%d\n"), BASEVIDWIDTH, BASEVIDHEIGHT); #endif*/ + + wallcolfunc = walldrawerfunc; + // set the apprpriate drawer for the sky (tall or INT16) setmodeneeded = 0; }