diff --git a/README.md b/README.md index d16071454..7d92ab303 100644 --- a/README.md +++ b/README.md @@ -13,8 +13,6 @@ - libupnp (Linux/OS X only) - libgme (Linux/OS X only) -Warning: 64-bit builds are not netgame compatible with 32-bit builds. Use at your own risk. - ## Compiling See [SRB2 Wiki/Source code compiling](http://wiki.srb2.org/wiki/Source_code_compiling) diff --git a/src/Makefile b/src/Makefile index f2d532cd5..c33e42daa 100644 --- a/src/Makefile +++ b/src/Makefile @@ -64,6 +64,7 @@ # Compile without 3D sound support, add 'NOHS=1' # Compile with GDBstubs, add 'RDB=1' # Compile without PNG, add 'NOPNG=1' +# Compile without zlib, add 'NOZLIB=1' # # Addon for SDL: # To Cross-Compile, add 'SDL_CONFIG=/usr/*/bin/sdl-config' @@ -107,6 +108,7 @@ include Makefile.cfg ifdef DUMMY NOPNG=1 +NOZLIB=1 NONET=1 NOHW=1 NOHS=1 @@ -269,13 +271,6 @@ LIBS+=$(PNG_LDFLAGS) CFLAGS+=$(PNG_CFLAGS) endif -ZLIB_PKGCONFIG?=zlib -ZLIB_CFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --cflags) -ZLIB_LDFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --libs) - -LIBS+=$(ZLIB_LDFLAGS) -CFLAGS+=$(ZLIB_CFLAGS) - ifdef HAVE_LIBGME OPTS+=-DHAVE_LIBGME @@ -287,6 +282,18 @@ LIBS+=$(LIBGME_LDFLAGS) CFLAGS+=$(LIBGME_CFLAGS) endif +ifndef NOZLIB +OPTS+=-DHAVE_ZLIB +ZLIB_PKGCONFIG?=zlib +ZLIB_CFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --cflags) +ZLIB_LDFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --libs) + +LIBS+=$(ZLIB_LDFLAGS) +CFLAGS+=$(ZLIB_CFLAGS) +else +NOPNG=1 +endif + ifdef STATIC LIBS:=-static $(LIBS) endif diff --git a/src/android/i_sound.c b/src/android/i_sound.c index d7a7973d4..b5a1c3646 100644 --- a/src/android/i_sound.c +++ b/src/android/i_sound.c @@ -21,13 +21,14 @@ void I_ShutdownSound(void){} // SFX I/O // -INT32 I_StartSound(sfxenum_t id, INT32 vol, INT32 sep, INT32 pitch, INT32 priority) +INT32 I_StartSound(sfxenum_t id, INT32 vol, INT32 sep, INT32 pitch, INT32 priority, INT32 channel) { (void)id; (void)vol; (void)sep; (void)pitch; (void)priority; + (void)channel; return -1; } diff --git a/src/byteptr.h b/src/byteptr.h index 410d7c004..364e6520c 100644 --- a/src/byteptr.h +++ b/src/byteptr.h @@ -15,7 +15,9 @@ #define DEALIGNED #endif -#ifndef _BIG_ENDIAN +#include "endian.h" + +#ifndef SRB2_BIG_ENDIAN // // Little-endian machines // @@ -75,7 +77,7 @@ #define READANGLE(p) *((angle_t *)p)++ #endif -#else //_BIG_ENDIAN +#else //SRB2_BIG_ENDIAN // // definitions for big-endian machines with alignment constraints. // @@ -144,7 +146,7 @@ FUNCINLINE static ATTRINLINE UINT32 readulong(void *ptr) #define READCHAR(p) ({ char *p_tmp = ( char *)p; char b = *p_tmp; p_tmp++; p = (void *)p_tmp; b; }) #define READFIXED(p) ({ fixed_t *p_tmp = (fixed_t *)p; fixed_t b = readlong(p); p_tmp++; p = (void *)p_tmp; b; }) #define READANGLE(p) ({ angle_t *p_tmp = (angle_t *)p; angle_t b = readulong(p); p_tmp++; p = (void *)p_tmp; b; }) -#endif //_BIG_ENDIAN +#endif //SRB2_BIG_ENDIAN #undef DEALIGNED diff --git a/src/command.c b/src/command.c index 62431b664..10af3a576 100644 --- a/src/command.c +++ b/src/command.c @@ -63,6 +63,7 @@ CV_PossibleValue_t CV_Unsigned[] = {{0, "MIN"}, {999999999, "MAX"}, {0, NULL}}; CV_PossibleValue_t CV_Natural[] = {{1, "MIN"}, {999999999, "MAX"}, {0, NULL}}; #define COM_BUF_SIZE 8192 // command buffer size +#define MAX_ALIAS_RECURSION 100 // max recursion allowed for aliases static INT32 com_wait; // one command per frame (for cmd sequences) @@ -485,6 +486,7 @@ static void COM_ExecuteString(char *ptext) { xcommand_t *cmd; cmdalias_t *a; + static INT32 recursion = 0; // detects recursion and stops it if it goes too far COM_TokenizeString(ptext); @@ -497,6 +499,7 @@ static void COM_ExecuteString(char *ptext) { if (!stricmp(com_argv[0], cmd->name)) //case insensitive now that we have lower and uppercase! { + recursion = 0; cmd->function(); return; } @@ -507,11 +510,20 @@ static void COM_ExecuteString(char *ptext) { if (!stricmp(com_argv[0], a->name)) { + if (recursion > MAX_ALIAS_RECURSION) + { + CONS_Alert(CONS_WARNING, M_GetText("Alias recursion cycle detected!\n")); + recursion = 0; + return; + } + recursion++; COM_BufInsertText(a->value); return; } } + recursion = 0; + // check cvars // Hurdler: added at Ebola's request ;) // (don't flood the console in software mode with bad gr_xxx command) diff --git a/src/console.c b/src/console.c index 9bc01cf19..f4234d949 100644 --- a/src/console.c +++ b/src/console.c @@ -232,18 +232,20 @@ UINT8 *yellowmap, *magentamap, *lgreenmap, *bluemap, *graymap, *redmap, *orangem // Console BG color UINT8 *consolebgmap = NULL; +UINT8 *promptbgmap = NULL; +static UINT8 promptbgcolor = UINT8_MAX; -void CON_SetupBackColormap(void) +void CON_SetupBackColormapEx(INT32 color, boolean prompt) { UINT16 i, palsum; UINT8 j, palindex, shift; UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE); - if (!consolebgmap) - consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); + if (color == INT32_MAX) + color = cons_backcolor.value; shift = 6; // 12 colors -- shift of 7 means 6 colors - switch (cons_backcolor.value) + switch (color) { case 0: palindex = 15; break; // White case 1: palindex = 31; break; // Gray @@ -257,20 +259,42 @@ void CON_SetupBackColormap(void) case 9: palindex = 187; break; // Magenta case 10: palindex = 139; break; // Aqua // Default green - default: palindex = 175; break; -} + default: palindex = 175; color = 11; break; + } + + if (prompt) + { + if (!promptbgmap) + promptbgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); + + if (color == promptbgcolor) + return; + else + promptbgcolor = color; + } + else if (!consolebgmap) + consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL); // setup background colormap for (i = 0, j = 0; i < 768; i += 3, j++) { palsum = (pal[i] + pal[i+1] + pal[i+2]) >> shift; - consolebgmap[j] = (UINT8)(palindex - palsum); + if (prompt) + promptbgmap[j] = (UINT8)(palindex - palsum); + else + consolebgmap[j] = (UINT8)(palindex - palsum); } } +void CON_SetupBackColormap(void) +{ + CON_SetupBackColormapEx(cons_backcolor.value, false); + CON_SetupBackColormapEx(1, true); // default to gray +} + static void CONS_backcolor_Change(void) { - CON_SetupBackColormap(); + CON_SetupBackColormapEx(cons_backcolor.value, false); } static void CON_SetupColormaps(void) diff --git a/src/console.h b/src/console.h index 970f841d0..c194f44bf 100644 --- a/src/console.h +++ b/src/console.h @@ -38,7 +38,9 @@ extern UINT8 *yellowmap, *magentamap, *lgreenmap, *bluemap, *graymap, *redmap, * // Console bg color (auto updated to match) extern UINT8 *consolebgmap; +extern UINT8 *promptbgmap; +void CON_SetupBackColormapEx(INT32 color, boolean prompt); void CON_SetupBackColormap(void); void CON_ClearHUD(void); // clear heads up messages diff --git a/src/d_clisrv.c b/src/d_clisrv.c index 92da2492e..b720dbf16 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -2353,7 +2353,7 @@ void CL_ClearPlayer(INT32 playernum) // // Removes a player from the current game // -static void CL_RemovePlayer(INT32 playernum) +static void CL_RemovePlayer(INT32 playernum, INT32 reason) { // Sanity check: exceptional cases (i.e. c-fails) can cause multiple // kick commands to be issued for the same player. @@ -2407,6 +2407,10 @@ static void CL_RemovePlayer(INT32 playernum) } } } + +#ifdef HAVE_BLUA + LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting +#endif // Reset player data CL_ClearPlayer(playernum); @@ -2683,6 +2687,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) INT32 pnum, msg; char buf[3 + MAX_REASONLENGTH]; char *reason = buf; + kickreason_t kickreason = KR_KICK; pnum = READUINT8(*p); msg = READUINT8(*p); @@ -2765,14 +2770,17 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) { case KICK_MSG_GO_AWAY: CONS_Printf(M_GetText("has been kicked (Go away)\n")); + kickreason = KR_KICK; break; #ifdef NEWPING case KICK_MSG_PING_HIGH: CONS_Printf(M_GetText("left the game (Broke ping limit)\n")); + kickreason = KR_PINGLIMIT; break; #endif case KICK_MSG_CON_FAIL: CONS_Printf(M_GetText("left the game (Synch failure)\n")); + kickreason = KR_SYNCH; if (M_CheckParm("-consisdump")) // Helps debugging some problems { @@ -2809,21 +2817,26 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) break; case KICK_MSG_TIMEOUT: CONS_Printf(M_GetText("left the game (Connection timeout)\n")); + kickreason = KR_TIMEOUT; break; case KICK_MSG_PLAYER_QUIT: if (netgame) // not splitscreen/bots CONS_Printf(M_GetText("left the game\n")); + kickreason = KR_LEAVE; break; case KICK_MSG_BANNED: CONS_Printf(M_GetText("has been banned (Don't come back)\n")); + kickreason = KR_BAN; break; case KICK_MSG_CUSTOM_KICK: READSTRINGN(*p, reason, MAX_REASONLENGTH+1); CONS_Printf(M_GetText("has been kicked (%s)\n"), reason); + kickreason = KR_KICK; break; case KICK_MSG_CUSTOM_BAN: READSTRINGN(*p, reason, MAX_REASONLENGTH+1); CONS_Printf(M_GetText("has been banned (%s)\n"), reason); + kickreason = KR_BAN; break; } @@ -2851,7 +2864,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) M_StartMessage(M_GetText("You have been kicked by the server\n\nPress ESC\n"), NULL, MM_NOTHING); } else - CL_RemovePlayer(pnum); + CL_RemovePlayer(pnum, kickreason); } consvar_t cv_allownewplayer = {"allowjoin", "On", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL }; diff --git a/src/d_clisrv.h b/src/d_clisrv.h index bdb85a76c..a0afb34c6 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -454,6 +454,17 @@ extern consvar_t cv_playbackspeed; #define KICK_MSG_CUSTOM_KICK 7 #define KICK_MSG_CUSTOM_BAN 8 +typedef enum +{ + KR_KICK = 1, //Kicked by server + KR_PINGLIMIT = 2, //Broke Ping Limit + KR_SYNCH = 3, //Synch Failure + KR_TIMEOUT = 4, //Connection Timeout + KR_BAN = 5, //Banned by server + KR_LEAVE = 6, //Quit the game + +} kickreason_t; + extern boolean server; #define client (!server) extern boolean dedicated; // For dedicated server diff --git a/src/d_main.c b/src/d_main.c index 74d7236a5..282ab4ae9 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -71,6 +71,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #include "fastcmp.h" #include "keys.h" #include "filesrch.h" // refreshdirmenu, mainwadstally +#include "g_input.h" // tutorial mode control scheming #ifdef CMAKECONFIG #include "config.h" @@ -151,7 +152,7 @@ void D_PostEvent(const event_t *ev) eventhead = (eventhead+1) & (MAXEVENTS-1); } // just for lock this function -#ifndef DOXYGEN +#if defined (PC_DOS) && !defined (DOXYGEN) void D_PostEvent_end(void) {}; #endif @@ -424,6 +425,7 @@ static void D_Display(void) if (gamestate == GS_LEVEL) { ST_Drawer(); + F_TextPromptDrawer(); HU_Drawer(); } else @@ -733,6 +735,19 @@ void D_StartTitle(void) // Reset the palette if (rendermode != render_none) V_SetPaletteLump("PLAYPAL"); + + // The title screen is obviously not a tutorial! (Unless I'm mistaken) + if (tutorialmode && tutorialgcs) + { + G_CopyControls(gamecontrol, gamecontroldefault[gcs_custom], gcl_tutorial_full, num_gcl_tutorial_full); // using gcs_custom as temp storage + CV_SetValue(&cv_usemouse, tutorialusemouse); + CV_SetValue(&cv_alwaysfreelook, tutorialfreelook); + CV_SetValue(&cv_mousemove, tutorialmousemove); + CV_SetValue(&cv_analog, tutorialanalog); + M_StartMessage("Do you want to \x82save the recommended \x82movement controls?\x80\n\nPress 'Y' or 'Enter' to confirm\nPress 'N' or any key to keep \nyour current controls", + M_TutorialSaveControlResponse, MM_YESNO); + } + tutorialmode = false; } // @@ -766,10 +781,6 @@ static inline void D_CleanFile(void) } } -#ifndef _MAX_PATH -#define _MAX_PATH MAX_WADPATH -#endif - // ========================================================================== // Identify the SRB2 version, and IWAD file to use. // ========================================================================== diff --git a/src/d_main.h b/src/d_main.h index d73b19d1f..4c9c99ea5 100644 --- a/src/d_main.h +++ b/src/d_main.h @@ -40,8 +40,8 @@ void D_SRB2Main(void); // Called by IO functions when input is detected. void D_PostEvent(const event_t *ev); -#ifndef DOXYGEN -FUNCMATH void D_PostEvent_end(void); // delimiter for locking memory +#if defined (PC_DOS) && !defined (DOXYGEN) +void D_PostEvent_end(void); // delimiter for locking memory #endif void D_ProcessEvents(void); diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 4be1da776..bb33eb133 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -126,8 +126,6 @@ FUNCNORETURN static ATTRNORETURN void Command_Quit_f(void); static void Command_Playintro_f(void); static void Command_Displayplayer_f(void); -static void Command_Tunes_f(void); -static void Command_RestartAudio_f(void); static void Command_ExitLevel_f(void); static void Command_Showmap_f(void); @@ -315,8 +313,6 @@ consvar_t cv_timetic = {"timerres", "Classic", CV_SAVE, timetic_cons_t, NULL, 0, static CV_PossibleValue_t powerupdisplay_cons_t[] = {{0, "Never"}, {1, "First-person only"}, {2, "Always"}, {0, NULL}}; consvar_t cv_powerupdisplay = {"powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; -consvar_t cv_resetmusic = {"resetmusic", "No", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; - static CV_PossibleValue_t pointlimit_cons_t[] = {{0, "MIN"}, {999999990, "MAX"}, {0, NULL}}; consvar_t cv_pointlimit = {"pointlimit", "0", CV_NETVAR|CV_CALL|CV_NOINIT, pointlimit_cons_t, PointLimit_OnChange, 0, NULL, NULL, 0, 0, NULL}; @@ -685,9 +681,6 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_ghost_guest); COM_AddCommand("displayplayer", Command_Displayplayer_f); - COM_AddCommand("tunes", Command_Tunes_f); - COM_AddCommand("restartaudio", Command_RestartAudio_f); - CV_RegisterVar(&cv_resetmusic); // FIXME: not to be here.. but needs be done for config loading CV_RegisterVar(&cv_globalgamma); @@ -719,6 +712,7 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_crosshair2); CV_RegisterVar(&cv_alwaysfreelook); CV_RegisterVar(&cv_alwaysfreelook2); + CV_RegisterVar(&cv_tutorialprompt); // g_input.c CV_RegisterVar(&cv_sideaxis); @@ -1815,6 +1809,16 @@ static void Command_Map_f(void) else fromlevelselect = ((netgame || multiplayer) && ((gametype == newgametype) && (newgametype == GT_COOP))); + if (tutorialmode && tutorialgcs) + { + G_CopyControls(gamecontrol, gamecontroldefault[gcs_custom], gcl_tutorial_full, num_gcl_tutorial_full); // using gcs_custom as temp storage + CV_SetValue(&cv_usemouse, tutorialusemouse); + CV_SetValue(&cv_alwaysfreelook, tutorialfreelook); + CV_SetValue(&cv_mousemove, tutorialmousemove); + CV_SetValue(&cv_analog, tutorialanalog); + } + tutorialmode = false; // warping takes us out of tutorial mode + D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, fromlevelselect); } @@ -3987,100 +3991,6 @@ static void Command_Displayplayer_f(void) CONS_Printf(M_GetText("Displayplayer is %d\n"), displayplayer); } -static void Command_Tunes_f(void) -{ - const char *tunearg; - UINT16 tunenum, track = 0; - UINT32 position = 0; - const size_t argc = COM_Argc(); - - if (argc < 2) //tunes slot ... - { - CONS_Printf("tunes [track] [speed] [position] / <-show> / <-default> / <-none>:\n"); - CONS_Printf(M_GetText("Play an arbitrary music lump. If a map number is used, 'MAP##M' is played.\n")); - CONS_Printf(M_GetText("If the format supports multiple songs, you can specify which one to play.\n\n")); - CONS_Printf(M_GetText("* With \"-show\", shows the currently playing tune and track.\n")); - CONS_Printf(M_GetText("* With \"-default\", returns to the default music for the map.\n")); - CONS_Printf(M_GetText("* With \"-none\", any music playing will be stopped.\n")); - return; - } - - tunearg = COM_Argv(1); - tunenum = (UINT16)atoi(tunearg); - track = 0; - - if (!strcasecmp(tunearg, "-show")) - { - CONS_Printf(M_GetText("The current tune is: %s [track %d]\n"), - mapmusname, (mapmusflags & MUSIC_TRACKMASK)); - return; - } - if (!strcasecmp(tunearg, "-none")) - { - S_StopMusic(); - return; - } - else if (!strcasecmp(tunearg, "-default")) - { - tunearg = mapheaderinfo[gamemap-1]->musname; - track = mapheaderinfo[gamemap-1]->mustrack; - } - else if (!tunearg[2] && toupper(tunearg[0]) >= 'A' && toupper(tunearg[0]) <= 'Z') - tunenum = (UINT16)M_MapNumber(tunearg[0], tunearg[1]); - - if (tunenum && tunenum >= 1036) - { - CONS_Alert(CONS_NOTICE, M_GetText("Valid music slots are 1 to 1035.\n")); - return; - } - if (!tunenum && strlen(tunearg) > 6) // This is automatic -- just show the error just in case - CONS_Alert(CONS_NOTICE, M_GetText("Music name too long - truncated to six characters.\n")); - - if (argc > 2) - track = (UINT16)atoi(COM_Argv(2))-1; - - if (tunenum) - snprintf(mapmusname, 7, "%sM", G_BuildMapName(tunenum)); - else - strncpy(mapmusname, tunearg, 7); - - if (argc > 4) - position = (UINT32)atoi(COM_Argv(4)); - - mapmusname[6] = 0; - mapmusflags = (track & MUSIC_TRACKMASK); - mapmusposition = position; - - S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); - - if (argc > 3) - { - float speed = (float)atof(COM_Argv(3)); - if (speed > 0.0f) - S_SpeedMusic(speed); - } -} - -static void Command_RestartAudio_f(void) -{ - if (dedicated) // No point in doing anything if game is a dedicated server. - return; - - S_StopMusic(); - S_StopSounds(); - I_ShutdownMusic(); - I_ShutdownSound(); - I_StartupSound(); - I_InitMusic(); - -// These must be called or no sound and music until manually set. - - I_SetSfxVolume(cv_soundvolume.value); - S_SetMusicVolume(cv_digmusicvolume.value, cv_midimusicvolume.value); - if (Playing()) // Gotta make sure the player is in a level - P_RestoreMusic(&players[consoleplayer]); -} - /** Quits a game and returns to the title screen. * */ diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 57e23b0f1..435ca64a5 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -103,8 +103,6 @@ extern consvar_t cv_startinglives; // for F_finale.c extern consvar_t cv_rollingdemos; -extern consvar_t cv_resetmusic; - extern consvar_t cv_ringslinger, cv_soundtest; extern consvar_t cv_specialrings, cv_powerstones, cv_matchboxes, cv_competitionboxes; @@ -202,6 +200,4 @@ void D_SetPassword(const char *pw); // used for the player setup menu UINT8 CanChangeSkin(INT32 playernum); -#endif - - +#endif \ No newline at end of file diff --git a/src/dehacked.c b/src/dehacked.c index 71a36cfdf..7e246201d 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -32,6 +32,7 @@ #include "fastcmp.h" #include "lua_script.h" #include "lua_hook.h" +#include "d_clisrv.h" #include "m_cond.h" @@ -165,9 +166,14 @@ static char *myhashfgets(char *buf, size_t bufsize, MYFILE *f) if (c == '\n') // Ensure debug line is right... dbg_line++; if (c == '#') + { + if (i > 0) // don't let i wrap past 0 + i--; // don't include hash char in string break; + } } - i++; + if (buf[i] != '#') // don't include hash char in string + i++; buf[i] = '\0'; return buf; @@ -914,7 +920,10 @@ static void readlevelheader(MYFILE *f, INT32 num) // Get the part before the " = " tmp = strchr(s, '='); - *(tmp-1) = '\0'; + if (tmp) + *(tmp-1) = '\0'; + else + break; strupr(word); // Now get the part after @@ -1560,6 +1569,365 @@ static void readcutscene(MYFILE *f, INT32 num) Z_Free(s); } +static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum) +{ + char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2; + INT32 i; + UINT16 usi; + UINT8 picid; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + if (fastcmp(word, "PAGETEXT")) + { + char *pagetext = NULL; + char *buffer; + const int bufferlen = 4096; + + for (i = 0; i < MAXLINELEN; i++) + { + if (s[i] == '=') + { + pagetext = &s[i+2]; + break; + } + } + + if (!pagetext) + { + Z_Free(textprompts[num]->page[pagenum].text); + textprompts[num]->page[pagenum].text = NULL; + continue; + } + + for (i = 0; i < MAXLINELEN; i++) + { + if (s[i] == '\0') + { + s[i] = '\n'; + s[i+1] = '\0'; + break; + } + } + + buffer = Z_Malloc(4096, PU_STATIC, NULL); + strcpy(buffer, pagetext); + + // \todo trim trailing whitespace before the # + // and also support # at the end of a PAGETEXT with no line break + + strcat(buffer, + myhashfgets(pagetext, bufferlen + - strlen(buffer) - 1, f)); + + // A text prompt overwriting another one... + Z_Free(textprompts[num]->page[pagenum].text); + + textprompts[num]->page[pagenum].text = Z_StrDup(buffer); + + Z_Free(buffer); + + continue; + } + + word2 = strtok(NULL, " = "); + if (word2) + strupr(word2); + else + break; + + if (word2[strlen(word2)-1] == '\n') + word2[strlen(word2)-1] = '\0'; + i = atoi(word2); + usi = (UINT16)i; + + // copypasta from readcutscenescene + if (fastcmp(word, "NUMBEROFPICS")) + { + textprompts[num]->page[pagenum].numpics = (UINT8)i; + } + else if (fastcmp(word, "PICMODE")) + { + UINT8 picmode = 0; // PROMPT_PIC_PERSIST + if (usi == 1 || word2[0] == 'L') picmode = PROMPT_PIC_LOOP; + else if (usi == 2 || word2[0] == 'D' || word2[0] == 'H') picmode = PROMPT_PIC_DESTROY; + textprompts[num]->page[pagenum].picmode = picmode; + } + else if (fastcmp(word, "PICTOLOOP")) + textprompts[num]->page[pagenum].pictoloop = (UINT8)i; + else if (fastcmp(word, "PICTOSTART")) + textprompts[num]->page[pagenum].pictostart = (UINT8)i; + else if (fastcmp(word, "PICSMETAPAGE")) + { + if (usi && usi <= textprompts[num]->numpages) + { + UINT8 metapagenum = usi - 1; + + textprompts[num]->page[pagenum].numpics = textprompts[num]->page[metapagenum].numpics; + textprompts[num]->page[pagenum].picmode = textprompts[num]->page[metapagenum].picmode; + textprompts[num]->page[pagenum].pictoloop = textprompts[num]->page[metapagenum].pictoloop; + textprompts[num]->page[pagenum].pictostart = textprompts[num]->page[metapagenum].pictostart; + + for (picid = 0; picid < MAX_PROMPT_PICS; picid++) + { + strncpy(textprompts[num]->page[pagenum].picname[picid], textprompts[num]->page[metapagenum].picname[picid], 8); + textprompts[num]->page[pagenum].pichires[picid] = textprompts[num]->page[metapagenum].pichires[picid]; + textprompts[num]->page[pagenum].picduration[picid] = textprompts[num]->page[metapagenum].picduration[picid]; + textprompts[num]->page[pagenum].xcoord[picid] = textprompts[num]->page[metapagenum].xcoord[picid]; + textprompts[num]->page[pagenum].ycoord[picid] = textprompts[num]->page[metapagenum].ycoord[picid]; + } + } + } + else if (fastncmp(word, "PIC", 3)) + { + picid = (UINT8)atoi(word + 3); + if (picid > MAX_PROMPT_PICS || picid == 0) + { + deh_warning("textpromptscene %d: unknown word '%s'", num, word); + continue; + } + --picid; + + if (fastcmp(word+4, "NAME")) + { + strncpy(textprompts[num]->page[pagenum].picname[picid], word2, 8); + } + else if (fastcmp(word+4, "HIRES")) + { + textprompts[num]->page[pagenum].pichires[picid] = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); + } + else if (fastcmp(word+4, "DURATION")) + { + textprompts[num]->page[pagenum].picduration[picid] = usi; + } + else if (fastcmp(word+4, "XCOORD")) + { + textprompts[num]->page[pagenum].xcoord[picid] = usi; + } + else if (fastcmp(word+4, "YCOORD")) + { + textprompts[num]->page[pagenum].ycoord[picid] = usi; + } + else + deh_warning("textpromptscene %d: unknown word '%s'", num, word); + } + else if (fastcmp(word, "MUSIC")) + { + strncpy(textprompts[num]->page[pagenum].musswitch, word2, 7); + textprompts[num]->page[pagenum].musswitch[6] = 0; + } +#ifdef MUSICSLOT_COMPATIBILITY + else if (fastcmp(word, "MUSICSLOT")) + { + i = get_mus(word2, true); + if (i && i <= 1035) + snprintf(textprompts[num]->page[pagenum].musswitch, 7, "%sM", G_BuildMapName(i)); + else if (i && i <= 1050) + strncpy(textprompts[num]->page[pagenum].musswitch, compat_special_music_slots[i - 1036], 7); + else + textprompts[num]->page[pagenum].musswitch[0] = 0; // becomes empty string + textprompts[num]->page[pagenum].musswitch[6] = 0; + } +#endif + else if (fastcmp(word, "MUSICTRACK")) + { + textprompts[num]->page[pagenum].musswitchflags = ((UINT16)i) & MUSIC_TRACKMASK; + } + else if (fastcmp(word, "MUSICLOOP")) + { + textprompts[num]->page[pagenum].musicloop = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); + } + // end copypasta from readcutscenescene + else if (fastcmp(word, "NAME")) + { + INT32 j; + + // HACK: Add yellow control char now + // so the drawing function doesn't call it repeatedly + char name[34]; + name[0] = '\x82'; // color yellow + name[1] = 0; + strncat(name, word2, 33); + name[33] = 0; + + // Replace _ with ' ' + for (j = 0; j < 32 && name[j]; j++) + { + if (name[j] == '_') + name[j] = ' '; + } + + strncpy(textprompts[num]->page[pagenum].name, name, 32); + } + else if (fastcmp(word, "ICON")) + strncpy(textprompts[num]->page[pagenum].iconname, word2, 8); + else if (fastcmp(word, "ICONALIGN")) + textprompts[num]->page[pagenum].rightside = (i || word2[0] == 'R'); + else if (fastcmp(word, "ICONFLIP")) + textprompts[num]->page[pagenum].iconflip = (i || word2[0] == 'T' || word2[0] == 'Y'); + else if (fastcmp(word, "LINES")) + textprompts[num]->page[pagenum].lines = usi; + else if (fastcmp(word, "BACKCOLOR")) + { + INT32 backcolor; + if (i == 0 || fastcmp(word2, "WHITE")) backcolor = 0; + else if (i == 1 || fastcmp(word2, "GRAY") || fastcmp(word2, "GREY") || + fastcmp(word2, "BLACK")) backcolor = 1; + else if (i == 2 || fastcmp(word2, "BROWN")) backcolor = 2; + else if (i == 3 || fastcmp(word2, "RED")) backcolor = 3; + else if (i == 4 || fastcmp(word2, "ORANGE")) backcolor = 4; + else if (i == 5 || fastcmp(word2, "YELLOW")) backcolor = 5; + else if (i == 6 || fastcmp(word2, "GREEN")) backcolor = 6; + else if (i == 7 || fastcmp(word2, "BLUE")) backcolor = 7; + else if (i == 8 || fastcmp(word2, "PURPLE")) backcolor = 8; + else if (i == 9 || fastcmp(word2, "MAGENTA")) backcolor = 9; + else if (i == 10 || fastcmp(word2, "AQUA")) backcolor = 10; + else if (i < 0) backcolor = INT32_MAX; // CONS_BACKCOLOR user-configured + else backcolor = 1; // default gray + textprompts[num]->page[pagenum].backcolor = backcolor; + } + else if (fastcmp(word, "ALIGN")) + { + UINT8 align = 0; // left + if (usi == 1 || word2[0] == 'R') align = 1; + else if (usi == 2 || word2[0] == 'C' || word2[0] == 'M') align = 2; + textprompts[num]->page[pagenum].align = align; + } + else if (fastcmp(word, "VERTICALALIGN")) + { + UINT8 align = 0; // top + if (usi == 1 || word2[0] == 'B') align = 1; + else if (usi == 2 || word2[0] == 'C' || word2[0] == 'M') align = 2; + textprompts[num]->page[pagenum].verticalalign = align; + } + else if (fastcmp(word, "TEXTSPEED")) + textprompts[num]->page[pagenum].textspeed = get_number(word2); + else if (fastcmp(word, "TEXTSFX")) + textprompts[num]->page[pagenum].textsfx = get_number(word2); + else if (fastcmp(word, "HIDEHUD")) + { + UINT8 hidehud = 0; + if ((word2[0] == 'F' && (word2[1] == 'A' || !word2[1])) || word2[0] == 'N') hidehud = 0; // false + else if (usi == 1 || word2[0] == 'T' || word2[0] == 'Y') hidehud = 1; // true (hide appropriate HUD elements) + else if (usi == 2 || word2[0] == 'A' || (word2[0] == 'F' && word2[1] == 'O')) hidehud = 2; // force (hide all HUD elements) + textprompts[num]->page[pagenum].hidehud = hidehud; + } + else if (fastcmp(word, "METAPAGE")) + { + if (usi && usi <= textprompts[num]->numpages) + { + UINT8 metapagenum = usi - 1; + + strncpy(textprompts[num]->page[pagenum].name, textprompts[num]->page[metapagenum].name, 32); + strncpy(textprompts[num]->page[pagenum].iconname, textprompts[num]->page[metapagenum].iconname, 8); + textprompts[num]->page[pagenum].rightside = textprompts[num]->page[metapagenum].rightside; + textprompts[num]->page[pagenum].iconflip = textprompts[num]->page[metapagenum].iconflip; + textprompts[num]->page[pagenum].lines = textprompts[num]->page[metapagenum].lines; + textprompts[num]->page[pagenum].backcolor = textprompts[num]->page[metapagenum].backcolor; + textprompts[num]->page[pagenum].align = textprompts[num]->page[metapagenum].align; + textprompts[num]->page[pagenum].verticalalign = textprompts[num]->page[metapagenum].verticalalign; + textprompts[num]->page[pagenum].textspeed = textprompts[num]->page[metapagenum].textspeed; + textprompts[num]->page[pagenum].textsfx = textprompts[num]->page[metapagenum].textsfx; + textprompts[num]->page[pagenum].hidehud = textprompts[num]->page[metapagenum].hidehud; + + // music: don't copy, else each page change may reset the music + } + } + else if (fastcmp(word, "TAG")) + strncpy(textprompts[num]->page[pagenum].tag, word2, 33); + else if (fastcmp(word, "NEXTPROMPT")) + textprompts[num]->page[pagenum].nextprompt = usi; + else if (fastcmp(word, "NEXTPAGE")) + textprompts[num]->page[pagenum].nextpage = usi; + else if (fastcmp(word, "NEXTTAG")) + strncpy(textprompts[num]->page[pagenum].nexttag, word2, 33); + else if (fastcmp(word, "TIMETONEXT")) + textprompts[num]->page[pagenum].timetonext = get_number(word2); + else + deh_warning("PromptPage %d: unknown word '%s'", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +static void readtextprompt(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2; + char *tmp; + INT32 value; + + // Allocate memory for this prompt if we don't yet have any + if (!textprompts[num]) + textprompts[num] = Z_Calloc(sizeof (textprompt_t), PU_STATIC, NULL); + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + word2 = strtok(NULL, " "); + if (word2) + value = atoi(word2); + else + { + deh_warning("No value for token %s", word); + continue; + } + + if (fastcmp(word, "NUMPAGES")) + { + textprompts[num]->numpages = min(max(value, 0), MAX_PAGES); + } + else if (fastcmp(word, "PAGE")) + { + if (1 <= value && value <= MAX_PAGES) + { + textprompts[num]->page[value - 1].backcolor = 1; // default to gray + textprompts[num]->page[value - 1].hidehud = 1; // hide appropriate HUD elements + readtextpromptpage(f, num, value - 1); + } + else + deh_warning("Page number %d out of range (1 - %d)", value, MAX_PAGES); + + } + else + deh_warning("Prompt %d: unknown word '%s', Page expected.", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + static void readhuditem(MYFILE *f, INT32 num) { char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); @@ -1588,7 +1956,10 @@ static void readhuditem(MYFILE *f, INT32 num) // Get the part before the " = " tmp = strchr(s, '='); - *(tmp-1) = '\0'; + if (tmp) + *(tmp-1) = '\0'; + else + break; strupr(word); // Now get the part after @@ -2094,7 +2465,10 @@ static void reademblemdata(MYFILE *f, INT32 num) // Get the part before the " = " tmp = strchr(s, '='); - *(tmp-1) = '\0'; + if (tmp) + *(tmp-1) = '\0'; + else + break; strupr(word); // Now get the part after @@ -2229,7 +2603,10 @@ static void readextraemblemdata(MYFILE *f, INT32 num) // Get the part before the " = " tmp = strchr(s, '='); - *(tmp-1) = '\0'; + if (tmp) + *(tmp-1) = '\0'; + else + break; strupr(word); // Now get the part after @@ -2304,7 +2681,10 @@ static void readunlockable(MYFILE *f, INT32 num) // Get the part before the " = " tmp = strchr(s, '='); - *(tmp-1) = '\0'; + if (tmp) + *(tmp-1) = '\0'; + else + break; strupr(word); // Now get the part after @@ -2591,7 +2971,10 @@ static void readconditionset(MYFILE *f, UINT8 setnum) // Get the part before the " = " tmp = strchr(s, '='); - *(tmp-1) = '\0'; + if (tmp) + *(tmp-1) = '\0'; + else + break; strupr(word); // Now get the part after @@ -2652,7 +3035,10 @@ static void readmaincfg(MYFILE *f) // Get the part before the " = " tmp = strchr(s, '='); - *(tmp-1) = '\0'; + if (tmp) + *(tmp-1) = '\0'; + else + break; strupr(word); // Now get the part after @@ -2885,6 +3271,19 @@ static void readmaincfg(MYFILE *f) startchar = (INT16)value; char_on = -1; } + else if (fastcmp(word, "TUTORIALMAP")) + { + // 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); + + tutorialmap = (INT16)value; + } else deh_warning("Maincfg: unknown word '%s'", word); } @@ -2923,7 +3322,10 @@ static void readwipes(MYFILE *f) // Get the part before the " = " tmp = strchr(s, '='); - *(tmp-1) = '\0'; + if (tmp) + *(tmp-1) = '\0'; + else + break; strupr(word); // Now get the part after @@ -3229,6 +3631,16 @@ static void DEH_LoadDehackedFile(MYFILE *f) ignorelines(f); } } + else if (fastcmp(word, "PROMPT")) + { + if (i > 0 && i < MAX_PROMPTS) + readtextprompt(f, i - 1); + else + { + deh_warning("Prompt number %d out of range (1 - %d)", i, MAX_PROMPTS); + ignorelines(f); + } + } else if (fastcmp(word, "FRAME") || fastcmp(word, "STATE")) { if (i == 0 && word2[0] != '0') // If word2 isn't a number @@ -6952,6 +7364,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s "MT_PULL", "MT_GHOST", "MT_OVERLAY", + "MT_ANGLEMAN", "MT_POLYANCHOR", "MT_POLYSPAWN", "MT_POLYSPAWNCRUSH", @@ -7763,6 +8176,13 @@ struct { // Node flags {"NF_SUBSECTOR",NF_SUBSECTOR}, // Indicate a leaf. #endif +#ifdef ESLOPE + // Slope flags + {"SL_NOPHYSICS",SL_NOPHYSICS}, // Don't do momentum adjustment with this slope + {"SL_NODYNAMIC",SL_NODYNAMIC}, // Slope will never need to move during the level, so don't fuss with recalculating it + {"SL_ANCHORVERTEX",SL_ANCHORVERTEX},// Slope is using a Slope Vertex Thing to anchor its position + {"SL_VERTEXSLOPE",SL_VERTEXSLOPE}, // Slope is built from three Slope Vertex Things +#endif // Angles {"ANG1",ANG1}, @@ -7895,6 +8315,14 @@ struct { {"V_CHARCOLORSHIFT",V_CHARCOLORSHIFT}, {"V_ALPHASHIFT",V_ALPHASHIFT}, + + //Kick Reasons + {"KR_KICK",KR_KICK}, + {"KR_PINGLIMIT",KR_PINGLIMIT}, + {"KR_SYNCH",KR_SYNCH}, + {"KR_TIMEOUT",KR_TIMEOUT}, + {"KR_BAN",KR_BAN}, + {"KR_LEAVE",KR_LEAVE}, #endif {NULL,0} @@ -8283,7 +8711,7 @@ fixed_t get_number(const char *word) #endif } -void FUNCMATH DEH_Check(void) +void DEH_Check(void) { #if defined(_DEBUG) || defined(PARANOIA) const size_t dehstates = sizeof(STATE_LIST)/sizeof(const char*); @@ -8759,6 +9187,9 @@ static inline int lib_getenum(lua_State *L) } else if (fastcmp(word,"maptol")) { lua_pushinteger(L, maptol); return 1; + } else if (fastcmp(word,"ultimatemode")) { + lua_pushboolean(L, ultimatemode != 0); + return 1; } else if (fastcmp(word,"mariomode")) { lua_pushboolean(L, mariomode != 0); return 1; @@ -8888,17 +9319,17 @@ static int lib_getActionName(lua_State *L) { lua_settop(L, 1); // set top of stack to 1 (removing any extra args, which there shouldn't be) // get the name for this action, if possible. - lua_getfield(gL, LUA_REGISTRYINDEX, LREG_ACTIONS); - lua_pushnil(gL); + lua_getfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS); + lua_pushnil(L); // Lua stack at this point: // 1 ... -2 -1 // arg ... LREG_ACTIONS nil - while (lua_next(gL, -2)) + while (lua_next(L, -2)) { // Lua stack at this point: // 1 ... -3 -2 -1 // arg ... LREG_ACTIONS "A_ACTION" function - if (lua_rawequal(gL, -1, 1)) // is this the same as the arg? + if (lua_rawequal(L, -1, 1)) // is this the same as the arg? { // make sure the key (i.e. "A_ACTION") is a string first // (note: we don't use lua_isstring because it also returns true for numbers) @@ -8907,12 +9338,12 @@ static int lib_getActionName(lua_State *L) lua_pushvalue(L, -2); // push "A_ACTION" string to top of stack return 1; } - lua_pop(gL, 2); // pop the name and function + lua_pop(L, 2); // pop the name and function break; // probably should have succeeded but we didn't, so end the loop } - lua_pop(gL, 1); + lua_pop(L, 1); } - lua_pop(gL, 1); // pop LREG_ACTIONS + lua_pop(L, 1); // pop LREG_ACTIONS return 0; // return nothing (don't error) } diff --git a/src/djgppdos/i_sound.c b/src/djgppdos/i_sound.c index 00e0f8c90..4424745c2 100644 --- a/src/djgppdos/i_sound.c +++ b/src/djgppdos/i_sound.c @@ -156,9 +156,11 @@ INT32 I_StartSound ( sfxenum_t id, INT32 vol, INT32 sep, INT32 pitch, - INT32 priority ) + INT32 priority, + INT32 channel) { int voice; + (void)channel; if (sound_disabled) return 0; @@ -548,6 +550,7 @@ void I_ResumeSong (INT32 handle) songpaused = false; } + void I_SetMusicVolume(INT32 volume) { if (midi_disabled) @@ -563,18 +566,6 @@ boolean I_SetSongTrack(INT32 track) return false; } -// Is the song playing? -#if 0 -int I_QrySongPlaying(int handle) -{ - if (midi_disabled) - return 0; - - //return islooping || musicdies > gametic; - return (midi_pos==-1); -} -#endif - /// ------------------------ // MUSIC FADING /// ------------------------ @@ -614,4 +605,14 @@ boolean I_FadeInPlaySong(UINT32 ms, boolean looping) (void)ms; (void)looping; return false; +// Is the song playing? +#if 0 +int I_QrySongPlaying(int handle) +{ + if (midi_disabled) + return 0; + + //return islooping || musicdies > gametic; + return (midi_pos==-1); } +#endif diff --git a/src/doomdef.h b/src/doomdef.h index 51ff25076..c067a71ee 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -573,4 +573,11 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// \note Required for proper collision with moving sloped surfaces that have sector specials on them. #define SECTORSPECIALSAFTERTHINK +/// FINALLY some real clipping that doesn't make walls dissappear AND speeds the game up +/// (that was the original comment from SRB2CB, sadly it is a lie and actually slows game down) +/// on the bright side it fixes some weird issues with translucent walls +/// \note SRB2CB port. +/// SRB2CB itself ported this from PrBoom+ +#define NEWCLIP + #endif // __DOOMDEF__ diff --git a/src/doomstat.h b/src/doomstat.h index 454af9a85..58cbaf64c 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -131,6 +131,14 @@ extern INT16 titlemap; extern boolean hidetitlepics; extern INT16 bootmap; //bootmap for loading a map on startup +extern INT16 tutorialmap; // map to load for tutorial +extern boolean tutorialmode; // are we in a tutorial right now? +extern INT32 tutorialgcs; // which control scheme is loaded? +extern INT32 tutorialusemouse; // store cv_usemouse user value +extern INT32 tutorialfreelook; // store cv_alwaysfreelook user value +extern INT32 tutorialmousemove; // store cv_mousemove user value +extern INT32 tutorialanalog; // store cv_analog user value + extern boolean looptitle; // CTF colors. @@ -169,6 +177,60 @@ typedef struct extern cutscene_t *cutscenes[128]; +// Reserve prompt space for tutorials +#define TUTORIAL_PROMPT 201 // one-based +#define TUTORIAL_AREAS 6 +#define TUTORIAL_AREA_PROMPTS 5 +#define MAX_PROMPTS (TUTORIAL_PROMPT+TUTORIAL_AREAS*TUTORIAL_AREA_PROMPTS*3) // 3 control modes +#define MAX_PAGES 128 + +#define PROMPT_PIC_PERSIST 0 +#define PROMPT_PIC_LOOP 1 +#define PROMPT_PIC_DESTROY 2 +#define MAX_PROMPT_PICS 8 +typedef struct +{ + UINT8 numpics; + UINT8 picmode; // sequence mode after displaying last pic, 0 = persist, 1 = loop, 2 = destroy + UINT8 pictoloop; // if picmode == loop, which pic to loop to? + UINT8 pictostart; // initial pic number to show + char picname[MAX_PROMPT_PICS][8]; + UINT8 pichires[MAX_PROMPT_PICS]; + UINT16 xcoord[MAX_PROMPT_PICS]; // gfx + UINT16 ycoord[MAX_PROMPT_PICS]; // gfx + UINT16 picduration[MAX_PROMPT_PICS]; + + char musswitch[7]; + UINT16 musswitchflags; + UINT8 musicloop; + + char tag[33]; // page tag + char name[34]; // narrator name, extra char for color + char iconname[8]; // narrator icon lump + boolean rightside; // narrator side, false = left, true = right + boolean iconflip; // narrator flip icon horizontally + UINT8 hidehud; // hide hud, 0 = show all, 1 = hide depending on prompt position (top/bottom), 2 = hide all + UINT8 lines; // # of lines to show. If name is specified, name takes one of the lines. If 0, defaults to 4. + INT32 backcolor; // see CON_SetupBackColormap: 0-11, INT32_MAX for user-defined (CONS_BACKCOLOR) + UINT8 align; // text alignment, 0 = left, 1 = right, 2 = center + UINT8 verticalalign; // vertical text alignment, 0 = top, 1 = bottom, 2 = middle + UINT8 textspeed; // text speed, delay in tics between characters. + sfxenum_t textsfx; // sfx_ id for printing text + UINT8 nextprompt; // next prompt to jump to, one-based. 0 = current prompt + UINT8 nextpage; // next page to jump to, one-based. 0 = next page within prompt->numpages + char nexttag[33]; // next tag to jump to. If set, this overrides nextprompt and nextpage. + INT32 timetonext; // time in tics to jump to next page automatically. 0 = don't jump automatically + char *text; +} textpage_t; + +typedef struct +{ + textpage_t page[MAX_PAGES]; + INT32 numpages; // Number of pages in this prompt +} textprompt_t; + +extern textprompt_t *textprompts[MAX_PROMPTS]; + // For the Custom Exit linedef. extern INT16 nextmapoverride; extern boolean skipstats; diff --git a/src/doomtype.h b/src/doomtype.h index b0623de9a..741b68c21 100644 --- a/src/doomtype.h +++ b/src/doomtype.h @@ -40,12 +40,13 @@ typedef long ssize_t; /* Older Visual C++ headers don't have the Win64-compatible typedefs... */ -#if ((_MSC_VER <= 1200) && (!defined(DWORD_PTR))) -#define DWORD_PTR DWORD -#endif - -#if ((_MSC_VER <= 1200) && (!defined(PDWORD_PTR))) -#define PDWORD_PTR PDWORD +#if (_MSC_VER <= 1200) + #ifndef DWORD_PTR + #define DWORD_PTR DWORD + #endif + #ifndef PDWORD_PTR + #define PDWORD_PTR PDWORD + #endif #endif #elif defined (__DJGPP__) #define UINT8 unsigned char @@ -80,11 +81,13 @@ typedef long ssize_t; #define NOIPX #endif +/* Strings and some misc platform specific stuff */ + #ifdef _MSC_VER // Microsoft VisualC++ #if (_MSC_VER <= 1800) // MSVC 2013 and back #define snprintf _snprintf -#if (_MSC_VER <= 1200) // MSVC 2012 and back +#if (_MSC_VER <= 1200) // MSVC 6.0 and back #define vsnprintf _vsnprintf #endif #endif @@ -148,6 +151,8 @@ size_t strlcpy(char *dst, const char *src, size_t siz); // not the number of bytes in the buffer. #define STRBUFCPY(dst,src) strlcpy(dst, src, sizeof dst) +/* Boolean type definition */ + // \note __BYTEBOOL__ used to be set above if "macintosh" was defined, // if macintosh's version of boolean type isn't needed anymore, then isn't this macro pointless now? #ifndef __BYTEBOOL__ @@ -155,7 +160,7 @@ size_t strlcpy(char *dst, const char *src, size_t siz); //faB: clean that up !! #if defined( _MSC_VER) && (_MSC_VER >= 1800) // MSVC 2013 and forward - #include "stdbool.h" + #include "stdbool.h" #elif defined (_WIN32) #define false FALSE // use windows types #define true TRUE @@ -205,89 +210,65 @@ size_t strlcpy(char *dst, const char *src, size_t siz); #define UINT64_MAX 0xffffffffffffffffULL /* 18446744073709551615ULL */ #endif -union FColorRGBA -{ - UINT32 rgba; - struct - { - UINT8 red; - UINT8 green; - UINT8 blue; - UINT8 alpha; - } s; -} ATTRPACK; -typedef union FColorRGBA RGBA_t; - -typedef enum -{ - postimg_none, - postimg_water, - postimg_motion, - postimg_flip, - postimg_heat -} postimg_t; - -typedef UINT32 lumpnum_t; // 16 : 16 unsigned long (wad num: lump num) -#define LUMPERROR UINT32_MAX - -typedef UINT32 tic_t; -#define INFTICS UINT32_MAX - -#ifdef _BIG_ENDIAN -#define UINT2RGBA(a) a -#else -#define UINT2RGBA(a) (UINT32)((a&0xff)<<24)|((a&0xff00)<<8)|((a&0xff0000)>>8)|(((UINT32)a&0xff000000)>>24) -#endif +/* Compiler-specific attributes and other macros */ #ifdef __GNUC__ // __attribute__ ((X)) -#define FUNCNORETURN __attribute__ ((noreturn)) -#if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && defined (__MINGW32__) -#include "inttypes.h" -#if 0 //defined (__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO > 0 -#define FUNCPRINTF __attribute__ ((format(gnu_printf, 1, 2))) -#define FUNCDEBUG __attribute__ ((format(gnu_printf, 2, 3))) -#define FUNCIERROR __attribute__ ((format(gnu_printf, 1, 2),noreturn)) -#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) -#define FUNCPRINTF __attribute__ ((format(ms_printf, 1, 2))) -#define FUNCDEBUG __attribute__ ((format(ms_printf, 2, 3))) -#define FUNCIERROR __attribute__ ((format(ms_printf, 1, 2),noreturn)) -#else -#define FUNCPRINTF __attribute__ ((format(printf, 1, 2))) -#define FUNCDEBUG __attribute__ ((format(printf, 2, 3))) -#define FUNCIERROR __attribute__ ((format(printf, 1, 2),noreturn)) -#endif -#else -#define FUNCPRINTF __attribute__ ((format(printf, 1, 2))) -#define FUNCDEBUG __attribute__ ((format(printf, 2, 3))) -#define FUNCIERROR __attribute__ ((format(printf, 1, 2),noreturn)) -#endif -#ifndef FUNCIERROR -#define FUNCIERROR __attribute__ ((noreturn)) -#endif -#define FUNCMATH __attribute__((const)) -#if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) -#define FUNCDEAD __attribute__ ((deprecated)) -#define FUNCINLINE __attribute__((always_inline)) -#define FUNCNONNULL __attribute__((nonnull)) -#endif -#define FUNCNOINLINE __attribute__((noinline)) -#if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) -#ifdef __i386__ // i386 only -#define FUNCTARGET(X) __attribute__ ((__target__ (X))) -#endif -#endif -#if defined (__MINGW32__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) -#define ATTRPACK __attribute__((packed, gcc_struct)) -#else -#define ATTRPACK __attribute__((packed)) -#endif -#define ATTRUNUSED __attribute__((unused)) + #define FUNCNORETURN __attribute__ ((noreturn)) + + #if ((__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1)) && defined (__MINGW32__) // MinGW, >= GCC 4.1 + #include "inttypes.h" + #if 0 //defined (__USE_MINGW_ANSI_STDIO) && __USE_MINGW_ANSI_STDIO > 0 + #define FUNCPRINTF __attribute__ ((format(gnu_printf, 1, 2))) + #define FUNCDEBUG __attribute__ ((format(gnu_printf, 2, 3))) + #define FUNCIERROR __attribute__ ((format(gnu_printf, 1, 2),noreturn)) + #elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) // >= GCC 4.4 + #define FUNCPRINTF __attribute__ ((format(ms_printf, 1, 2))) + #define FUNCDEBUG __attribute__ ((format(ms_printf, 2, 3))) + #define FUNCIERROR __attribute__ ((format(ms_printf, 1, 2),noreturn)) + #else + #define FUNCPRINTF __attribute__ ((format(printf, 1, 2))) + #define FUNCDEBUG __attribute__ ((format(printf, 2, 3))) + #define FUNCIERROR __attribute__ ((format(printf, 1, 2),noreturn)) + #endif + #else + #define FUNCPRINTF __attribute__ ((format(printf, 1, 2))) + #define FUNCDEBUG __attribute__ ((format(printf, 2, 3))) + #define FUNCIERROR __attribute__ ((format(printf, 1, 2),noreturn)) + #endif + + #ifndef FUNCIERROR + #define FUNCIERROR __attribute__ ((noreturn)) + #endif + + #define FUNCMATH __attribute__((const)) + + #if (__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) // >= GCC 3.1 + #define FUNCDEAD __attribute__ ((deprecated)) + #define FUNCINLINE __attribute__((always_inline)) + #define FUNCNONNULL __attribute__((nonnull)) + #endif + + #define FUNCNOINLINE __attribute__((noinline)) + + #if (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 4) // >= GCC 4.4 + #ifdef __i386__ // i386 only + #define FUNCTARGET(X) __attribute__ ((__target__ (X))) + #endif + #endif + + #if defined (__MINGW32__) && ((__GNUC__ > 3) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)) // MinGW, >= GCC 3.4 + #define ATTRPACK __attribute__((packed, gcc_struct)) + #else + #define ATTRPACK __attribute__((packed)) + #endif + + #define ATTRUNUSED __attribute__((unused)) #elif defined (_MSC_VER) -#define ATTRNORETURN __declspec(noreturn) -#define ATTRINLINE __forceinline -#if _MSC_VER > 1200 -#define ATTRNOINLINE __declspec(noinline) -#endif + #define ATTRNORETURN __declspec(noreturn) + #define ATTRINLINE __forceinline + #if _MSC_VER > 1200 // >= MSVC 6.0 + #define ATTRNOINLINE __declspec(noinline) + #endif #endif #ifndef FUNCPRINTF @@ -335,4 +316,43 @@ typedef UINT32 tic_t; #ifndef ATTRNOINLINE #define ATTRNOINLINE #endif + +/* Miscellaneous types that don't fit anywhere else (Can this be changed?) */ + +union FColorRGBA +{ + UINT32 rgba; + struct + { + UINT8 red; + UINT8 green; + UINT8 blue; + UINT8 alpha; + } s; +} ATTRPACK; +typedef union FColorRGBA RGBA_t; + +typedef enum +{ + postimg_none, + postimg_water, + postimg_motion, + postimg_flip, + postimg_heat +} postimg_t; + +typedef UINT32 lumpnum_t; // 16 : 16 unsigned long (wad num: lump num) +#define LUMPERROR UINT32_MAX + +typedef UINT32 tic_t; +#define INFTICS UINT32_MAX + +#include "endian.h" // This is needed to make sure the below macro acts correctly in big endian builds + +#ifdef SRB2_BIG_ENDIAN +#define UINT2RGBA(a) a +#else +#define UINT2RGBA(a) (UINT32)((a&0xff)<<24)|((a&0xff00)<<8)|((a&0xff0000)>>8)|(((UINT32)a&0xff000000)>>24) +#endif + #endif //__DOOMTYPE__ diff --git a/src/dummy/i_sound.c b/src/dummy/i_sound.c index 89c400631..bb209ddca 100644 --- a/src/dummy/i_sound.c +++ b/src/dummy/i_sound.c @@ -23,13 +23,14 @@ void I_UpdateSound(void){}; // SFX I/O // -INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority) +INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel) { (void)id; (void)vol; (void)sep; (void)pitch; (void)priority; + (void)channel; return -1; } @@ -144,13 +145,13 @@ void I_UnloadSong(void) boolean I_PlaySong(boolean looping) { (void)handle; - (void)looping; - return false; } void I_StopSong(void) { (void)handle; + (void)looping; + return false; } void I_PauseSong(void) @@ -180,7 +181,7 @@ boolean I_SetSongTrack(int track) void I_SetInternalMusicVolume(UINT8 volume) { - (void)volume; + (void)handle; } void I_StopFadingSong(void) diff --git a/src/f_finale.c b/src/f_finale.c index 755a1103a..76eecb378 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -33,6 +33,8 @@ #include "m_cond.h" #include "p_local.h" #include "p_setup.h" +#include "st_stuff.h" // hud hiding +#include "fastcmp.h" #ifdef HAVE_BLUA #include "lua_hud.h" @@ -48,6 +50,7 @@ static INT32 timetonext; // Delay between screen changes static INT32 continuetime; // Short delay when continuing static tic_t animtimer; // Used for some animation timings +static INT16 skullAnimCounter; // Chevron animation static INT32 roidtics; // Asteroid spinning static INT32 deplete; @@ -78,6 +81,18 @@ static patch_t *ttspop7; static void F_SkyScroll(INT32 scrollspeed); +// +// PROMPT STATE +// +static boolean promptactive = false; +static mobj_t *promptmo; +static INT16 promptpostexectag; +static boolean promptblockcontrols; +static char *promptpagetext = NULL; +static INT32 callpromptnum = INT32_MAX; +static INT32 callpagenum = INT32_MAX; +static INT32 callplayer = INT32_MAX; + // // CUTSCENE TEXT WRITING // @@ -1414,6 +1429,7 @@ void F_StartGameEnd(void) // void F_GameEndDrawer(void) { + // this function does nothing } // @@ -1808,7 +1824,7 @@ boolean F_ContinueResponder(event_t *event) // CUSTOM CUTSCENES // ================== static INT32 scenenum, cutnum; -static INT32 picxpos, picypos, picnum, pictime; +static INT32 picxpos, picypos, picnum, pictime, picmode, numpics, pictoloop; static INT32 textxpos, textypos; static boolean dofadenow = false, cutsceneover = false; static boolean runningprecutscene = false, precutresetplayer = false; @@ -2017,3 +2033,617 @@ boolean F_CutsceneResponder(event_t *event) return false; } + +// ================== +// TEXT PROMPTS +// ================== + +static void F_GetPageTextGeometry(UINT8 *pagelines, boolean *rightside, INT32 *boxh, INT32 *texth, INT32 *texty, INT32 *namey, INT32 *chevrony, INT32 *textx, INT32 *textr) +{ + // reuse: + // cutnum -> promptnum + // scenenum -> pagenum + lumpnum_t iconlump = W_CheckNumForName(textprompts[cutnum]->page[scenenum].iconname); + + *pagelines = textprompts[cutnum]->page[scenenum].lines ? textprompts[cutnum]->page[scenenum].lines : 4; + *rightside = (iconlump != LUMPERROR && textprompts[cutnum]->page[scenenum].rightside); + + // Vertical calculations + *boxh = *pagelines*2; + *texth = textprompts[cutnum]->page[scenenum].name[0] ? (*pagelines-1)*2 : *pagelines*2; // name takes up first line if it exists + *texty = BASEVIDHEIGHT - ((*texth * 4) + (*texth/2)*4); + *namey = BASEVIDHEIGHT - ((*boxh * 4) + (*boxh/2)*4); + *chevrony = BASEVIDHEIGHT - (((1*2) * 4) + ((1*2)/2)*4); // force on last line + + // Horizontal calculations + // Shift text to the right if we have a character icon on the left side + // Add 4 margin against icon + *textx = (iconlump != LUMPERROR && !*rightside) ? ((*boxh * 4) + (*boxh/2)*4) + 4 : 4; + *textr = *rightside ? BASEVIDWIDTH - (((*boxh * 4) + (*boxh/2)*4) + 4) : BASEVIDWIDTH-4; +} + +static fixed_t F_GetPromptHideHudBound(void) +{ + UINT8 pagelines; + boolean rightside; + INT32 boxh, texth, texty, namey, chevrony; + INT32 textx, textr; + + if (cutnum == INT32_MAX || scenenum == INT32_MAX || !textprompts[cutnum] || scenenum >= textprompts[cutnum]->numpages || + !textprompts[cutnum]->page[scenenum].hidehud || + (splitscreen && textprompts[cutnum]->page[scenenum].hidehud != 2)) // don't hide on splitscreen, unless hide all is forced + return 0; + else if (textprompts[cutnum]->page[scenenum].hidehud == 2) // hide all + return BASEVIDHEIGHT; + + F_GetPageTextGeometry(&pagelines, &rightside, &boxh, &texth, &texty, &namey, &chevrony, &textx, &textr); + + // calc boxheight (see V_DrawPromptBack) + boxh *= vid.dupy; + boxh = (boxh * 4) + (boxh/2)*5; // 4 lines of space plus gaps between and some leeway + + // return a coordinate to check + // if negative: don't show hud elements below this coordinate (visually) + // if positive: don't show hud elements above this coordinate (visually) + return 0 - boxh; // \todo: if prompt at top of screen (someday), make this return positive +} + +boolean F_GetPromptHideHudAll(void) +{ + if (cutnum == INT32_MAX || scenenum == INT32_MAX || !textprompts[cutnum] || scenenum >= textprompts[cutnum]->numpages || + !textprompts[cutnum]->page[scenenum].hidehud || + (splitscreen && textprompts[cutnum]->page[scenenum].hidehud != 2)) // don't hide on splitscreen, unless hide all is forced + return false; + else if (textprompts[cutnum]->page[scenenum].hidehud == 2) // hide all + return true; + else + return false; +} + +boolean F_GetPromptHideHud(fixed_t y) +{ + INT32 ybound; + boolean fromtop; + fixed_t ytest; + + if (!promptactive) + return false; + + ybound = F_GetPromptHideHudBound(); + fromtop = (ybound >= 0); + ytest = (fromtop ? ybound : BASEVIDHEIGHT + ybound); + + return (fromtop ? y < ytest : y >= ytest); // true means hide +} + +static void F_PreparePageText(char *pagetext) +{ + UINT8 pagelines; + boolean rightside; + INT32 boxh, texth, texty, namey, chevrony; + INT32 textx, textr; + + F_GetPageTextGeometry(&pagelines, &rightside, &boxh, &texth, &texty, &namey, &chevrony, &textx, &textr); + + if (promptpagetext) + Z_Free(promptpagetext); + promptpagetext = (pagetext && pagetext[0]) ? V_WordWrap(textx, textr, 0, pagetext) : Z_StrDup(""); + + F_NewCutscene(promptpagetext); + cutscene_textspeed = textprompts[cutnum]->page[scenenum].textspeed ? textprompts[cutnum]->page[scenenum].textspeed : TICRATE/5; + cutscene_textcount = 0; // no delay in beginning + cutscene_boostspeed = 0; // don't print 8 characters to start + + // \todo update control hot strings on re-config + // and somehow don't reset cutscene text counters +} + +static void F_AdvanceToNextPage(void) +{ + INT32 nextprompt = textprompts[cutnum]->page[scenenum].nextprompt ? textprompts[cutnum]->page[scenenum].nextprompt - 1 : INT32_MAX, + nextpage = textprompts[cutnum]->page[scenenum].nextpage ? textprompts[cutnum]->page[scenenum].nextpage - 1 : INT32_MAX, + oldcutnum = cutnum; + + if (textprompts[cutnum]->page[scenenum].nexttag[0]) + F_GetPromptPageByNamedTag(textprompts[cutnum]->page[scenenum].nexttag, &nextprompt, &nextpage); + + // determine next prompt + if (nextprompt != INT32_MAX) + { + if (nextprompt <= MAX_PROMPTS && textprompts[nextprompt]) + cutnum = nextprompt; + else + cutnum = INT32_MAX; + } + + // determine next page + if (nextpage != INT32_MAX) + { + if (cutnum != INT32_MAX) + { + scenenum = nextpage; + if (scenenum >= MAX_PAGES || scenenum > textprompts[cutnum]->numpages-1) + scenenum = INT32_MAX; + } + } + else + { + if (cutnum != oldcutnum) + scenenum = 0; + else if (scenenum + 1 < MAX_PAGES && scenenum < textprompts[cutnum]->numpages-1) + scenenum++; + else + scenenum = INT32_MAX; + } + + // close the prompt if either num is invalid + if (cutnum == INT32_MAX || scenenum == INT32_MAX) + F_EndTextPrompt(false, false); + else + { + // on page mode, number of tics before allowing boost + // on timer mode, number of tics until page advances + timetonext = textprompts[cutnum]->page[scenenum].timetonext ? textprompts[cutnum]->page[scenenum].timetonext : TICRATE/10; + F_PreparePageText(textprompts[cutnum]->page[scenenum].text); + + // gfx + picnum = textprompts[cutnum]->page[scenenum].pictostart; + numpics = textprompts[cutnum]->page[scenenum].numpics; + picmode = textprompts[cutnum]->page[scenenum].picmode; + pictoloop = textprompts[cutnum]->page[scenenum].pictoloop > 0 ? textprompts[cutnum]->page[scenenum].pictoloop - 1 : 0; + picxpos = textprompts[cutnum]->page[scenenum].xcoord[picnum]; + picypos = textprompts[cutnum]->page[scenenum].ycoord[picnum]; + animtimer = pictime = textprompts[cutnum]->page[scenenum].picduration[picnum]; + + // music change + if (textprompts[cutnum]->page[scenenum].musswitch[0]) + S_ChangeMusic(textprompts[cutnum]->page[scenenum].musswitch, + textprompts[cutnum]->page[scenenum].musswitchflags, + textprompts[cutnum]->page[scenenum].musicloop); + } +} + +void F_EndTextPrompt(boolean forceexec, boolean noexec) +{ + boolean promptwasactive = promptactive; + promptactive = false; + callpromptnum = callpagenum = callplayer = INT32_MAX; + + if (promptwasactive) + { + if (promptmo && promptmo->player && promptblockcontrols) + promptmo->reactiontime = TICRATE/4; // prevent jumping right away // \todo account freeze realtime for this) + // \todo reset frozen realtime? + } + + // \todo net safety, maybe loop all player thinkers? + if ((promptwasactive || forceexec) && !noexec && promptpostexectag) + { + if (tmthing) // edge case where starting an invalid prompt immediately on level load will make P_MapStart fail + P_LinedefExecute(promptpostexectag, promptmo, NULL); + else + { + P_MapStart(); + P_LinedefExecute(promptpostexectag, promptmo, NULL); + P_MapEnd(); + } + } +} + +void F_StartTextPrompt(INT32 promptnum, INT32 pagenum, mobj_t *mo, UINT16 postexectag, boolean blockcontrols, boolean freezerealtime) +{ + INT32 i; + + // if splitscreen and we already have a prompt active, ignore. + // \todo Proper per-player splitscreen support (individual prompts) + if (promptactive && splitscreen && promptnum == callpromptnum && pagenum == callpagenum) + return; + + // \todo proper netgame support + if (netgame) + { + F_EndTextPrompt(true, false); // run the post-effects immediately + return; + } + + // We share vars, so no starting text prompts over cutscenes or title screens! + keypressed = false; + finalecount = 0; + timetonext = 0; + animtimer = 0; + stoptimer = 0; + skullAnimCounter = 0; + + // Set up state + promptmo = mo; + promptpostexectag = postexectag; + promptblockcontrols = blockcontrols; + (void)freezerealtime; // \todo freeze player->realtime, maybe this needs to cycle through player thinkers + + // Initialize current prompt and scene + callpromptnum = promptnum; + callpagenum = pagenum; + cutnum = (promptnum < MAX_PROMPTS && textprompts[promptnum]) ? promptnum : INT32_MAX; + scenenum = (cutnum != INT32_MAX && pagenum < MAX_PAGES && pagenum <= textprompts[cutnum]->numpages-1) ? pagenum : INT32_MAX; + promptactive = (cutnum != INT32_MAX && scenenum != INT32_MAX); + + if (promptactive) + { + // on page mode, number of tics before allowing boost + // on timer mode, number of tics until page advances + timetonext = textprompts[cutnum]->page[scenenum].timetonext ? textprompts[cutnum]->page[scenenum].timetonext : TICRATE/10; + F_PreparePageText(textprompts[cutnum]->page[scenenum].text); + + // gfx + picnum = textprompts[cutnum]->page[scenenum].pictostart; + numpics = textprompts[cutnum]->page[scenenum].numpics; + picmode = textprompts[cutnum]->page[scenenum].picmode; + pictoloop = textprompts[cutnum]->page[scenenum].pictoloop > 0 ? textprompts[cutnum]->page[scenenum].pictoloop - 1 : 0; + picxpos = textprompts[cutnum]->page[scenenum].xcoord[picnum]; + picypos = textprompts[cutnum]->page[scenenum].ycoord[picnum]; + animtimer = pictime = textprompts[cutnum]->page[scenenum].picduration[picnum]; + + // music change + if (textprompts[cutnum]->page[scenenum].musswitch[0]) + S_ChangeMusic(textprompts[cutnum]->page[scenenum].musswitch, + textprompts[cutnum]->page[scenenum].musswitchflags, + textprompts[cutnum]->page[scenenum].musicloop); + + // get the calling player + if (promptblockcontrols && mo && mo->player) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (players[i].mo == mo) + { + callplayer = i; + break; + } + } + } + } + else + F_EndTextPrompt(true, false); // run the post-effects immediately +} + +static boolean F_GetTextPromptTutorialTag(char *tag, INT32 length) +{ + INT32 gcs = gcs_custom; + boolean suffixed = true; + + if (!tag || !tag[0] || !tutorialmode) + return false; + + if (!strncmp(tag, "TAM", 3)) // Movement + gcs = G_GetControlScheme(gamecontrol, gcl_movement, num_gcl_movement); + else if (!strncmp(tag, "TAC", 3)) // Camera + { + // Check for gcl_movement so we can differentiate between FPS and Platform schemes. + gcs = G_GetControlScheme(gamecontrol, gcl_movement, num_gcl_movement); + if (gcs == gcs_custom) // try again, maybe we'll get a match + gcs = G_GetControlScheme(gamecontrol, gcl_camera, num_gcl_camera); + if (gcs == gcs_fps && !cv_usemouse.value) + gcs = gcs_platform; // Platform (arrow) scheme is stand-in for no mouse + } + else if (!strncmp(tag, "TAD", 3)) // Movement and Camera + gcs = G_GetControlScheme(gamecontrol, gcl_movement_camera, num_gcl_movement_camera); + else if (!strncmp(tag, "TAJ", 3)) // Jump + gcs = G_GetControlScheme(gamecontrol, gcl_jump, num_gcl_jump); + else if (!strncmp(tag, "TAS", 3)) // Spin + gcs = G_GetControlScheme(gamecontrol, gcl_use, num_gcl_use); + else if (!strncmp(tag, "TAA", 3)) // Char ability + gcs = G_GetControlScheme(gamecontrol, gcl_jump, num_gcl_jump); + else if (!strncmp(tag, "TAW", 3)) // Shield ability + gcs = G_GetControlScheme(gamecontrol, gcl_jump_use, num_gcl_jump_use); + else + gcs = G_GetControlScheme(gamecontrol, gcl_tutorial_used, num_gcl_tutorial_used); + + switch (gcs) + { + case gcs_fps: + // strncat(tag, "FPS", length); + suffixed = false; + break; + + case gcs_platform: + strncat(tag, "PLATFORM", length); + break; + + default: + strncat(tag, "CUSTOM", length); + break; + } + + return suffixed; +} + +void F_GetPromptPageByNamedTag(const char *tag, INT32 *promptnum, INT32 *pagenum) +{ + INT32 nosuffixpromptnum = INT32_MAX, nosuffixpagenum = INT32_MAX; + INT32 tutorialpromptnum = (tutorialmode) ? TUTORIAL_PROMPT-1 : 0; + boolean suffixed = false, found = false; + char suffixedtag[33]; + + *promptnum = *pagenum = INT32_MAX; + + if (!tag || !tag[0]) + return; + + strncpy(suffixedtag, tag, 33); + suffixedtag[32] = 0; + + if (tutorialmode) + suffixed = F_GetTextPromptTutorialTag(suffixedtag, 33); + + for (*promptnum = 0 + tutorialpromptnum; *promptnum < MAX_PROMPTS; (*promptnum)++) + { + if (!textprompts[*promptnum]) + continue; + + for (*pagenum = 0; *pagenum < textprompts[*promptnum]->numpages && *pagenum < MAX_PAGES; (*pagenum)++) + { + if (suffixed && fastcmp(suffixedtag, textprompts[*promptnum]->page[*pagenum].tag)) + { + // this goes first because fastcmp ends early if first string is shorter + found = true; + break; + } + else if (nosuffixpromptnum == INT32_MAX && nosuffixpagenum == INT32_MAX && fastcmp(tag, textprompts[*promptnum]->page[*pagenum].tag)) + { + if (suffixed) + { + nosuffixpromptnum = *promptnum; + nosuffixpagenum = *pagenum; + // continue searching for the suffixed tag + } + else + { + found = true; + break; + } + } + } + + if (found) + break; + } + + if (suffixed && !found && nosuffixpromptnum != INT32_MAX && nosuffixpagenum != INT32_MAX) + { + found = true; + *promptnum = nosuffixpromptnum; + *pagenum = nosuffixpagenum; + } + + if (!found) + CONS_Debug(DBG_GAMELOGIC, "Text prompt: Can't find a page with named tag %s or suffixed tag %s\n", tag, suffixedtag); +} + +void F_TextPromptDrawer(void) +{ + // reuse: + // cutnum -> promptnum + // scenenum -> pagenum + lumpnum_t iconlump; + UINT8 pagelines; + boolean rightside; + INT32 boxh, texth, texty, namey, chevrony; + INT32 textx, textr; + + // Data + patch_t *patch; + + if (!promptactive) + return; + + iconlump = W_CheckNumForName(textprompts[cutnum]->page[scenenum].iconname); + F_GetPageTextGeometry(&pagelines, &rightside, &boxh, &texth, &texty, &namey, &chevrony, &textx, &textr); + + // Draw gfx first + if (picnum >= 0 && picnum < numpics && textprompts[cutnum]->page[scenenum].picname[picnum][0] != '\0') + { + if (textprompts[cutnum]->page[scenenum].pichires[picnum]) + V_DrawSmallScaledPatch(picxpos, picypos, 0, + W_CachePatchName(textprompts[cutnum]->page[scenenum].picname[picnum], PU_CACHE)); + else + V_DrawScaledPatch(picxpos,picypos, 0, + W_CachePatchName(textprompts[cutnum]->page[scenenum].picname[picnum], PU_CACHE)); + } + + // Draw background + V_DrawPromptBack(boxh, textprompts[cutnum]->page[scenenum].backcolor); + + // Draw narrator icon + if (iconlump != LUMPERROR) + { + INT32 iconx, icony, scale, scaledsize; + patch = W_CachePatchName(textprompts[cutnum]->page[scenenum].iconname, PU_CACHE); + + // scale and center + if (patch->width > patch->height) + { + scale = FixedDiv(((boxh * 4) + (boxh/2)*4) - 4, patch->width); + scaledsize = FixedMul(patch->height, scale); + iconx = (rightside ? BASEVIDWIDTH - (((boxh * 4) + (boxh/2)*4)) : 4) << FRACBITS; + icony = ((namey-4) << FRACBITS) + FixedDiv(BASEVIDHEIGHT - namey + 4 - scaledsize, 2); // account for 4 margin + } + else if (patch->height > patch->width) + { + scale = FixedDiv(((boxh * 4) + (boxh/2)*4) - 4, patch->height); + scaledsize = FixedMul(patch->width, scale); + iconx = (rightside ? BASEVIDWIDTH - (((boxh * 4) + (boxh/2)*4)) : 4) << FRACBITS; + icony = namey << FRACBITS; + iconx += FixedDiv(FixedMul(patch->height, scale) - scaledsize, 2); + } + else + { + scale = FixedDiv(((boxh * 4) + (boxh/2)*4) - 4, patch->width); + iconx = (rightside ? BASEVIDWIDTH - (((boxh * 4) + (boxh/2)*4)) : 4) << FRACBITS; + icony = namey << FRACBITS; + } + + if (textprompts[cutnum]->page[scenenum].iconflip) + iconx += FixedMul(patch->width, scale) << FRACBITS; + + V_DrawFixedPatch(iconx, icony, scale, (V_SNAPTOBOTTOM|(textprompts[cutnum]->page[scenenum].iconflip ? V_FLIP : 0)), patch, NULL); + W_UnlockCachedPatch(patch); + } + + // Draw text + V_DrawString(textx, texty, (V_SNAPTOBOTTOM|V_ALLOWLOWERCASE), cutscene_disptext); + + // Draw name + // Don't use V_YELLOWMAP here so that the name color can be changed with control codes + if (textprompts[cutnum]->page[scenenum].name[0]) + V_DrawString(textx, namey, (V_SNAPTOBOTTOM|V_ALLOWLOWERCASE), textprompts[cutnum]->page[scenenum].name); + + // Draw chevron + if (promptblockcontrols && !timetonext) + V_DrawString(textr-8, chevrony + (skullAnimCounter/5), (V_SNAPTOBOTTOM|V_YELLOWMAP), "\x1B"); // down arrow +} + +void F_TextPromptTicker(void) +{ + INT32 i; + + if (!promptactive || paused || P_AutoPause()) + return; + + // advance animation + finalecount++; + cutscene_boostspeed = 0; + + // for the chevron + if (--skullAnimCounter <= 0) + skullAnimCounter = 8; + + // button handling + if (textprompts[cutnum]->page[scenenum].timetonext) + { + if (promptblockcontrols) // same procedure as below, just without the button handling + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (netgame && i != serverplayer && i != adminplayer) + continue; + else if (splitscreen) { + // Both players' controls are locked, + // But only consoleplayer can advance the prompt. + // \todo Proper per-player splitscreen support (individual prompts) + if (i == consoleplayer || i == secondarydisplayplayer) + players[i].powers[pw_nocontrol] = 1; + } + else if (i == consoleplayer) + players[i].powers[pw_nocontrol] = 1; + + if (!splitscreen) + break; + } + } + + if (timetonext >= 1) + timetonext--; + + if (!timetonext) + F_AdvanceToNextPage(); + + F_WriteText(); + } + else + { + if (promptblockcontrols) + { + for (i = 0; i < MAXPLAYERS; i++) + { + if (netgame && i != serverplayer && i != adminplayer) + continue; + else if (splitscreen) { + // Both players' controls are locked, + // But only the triggering player can advance the prompt. + if (i == consoleplayer || i == secondarydisplayplayer) + { + players[i].powers[pw_nocontrol] = 1; + + if (callplayer == consoleplayer || callplayer == secondarydisplayplayer) + { + if (i != callplayer) + continue; + } + else if (i != consoleplayer) + continue; + } + else + continue; + } + else if (i == consoleplayer) + players[i].powers[pw_nocontrol] = 1; + else + continue; + + if ((players[i].cmd.buttons & BT_USE) || (players[i].cmd.buttons & BT_JUMP)) + { + if (timetonext > 1) + timetonext--; + else if (cutscene_baseptr) // don't set boost if we just reset the string + cutscene_boostspeed = 1; // only after a slight delay + + if (keypressed) + { + if (!splitscreen) + break; + else + continue; + } + + if (!timetonext) // is 0 when finished generating text + { + F_AdvanceToNextPage(); + if (promptactive) + S_StartSound(NULL, sfx_menu1); + } + keypressed = true; // prevent repeat events + } + else if (!(players[i].cmd.buttons & BT_USE) && !(players[i].cmd.buttons & BT_JUMP)) + keypressed = false; + + if (!splitscreen) + break; + } + } + + // generate letter-by-letter text + if (scenenum >= MAX_PAGES || + !textprompts[cutnum]->page[scenenum].text || + !textprompts[cutnum]->page[scenenum].text[0] || + !F_WriteText()) + timetonext = !promptblockcontrols; // never show the chevron if we can't toggle pages + } + + // gfx + if (picnum >= 0 && picnum < numpics) + { + if (animtimer <= 0) + { + boolean persistanimtimer = false; + + if (picnum < numpics-1 && textprompts[cutnum]->page[scenenum].picname[picnum+1][0] != '\0') + picnum++; + else if (picmode == PROMPT_PIC_LOOP) + picnum = pictoloop; + else if (picmode == PROMPT_PIC_DESTROY) + picnum = -1; + else // if (picmode == PROMPT_PIC_PERSIST) + persistanimtimer = true; + + if (!persistanimtimer && picnum >= 0) + { + picxpos = textprompts[cutnum]->page[scenenum].xcoord[picnum]; + picypos = textprompts[cutnum]->page[scenenum].ycoord[picnum]; + pictime = textprompts[cutnum]->page[scenenum].picduration[picnum]; + animtimer = pictime; + } + } + else + animtimer--; + } +} diff --git a/src/f_finale.h b/src/f_finale.h index aadc64ad0..8e8a06365 100644 --- a/src/f_finale.h +++ b/src/f_finale.h @@ -17,6 +17,7 @@ #include "doomtype.h" #include "d_event.h" +#include "p_mobj.h" // // FINALE @@ -33,9 +34,10 @@ void F_IntroTicker(void); void F_TitleScreenTicker(boolean run); void F_CutsceneTicker(void); void F_TitleDemoTicker(void); +void F_TextPromptTicker(void); // Called by main loop. -FUNCMATH void F_GameEndDrawer(void); +void F_GameEndDrawer(void); void F_IntroDrawer(void); void F_TitleScreenDrawer(void); @@ -50,6 +52,13 @@ void F_StartCustomCutscene(INT32 cutscenenum, boolean precutscene, boolean reset void F_CutsceneDrawer(void); void F_EndCutScene(void); +void F_StartTextPrompt(INT32 promptnum, INT32 pagenum, mobj_t *mo, UINT16 postexectag, boolean blockcontrols, boolean freezerealtime); +void F_GetPromptPageByNamedTag(const char *tag, INT32 *promptnum, INT32 *pagenum); +void F_TextPromptDrawer(void); +void F_EndTextPrompt(boolean forceexec, boolean noexec); +boolean F_GetPromptHideHudAll(void); +boolean F_GetPromptHideHud(fixed_t y); + void F_StartGameEnd(void); void F_StartIntro(void); void F_StartTitleScreen(void); diff --git a/src/g_game.c b/src/g_game.c index 3b0656c7a..67780e303 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -128,6 +128,14 @@ INT16 titlemap = 0; boolean hidetitlepics = false; INT16 bootmap; //bootmap for loading a map on startup +INT16 tutorialmap = 0; // map to load for tutorial +boolean tutorialmode = false; // are we in a tutorial right now? +INT32 tutorialgcs = gcs_custom; // which control scheme is loaded? +INT32 tutorialusemouse = 0; // store cv_usemouse user value +INT32 tutorialfreelook = 0; // store cv_alwaysfreelook user value +INT32 tutorialmousemove = 0; // store cv_mousemove user value +INT32 tutorialanalog = 0; // store cv_analog user value + boolean looptitle = false; UINT8 skincolor_redteam = SKINCOLOR_RED; @@ -139,6 +147,7 @@ tic_t countdowntimer = 0; boolean countdowntimeup = false; cutscene_t *cutscenes[128]; +textprompt_t *textprompts[MAX_PROMPTS]; INT16 nextmapoverride; boolean skipstats; @@ -1934,6 +1943,7 @@ void G_Ticker(boolean run) F_TitleDemoTicker(); P_Ticker(run); // tic the game ST_Ticker(); + F_TextPromptTicker(); AM_Ticker(); HU_Ticker(); break; diff --git a/src/g_game.h b/src/g_game.h index d6b41830e..9e8580d13 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -55,6 +55,7 @@ extern tic_t timeinmap; // Ticker for time spent in level (used for levelcard di extern INT16 rw_maximums[NUM_WEAPONS]; // used in game menu +extern consvar_t cv_tutorialprompt; extern consvar_t cv_crosshair, cv_crosshair2; extern consvar_t cv_invertmouse, cv_alwaysfreelook, cv_mousemove; extern consvar_t cv_invertmouse2, cv_alwaysfreelook2, cv_mousemove2; diff --git a/src/g_input.c b/src/g_input.c index 67aaf4179..4f7296cd5 100644 --- a/src/g_input.c +++ b/src/g_input.c @@ -45,6 +45,47 @@ UINT8 gamekeydown[NUMINPUTS]; // two key codes (or virtual key) per game control INT32 gamecontrol[num_gamecontrols][2]; INT32 gamecontrolbis[num_gamecontrols][2]; // secondary splitscreen player +INT32 gamecontroldefault[num_gamecontrolschemes][num_gamecontrols][2]; // default control storage, use 0 (gcs_custom) for memory retention + +// lists of GC codes for selective operation +const INT32 gcl_tutorial_check[num_gcl_tutorial_check] = { + gc_forward, gc_backward, gc_strafeleft, gc_straferight, + gc_turnleft, gc_turnright +}; + +const INT32 gcl_tutorial_used[num_gcl_tutorial_used] = { + gc_forward, gc_backward, gc_strafeleft, gc_straferight, + gc_turnleft, gc_turnright, + gc_jump, gc_use +}; + +const INT32 gcl_tutorial_full[num_gcl_tutorial_full] = { + gc_forward, gc_backward, gc_strafeleft, gc_straferight, + gc_lookup, gc_lookdown, gc_turnleft, gc_turnright, gc_centerview, + gc_jump, gc_use, + gc_fire, gc_firenormal +}; + +const INT32 gcl_movement[num_gcl_movement] = { + gc_forward, gc_backward, gc_strafeleft, gc_straferight +}; + +const INT32 gcl_camera[num_gcl_camera] = { + gc_turnleft, gc_turnright +}; + +const INT32 gcl_movement_camera[num_gcl_movement_camera] = { + gc_forward, gc_backward, gc_strafeleft, gc_straferight, + gc_turnleft, gc_turnright +}; + +const INT32 gcl_jump[num_gcl_jump] = { gc_jump }; + +const INT32 gcl_use[num_gcl_use] = { gc_use }; + +const INT32 gcl_jump_use[num_gcl_jump_use] = { + gc_jump, gc_use +}; typedef struct { @@ -611,55 +652,117 @@ INT32 G_KeyStringtoNum(const char *keystr) return 0; } -void G_Controldefault(void) +void G_DefineDefaultControls(void) { - gamecontrol[gc_forward ][0] = 'w'; - gamecontrol[gc_backward ][0] = 's'; - gamecontrol[gc_strafeleft ][0] = 'a'; - gamecontrol[gc_straferight][0] = 'd'; - gamecontrol[gc_turnleft ][0] = KEY_LEFTARROW; - gamecontrol[gc_turnright ][0] = KEY_RIGHTARROW; - gamecontrol[gc_weaponnext ][0] = 'e'; - gamecontrol[gc_weaponprev ][0] = 'q'; - gamecontrol[gc_wepslot1 ][0] = '1'; - gamecontrol[gc_wepslot2 ][0] = '2'; - gamecontrol[gc_wepslot3 ][0] = '3'; - gamecontrol[gc_wepslot4 ][0] = '4'; - gamecontrol[gc_wepslot5 ][0] = '5'; - gamecontrol[gc_wepslot6 ][0] = '6'; - gamecontrol[gc_wepslot7 ][0] = '7'; - gamecontrol[gc_wepslot8 ][0] = '8'; - gamecontrol[gc_wepslot9 ][0] = '9'; - gamecontrol[gc_wepslot10 ][0] = '0'; - gamecontrol[gc_fire ][0] = KEY_RCTRL; - gamecontrol[gc_fire ][1] = KEY_MOUSE1+0; - gamecontrol[gc_firenormal ][0] = 'c'; - gamecontrol[gc_tossflag ][0] = '\''; - gamecontrol[gc_use ][0] = KEY_LSHIFT; - gamecontrol[gc_camtoggle ][0] = 'v'; - gamecontrol[gc_camreset ][0] = 'r'; - gamecontrol[gc_lookup ][0] = KEY_UPARROW; - gamecontrol[gc_lookdown ][0] = KEY_DOWNARROW; - gamecontrol[gc_centerview ][0] = KEY_END; - gamecontrol[gc_talkkey ][0] = 't'; - gamecontrol[gc_teamkey ][0] = 'y'; - gamecontrol[gc_scores ][0] = KEY_TAB; - gamecontrol[gc_jump ][0] = KEY_SPACE; - gamecontrol[gc_console ][0] = KEY_CONSOLE; - gamecontrol[gc_pause ][0] = KEY_PAUSE; + INT32 i; + + // FPS game controls (WASD) + gamecontroldefault[gcs_fps][gc_forward ][0] = 'w'; + gamecontroldefault[gcs_fps][gc_backward ][0] = 's'; + gamecontroldefault[gcs_fps][gc_strafeleft ][0] = 'a'; + gamecontroldefault[gcs_fps][gc_straferight][0] = 'd'; + gamecontroldefault[gcs_fps][gc_lookup ][0] = KEY_UPARROW; + gamecontroldefault[gcs_fps][gc_lookdown ][0] = KEY_DOWNARROW; + gamecontroldefault[gcs_fps][gc_turnleft ][0] = KEY_LEFTARROW; + gamecontroldefault[gcs_fps][gc_turnright ][0] = KEY_RIGHTARROW; + gamecontroldefault[gcs_fps][gc_centerview ][0] = KEY_END; + gamecontroldefault[gcs_fps][gc_jump ][0] = KEY_SPACE; + gamecontroldefault[gcs_fps][gc_use ][0] = KEY_LSHIFT; + gamecontroldefault[gcs_fps][gc_fire ][0] = KEY_RCTRL; + gamecontroldefault[gcs_fps][gc_fire ][1] = KEY_MOUSE1+0; + gamecontroldefault[gcs_fps][gc_firenormal ][0] = 'c'; + + // Platform game controls (arrow keys) + gamecontroldefault[gcs_platform][gc_forward ][0] = KEY_UPARROW; + gamecontroldefault[gcs_platform][gc_backward ][0] = KEY_DOWNARROW; + gamecontroldefault[gcs_platform][gc_strafeleft ][0] = 'a'; + gamecontroldefault[gcs_platform][gc_straferight][0] = 'd'; + gamecontroldefault[gcs_platform][gc_lookup ][0] = KEY_PGUP; + gamecontroldefault[gcs_platform][gc_lookdown ][0] = KEY_PGDN; + gamecontroldefault[gcs_platform][gc_turnleft ][0] = KEY_LEFTARROW; + gamecontroldefault[gcs_platform][gc_turnright ][0] = KEY_RIGHTARROW; + gamecontroldefault[gcs_platform][gc_centerview ][0] = KEY_END; + gamecontroldefault[gcs_platform][gc_jump ][0] = KEY_SPACE; + gamecontroldefault[gcs_platform][gc_use ][0] = KEY_LSHIFT; + gamecontroldefault[gcs_platform][gc_fire ][0] = 's'; + gamecontroldefault[gcs_platform][gc_fire ][1] = KEY_MOUSE1+0; + gamecontroldefault[gcs_platform][gc_firenormal ][0] = 'w'; + + for (i = 1; i < num_gamecontrolschemes; i++) // skip gcs_custom (0) + { + gamecontroldefault[i][gc_weaponnext ][0] = 'e'; + gamecontroldefault[i][gc_weaponprev ][0] = 'q'; + gamecontroldefault[i][gc_wepslot1 ][0] = '1'; + gamecontroldefault[i][gc_wepslot2 ][0] = '2'; + gamecontroldefault[i][gc_wepslot3 ][0] = '3'; + gamecontroldefault[i][gc_wepslot4 ][0] = '4'; + gamecontroldefault[i][gc_wepslot5 ][0] = '5'; + gamecontroldefault[i][gc_wepslot6 ][0] = '6'; + gamecontroldefault[i][gc_wepslot7 ][0] = '7'; + gamecontroldefault[i][gc_wepslot8 ][0] = '8'; + gamecontroldefault[i][gc_wepslot9 ][0] = '9'; + gamecontroldefault[i][gc_wepslot10 ][0] = '0'; + gamecontroldefault[i][gc_tossflag ][0] = '\''; + gamecontroldefault[i][gc_camtoggle ][0] = 'v'; + gamecontroldefault[i][gc_camreset ][0] = 'r'; + gamecontroldefault[i][gc_talkkey ][0] = 't'; + gamecontroldefault[i][gc_teamkey ][0] = 'y'; + gamecontroldefault[i][gc_scores ][0] = KEY_TAB; + gamecontroldefault[i][gc_console ][0] = KEY_CONSOLE; + gamecontroldefault[i][gc_pause ][0] = KEY_PAUSE; + } } -void G_SaveKeySetting(FILE *f) +INT32 G_GetControlScheme(INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen) +{ + INT32 i, j, gc; + boolean skipscheme; + + for (i = 1; i < num_gamecontrolschemes; i++) // skip gcs_custom (0) + { + skipscheme = false; + for (j = 0; j < (gclist && gclen ? gclen : num_gamecontrols); j++) + { + gc = (gclist && gclen) ? gclist[j] : j; + if (((fromcontrols[gc][0] && gamecontroldefault[i][gc][0]) ? fromcontrols[gc][0] != gamecontroldefault[i][gc][0] : true) && + ((fromcontrols[gc][0] && gamecontroldefault[i][gc][1]) ? fromcontrols[gc][0] != gamecontroldefault[i][gc][1] : true) && + ((fromcontrols[gc][1] && gamecontroldefault[i][gc][0]) ? fromcontrols[gc][1] != gamecontroldefault[i][gc][0] : true) && + ((fromcontrols[gc][1] && gamecontroldefault[i][gc][1]) ? fromcontrols[gc][1] != gamecontroldefault[i][gc][1] : true)) + { + skipscheme = true; + break; + } + } + if (!skipscheme) + return i; + } + + return gcs_custom; +} + +void G_CopyControls(INT32 (*setupcontrols)[2], INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen) +{ + INT32 i, gc; + + for (i = 0; i < (gclist && gclen ? gclen : num_gamecontrols); i++) + { + gc = (gclist && gclen) ? gclist[i] : i; + setupcontrols[gc][0] = fromcontrols[gc][0]; + setupcontrols[gc][1] = fromcontrols[gc][1]; + } +} + +void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis)[2]) { INT32 i; for (i = 1; i < num_gamecontrols; i++) { fprintf(f, "setcontrol \"%s\" \"%s\"", gamecontrolname[i], - G_KeynumToString(gamecontrol[i][0])); + G_KeynumToString(fromcontrols[i][0])); - if (gamecontrol[i][1]) - fprintf(f, " \"%s\"\n", G_KeynumToString(gamecontrol[i][1])); + if (fromcontrols[i][1]) + fprintf(f, " \"%s\"\n", G_KeynumToString(fromcontrols[i][1])); else fprintf(f, "\n"); } @@ -667,10 +770,10 @@ void G_SaveKeySetting(FILE *f) for (i = 1; i < num_gamecontrols; i++) { fprintf(f, "setcontrol2 \"%s\" \"%s\"", gamecontrolname[i], - G_KeynumToString(gamecontrolbis[i][0])); + G_KeynumToString(fromcontrolsbis[i][0])); - if (gamecontrolbis[i][1]) - fprintf(f, " \"%s\"\n", G_KeynumToString(gamecontrolbis[i][1])); + if (fromcontrolsbis[i][1]) + fprintf(f, " \"%s\"\n", G_KeynumToString(fromcontrolsbis[i][1])); else fprintf(f, "\n"); } diff --git a/src/g_input.h b/src/g_input.h index 2a447c683..4bf67ebd8 100644 --- a/src/g_input.h +++ b/src/g_input.h @@ -99,6 +99,14 @@ typedef enum num_gamecontrols } gamecontrols_e; +typedef enum +{ + gcs_custom, + gcs_fps, + gcs_platform, + num_gamecontrolschemes +} gamecontrolschemes_e; + // mouse values are used once extern consvar_t cv_mousesens, cv_mouseysens; extern consvar_t cv_mousesens2, cv_mouseysens2; @@ -116,9 +124,30 @@ extern UINT8 gamekeydown[NUMINPUTS]; // two key codes (or virtual key) per game control extern INT32 gamecontrol[num_gamecontrols][2]; extern INT32 gamecontrolbis[num_gamecontrols][2]; // secondary splitscreen player +extern INT32 gamecontroldefault[num_gamecontrolschemes][num_gamecontrols][2]; // default control storage, use 0 (gcs_custom) for memory retention #define PLAYER1INPUTDOWN(gc) (gamekeydown[gamecontrol[gc][0]] || gamekeydown[gamecontrol[gc][1]]) #define PLAYER2INPUTDOWN(gc) (gamekeydown[gamecontrolbis[gc][0]] || gamekeydown[gamecontrolbis[gc][1]]) +#define num_gcl_tutorial_check 6 +#define num_gcl_tutorial_used 8 +#define num_gcl_tutorial_full 13 +#define num_gcl_movement 4 +#define num_gcl_camera 2 +#define num_gcl_movement_camera 6 +#define num_gcl_jump 1 +#define num_gcl_use 1 +#define num_gcl_jump_use 2 + +extern const INT32 gcl_tutorial_check[num_gcl_tutorial_check]; +extern const INT32 gcl_tutorial_used[num_gcl_tutorial_used]; +extern const INT32 gcl_tutorial_full[num_gcl_tutorial_full]; +extern const INT32 gcl_movement[num_gcl_movement]; +extern const INT32 gcl_camera[num_gcl_camera]; +extern const INT32 gcl_movement_camera[num_gcl_movement_camera]; +extern const INT32 gcl_jump[num_gcl_jump]; +extern const INT32 gcl_use[num_gcl_use]; +extern const INT32 gcl_jump_use[num_gcl_jump_use]; + // peace to my little coder fingers! // check a gamecontrol being active or not @@ -133,8 +162,10 @@ INT32 G_KeyStringtoNum(const char *keystr); void G_ClearControlKeys(INT32 (*setupcontrols)[2], INT32 control); void Command_Setcontrol_f(void); void Command_Setcontrol2_f(void); -void G_Controldefault(void); -void G_SaveKeySetting(FILE *f); +void G_DefineDefaultControls(void); +INT32 G_GetControlScheme(INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen); +void G_CopyControls(INT32 (*setupcontrols)[2], INT32 (*fromcontrols)[2], const INT32 *gclist, INT32 gclen); +void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis)[2]); void G_CheckDoubleUsage(INT32 keynum); #endif diff --git a/src/hardware/hw_clip.c b/src/hardware/hw_clip.c index fd6ba13f3..6d120efe7 100644 --- a/src/hardware/hw_clip.c +++ b/src/hardware/hw_clip.c @@ -338,7 +338,7 @@ angle_t gld_FrustumAngle(void) } // If the pitch is larger than this you can look all around at a FOV of 90 - if (aimingangle > (ANGLE_45+ANG1) && (ANGLE_315-ANG1) > aimingangle) + if (abs((signed)aimingangle) > 46 * ANG1) return 0xffffffff; // ok, this is a gross hack that barely works... diff --git a/src/hardware/hw_data.h b/src/hardware/hw_data.h index f6bbf9455..44929dd67 100644 --- a/src/hardware/hw_data.h +++ b/src/hardware/hw_data.h @@ -26,10 +26,6 @@ #include #endif -#if defined (VID_X11) && !defined (HAVE_SDL) -#include -#endif - #include "../doomdef.h" //THIS MUST DISAPPEAR!!! #include "hw_glide.h" diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index 02608892e..4efd49817 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -114,10 +114,10 @@ void HWR_DrawPatch(GLPatch_t *gpatch, INT32 x, INT32 y, INT32 option) if (option & V_NOSCALESTART) sdupx = sdupy = 2.0f; - v[0].x = v[3].x = (x*sdupx-gpatch->leftoffset*pdupx)/vid.width - 1; - v[2].x = v[1].x = (x*sdupx+(gpatch->width-gpatch->leftoffset)*pdupx)/vid.width - 1; - v[0].y = v[1].y = 1-(y*sdupy-gpatch->topoffset*pdupy)/vid.height; - v[2].y = v[3].y = 1-(y*sdupy+(gpatch->height-gpatch->topoffset)*pdupy)/vid.height; + v[0].x = v[3].x = (x*sdupx-SHORT(gpatch->leftoffset)*pdupx)/vid.width - 1; + v[2].x = v[1].x = (x*sdupx+(SHORT(gpatch->width)-SHORT(gpatch->leftoffset))*pdupx)/vid.width - 1; + v[0].y = v[1].y = 1-(y*sdupy-SHORT(gpatch->topoffset)*pdupy)/vid.height; + v[2].y = v[3].y = 1-(y*sdupy+(SHORT(gpatch->height)-SHORT(gpatch->topoffset))*pdupy)/vid.height; v[0].z = v[1].z = v[2].z = v[3].z = 1.0f; @@ -183,18 +183,29 @@ void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, dupx = dupy = (dupx < dupy ? dupx : dupy); fscalew = fscaleh = FIXED_TO_FLOAT(pscale); - if (option & V_OFFSET) + // See my comments in v_video.c's V_DrawFixedPatch + // -- Monster Iestyn 29/10/18 { - cx -= (float)gpatch->leftoffset * dupx * fscalew; - cy -= (float)gpatch->topoffset * dupy * fscaleh; - } - else - { - cy -= (float)gpatch->topoffset * fscaleh; + float offsetx = 0.0f, offsety = 0.0f; + + // left offset if (option & V_FLIP) - cx -= ((float)gpatch->width - (float)gpatch->leftoffset) * fscalew; + offsetx = (float)(SHORT(gpatch->width) - SHORT(gpatch->leftoffset)) * fscalew; else - cx -= (float)gpatch->leftoffset * fscalew; + offsetx = (float)SHORT(gpatch->leftoffset) * fscalew; + + // top offset + // TODO: make some kind of vertical version of V_FLIP, maybe by deprecating V_OFFSET in future?!? + offsety = (float)SHORT(gpatch->topoffset) * fscaleh; + + if ((option & (V_NOSCALESTART|V_OFFSET)) == (V_NOSCALESTART|V_OFFSET)) // Multiply by dupx/dupy for crosshairs + { + offsetx *= dupx; + offsety *= dupy; + } + + cx -= offsetx; + cy -= offsety; } if (splitscreen && (option & V_PERPLAYER)) @@ -312,13 +323,13 @@ void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, if (pscale != FRACUNIT || (splitscreen && option & V_PERPLAYER)) { - fwidth = (float)gpatch->width * fscalew * dupx; - fheight = (float)gpatch->height * fscaleh * dupy; + fwidth = (float)SHORT(gpatch->width) * fscalew * dupx; + fheight = (float)SHORT(gpatch->height) * fscaleh * dupy; } else { - fwidth = (float)gpatch->width * dupx; - fheight = (float)gpatch->height * dupy; + fwidth = (float)SHORT(gpatch->width) * dupx; + fheight = (float)SHORT(gpatch->height) * dupy; } // positions of the cx, cy, are between 0 and vid.width/vid.height now, we need them to be between -1 and 1 @@ -418,8 +429,8 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal // fuck it, no GL support for croppedpatch v_perplayer right now. it's not like it's accessible to Lua or anything, and we only use it for menus... - cy -= (float)gpatch->topoffset * fscale; - cx -= (float)gpatch->leftoffset * fscale; + cy -= (float)SHORT(gpatch->topoffset) * fscale; + cx -= (float)SHORT(gpatch->leftoffset) * fscale; if (!(option & V_NOSCALESTART)) { @@ -461,11 +472,11 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal fwidth = w; fheight = h; - if (fwidth > gpatch->width) - fwidth = gpatch->width; + if (fwidth > SHORT(gpatch->width)) + fwidth = SHORT(gpatch->width); - if (fheight > gpatch->height) - fheight = gpatch->height; + if (fheight > SHORT(gpatch->height)) + fheight = SHORT(gpatch->height); if (pscale != FRACUNIT) { @@ -495,17 +506,17 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal v[0].z = v[1].z = v[2].z = v[3].z = 1.0f; - v[0].sow = v[3].sow = ((sx )/(float)gpatch->width )*gpatch->max_s; - if (sx + w > gpatch->width) + v[0].sow = v[3].sow = ((sx )/(float)SHORT(gpatch->width) )*gpatch->max_s; + if (sx + w > SHORT(gpatch->width)) v[2].sow = v[1].sow = gpatch->max_s; else - v[2].sow = v[1].sow = ((sx+w)/(float)gpatch->width )*gpatch->max_s; + v[2].sow = v[1].sow = ((sx+w)/(float)SHORT(gpatch->width) )*gpatch->max_s; - v[0].tow = v[1].tow = ((sy )/(float)gpatch->height)*gpatch->max_t; - if (sy + h > gpatch->height) + v[0].tow = v[1].tow = ((sy )/(float)SHORT(gpatch->height))*gpatch->max_t; + if (sy + h > SHORT(gpatch->height)) v[2].tow = v[3].tow = gpatch->max_t; else - v[2].tow = v[3].tow = ((sy+h)/(float)gpatch->height)*gpatch->max_t; + v[2].tow = v[3].tow = ((sy+h)/(float)SHORT(gpatch->height))*gpatch->max_t; flags = BLENDMODE|PF_Clip|PF_NoZClip|PF_NoDepthTest; @@ -705,6 +716,32 @@ void HWR_DrawConsoleBack(UINT32 color, INT32 height) HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest); } +// Very similar to HWR_DrawConsoleBack, except we draw from the middle(-ish) of the screen to the bottom. +void HWR_DrawTutorialBack(UINT32 color, INT32 boxheight) +{ + FOutVector v[4]; + FSurfaceInfo Surf; + INT32 height = (boxheight * 4) + (boxheight/2)*5; // 4 lines of space plus gaps between and some leeway + + // setup some neat-o translucency effect + + v[0].x = v[3].x = -1.0f; + v[2].x = v[1].x = 1.0f; + v[0].y = v[1].y = -1.0f; + v[2].y = v[3].y = -1.0f+((height<<1)/(float)vid.height); + v[0].z = v[1].z = v[2].z = v[3].z = 1.0f; + + v[0].sow = v[3].sow = 0.0f; + v[2].sow = v[1].sow = 1.0f; + v[0].tow = v[1].tow = 1.0f; + v[2].tow = v[3].tow = 0.0f; + + Surf.FlatColor.rgba = UINT2RGBA(color); + Surf.FlatColor.s.alpha = (color == 0 ? 0xC0 : 0x80); // make black darker, like software + + HWD.pfnDrawPolygon(&Surf, v, 4, PF_NoTexture|PF_Modulated|PF_Translucent|PF_NoDepthTest); +} + // ========================================================================== // R_DRAW.C STUFF diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index a5ac82001..e2fa90eb0 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -32,10 +32,6 @@ // STANDARD DLL EXPORTS // ========================================================================== -#ifdef HAVE_SDL -#undef VID_X11 -#endif - EXPORT boolean HWRAPI(Init) (I_Error_t ErrorFunction); #ifndef HAVE_SDL EXPORT void HWRAPI(Shutdown) (void); @@ -43,9 +39,6 @@ EXPORT void HWRAPI(Shutdown) (void); #ifdef _WINDOWS EXPORT void HWRAPI(GetModeList) (vmode_t **pvidmodes, INT32 *numvidmodes); #endif -#ifdef VID_X11 -EXPORT Window HWRAPI(HookXwin) (Display *, INT32, INT32, boolean); -#endif #if defined (PURESDL) || defined (macintosh) EXPORT void HWRAPI(SetPalette) (INT32 *, RGBA_t *gamma); #else @@ -71,10 +64,6 @@ EXPORT void HWRAPI(SetTransform) (FTransform *ptransform); EXPORT INT32 HWRAPI(GetTextureUsed) (void); EXPORT INT32 HWRAPI(GetRenderVersion) (void); -#ifdef VID_X11 // ifdef to be removed as soon as windoze supports that as well -// metzgermeister: added for Voodoo detection -EXPORT char *HWRAPI(GetRenderer) (void); -#endif #ifdef SHUFFLE #define SCREENVERTS 10 EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2]); @@ -115,10 +104,6 @@ struct hwdriver_s #ifdef _WINDOWS GetModeList pfnGetModeList; #endif -#ifdef VID_X11 - HookXwin pfnHookXwin; - GetRenderer pfnGetRenderer; -#endif #ifndef HAVE_SDL Shutdown pfnShutdown; #endif diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c index 50399dbd6..349103602 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -2116,6 +2116,10 @@ static void HWR_StoreWallRange(double startfrac, double endfrac) else { fixed_t texturevpeg; + boolean attachtobottom = false; +#ifdef ESLOPE + boolean slopeskew = false; // skew FOF walls with slopes? +#endif // Wow, how was this missing from OpenGL for so long? // ...Oh well, anyway, Lower Unpegged now changes pegging of FOFs like in software @@ -2123,24 +2127,50 @@ static void HWR_StoreWallRange(double startfrac, double endfrac) if (newline) { texturevpeg = sides[newline->sidenum[0]].rowoffset; - if (newline->flags & ML_DONTPEGBOTTOM) - texturevpeg -= *rover->topheight - *rover->bottomheight; + attachtobottom = !!(newline->flags & ML_DONTPEGBOTTOM); +#ifdef ESLOPE + slopeskew = !!(newline->flags & ML_DONTPEGTOP); +#endif } else { texturevpeg = sides[rover->master->sidenum[0]].rowoffset; - if (gr_linedef->flags & ML_DONTPEGBOTTOM) - texturevpeg -= *rover->topheight - *rover->bottomheight; + attachtobottom = !!(gr_linedef->flags & ML_DONTPEGBOTTOM); +#ifdef ESLOPE + slopeskew = !!(rover->master->flags & ML_DONTPEGTOP); +#endif } grTex = HWR_GetTexture(texnum); #ifdef ESLOPE - wallVerts[3].t = (*rover->topheight - h + texturevpeg) * grTex->scaleY; - wallVerts[2].t = (*rover->topheight - hS + texturevpeg) * grTex->scaleY; - wallVerts[0].t = (*rover->topheight - l + texturevpeg) * grTex->scaleY; - wallVerts[1].t = (*rover->topheight - lS + texturevpeg) * grTex->scaleY; + if (!slopeskew) // no skewing + { + if (attachtobottom) + texturevpeg -= *rover->topheight - *rover->bottomheight; + wallVerts[3].t = (*rover->topheight - h + texturevpeg) * grTex->scaleY; + wallVerts[2].t = (*rover->topheight - hS + texturevpeg) * grTex->scaleY; + wallVerts[0].t = (*rover->topheight - l + texturevpeg) * grTex->scaleY; + wallVerts[1].t = (*rover->topheight - lS + texturevpeg) * grTex->scaleY; + } + else + { + if (!attachtobottom) // skew by top + { + wallVerts[3].t = wallVerts[2].t = texturevpeg * grTex->scaleY; + wallVerts[0].t = (h - l + texturevpeg) * grTex->scaleY; + wallVerts[1].t = (hS - lS + texturevpeg) * grTex->scaleY; + } + else // skew by bottom + { + wallVerts[0].t = wallVerts[1].t = texturevpeg * grTex->scaleY; + wallVerts[3].t = wallVerts[0].t - (h - l) * grTex->scaleY; + wallVerts[2].t = wallVerts[1].t - (hS - lS) * grTex->scaleY; + } + } #else + if (attachtobottom) + texturevpeg -= *rover->topheight - *rover->bottomheight; wallVerts[3].t = wallVerts[2].t = (*rover->topheight - h + texturevpeg) * grTex->scaleY; wallVerts[0].t = wallVerts[1].t = (*rover->topheight - l + texturevpeg) * grTex->scaleY; #endif @@ -5270,8 +5300,10 @@ static void HWR_AddSprites(sector_t *sec) approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y); - if (approx_dist <= limit_dist) - HWR_ProjectSprite(thing); + if (approx_dist > limit_dist) + continue; + + HWR_ProjectSprite(thing); } } else @@ -5293,8 +5325,10 @@ static void HWR_AddSprites(sector_t *sec) approx_dist = P_AproxDistance(viewx-precipthing->x, viewy-precipthing->y); - if (approx_dist <= limit_dist) - HWR_ProjectPrecipitationSprite(precipthing); + if (approx_dist > limit_dist) + continue; + + HWR_ProjectPrecipitationSprite(precipthing); } } else @@ -5624,6 +5658,16 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) x1 = tr_x + x1 * rightcos; x2 = tr_x - x2 * rightcos; + // okay, we can't return now... this is a hack, but weather isn't networked, so it should be ok + if (!(thing->precipflags & PCF_THUNK)) + { + if (thing->precipflags & PCF_RAIN) + P_RainThinker(thing); + else + P_SnowThinker(thing); + thing->precipflags |= PCF_THUNK; + } + // // store information in a vissprite // diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 1ae2d8fc3..f25720d1e 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -35,6 +35,7 @@ void HWR_clearAutomap(void); void HWR_drawAMline(const fline_t *fl, INT32 color); void HWR_FadeScreenMenuBack(UINT16 color, UINT8 strength); void HWR_DrawConsoleBack(UINT32 color, INT32 height); +void HWR_DrawTutorialBack(UINT32 color, INT32 boxheight); void HWR_RenderSkyboxView(INT32 viewnumber, player_t *player); void HWR_RenderPlayerView(INT32 viewnumber, player_t *player); void HWR_DrawViewBorder(INT32 clearlines); diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h index c7cda35af..24a563933 100644 --- a/src/hardware/hw_md2.h +++ b/src/hardware/hw_md2.h @@ -29,6 +29,11 @@ // model version #define MD2_VERSION 8 +// magic number "IDP2" or 844121161 +#define MD2_IDENT (INT32)(('2' << 24) + ('P' << 16) + ('D' << 8) + 'I') +// model version +#define MD2_VERSION 8 + #define MD2_MAX_TRIANGLES 8192 #define MD2_MAX_VERTICES 4096 #define MD2_MAX_TEXCOORDS 4096 diff --git a/src/hu_stuff.h b/src/hu_stuff.h index 9e67e49b5..46906dd73 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -86,7 +86,7 @@ void HU_Init(void); void HU_LoadGraphics(void); // reset heads up when consoleplayer respawns. -FUNCMATH void HU_Start(void); +void HU_Start(void); boolean HU_Responder(event_t *ev); diff --git a/src/i_sound.h b/src/i_sound.h index f6e9a18af..3dc0d0d1f 100644 --- a/src/i_sound.h +++ b/src/i_sound.h @@ -79,7 +79,7 @@ void I_ShutdownSound(void); \return sfx handle */ -INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority); +INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel); /** \brief Stops a sound channel. @@ -288,4 +288,4 @@ void I_PlayCD(UINT8 track, UINT8 looping); */ boolean I_SetVolumeCD(INT32 volume); -#endif +#endif \ No newline at end of file diff --git a/src/i_tcp.c b/src/i_tcp.c index 044bf4e4c..c62adab06 100644 --- a/src/i_tcp.c +++ b/src/i_tcp.c @@ -225,6 +225,33 @@ static void wattcp_outch(char s) } #endif +#ifdef USE_WINSOCK +// stupid microsoft makes things complicated +static char *get_WSAErrorStr(int e) +{ + static char buf[256]; // allow up to 255 bytes + + buf[0] = '\0'; + + FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + (DWORD)e, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR)buf, + sizeof (buf), + NULL); + + if (!buf[0]) // provide a fallback error message if no message is available for some reason + sprintf(buf, "Unknown error"); + + return buf; +} +#undef strerror +#define strerror get_WSAErrorStr +#endif + #ifdef USE_WINSOCK2 #define inet_ntop inet_ntopA #define HAVE_NTOP @@ -703,9 +730,13 @@ static void SOCK_Send(void) c = SOCK_SendToAddr(nodesocket[doomcom->remotenode], &clientaddress[doomcom->remotenode]); } - if (c == ERRSOCKET && errno != ECONNREFUSED && errno != EWOULDBLOCK) - I_Error("SOCK_Send, error sending to node %d (%s) #%u: %s", doomcom->remotenode, - SOCK_GetNodeAddress(doomcom->remotenode), errno, strerror(errno)); + if (c == ERRSOCKET) + { + int e = errno; // save error code so it can't be modified later + if (e != ECONNREFUSED && e != EWOULDBLOCK) + I_Error("SOCK_Send, error sending to node %d (%s) #%u: %s", doomcom->remotenode, + SOCK_GetNodeAddress(doomcom->remotenode), e, strerror(e)); + } } #endif diff --git a/src/info.c b/src/info.c index 728fb13c0..72abcede1 100644 --- a/src/info.c +++ b/src/info.c @@ -3458,8 +3458,8 @@ state_t states[NUMSTATES] = {SPR_FMCE, 0, 20, {NULL}, 0, 0, S_SMASHSPIKE_EASE1}, // S_SMASHSPIKE_FLOAT {SPR_FMCE, 0, 4, {A_ZThrust}, 4, (1<<16)|1, S_SMASHSPIKE_EASE2}, // S_SMASHSPIKE_EASE1 - {SPR_FMCE, 0, 4, {A_ZThrust}, 0, (1<<16)|1, S_SMASHSPIKE_FALL}, // S_SMASHSPIKE_EASE1 - {SPR_FMCE, 0, 2, {A_ZThrust}, -6, (1<<16)|1, S_SMASHSPIKE_FALL}, // S_SMASHSPIKE_FALL + {SPR_FMCE, 0, 4, {A_ZThrust}, 0, (1<<16)|1, S_SMASHSPIKE_FALL}, // S_SMASHSPIKE_EASE2 + {SPR_FMCE, 0, 2, {A_ZThrust}, -6, 1, S_SMASHSPIKE_FALL}, // S_SMASHSPIKE_FALL {SPR_FMCE, 1, 2, {A_MultiShotDist}, (MT_DUST<<16)|10, -48, S_SMASHSPIKE_STOMP2}, // S_SMASHSPIKE_STOMP1 {SPR_FMCE, 2, 14, {NULL}, 0, 0, S_SMASHSPIKE_RISE1}, // S_SMASHSPIKE_STOMP2 {SPR_FMCE, 1, 2, {NULL}, 0, 0, S_SMASHSPIKE_RISE2}, // S_SMASHSPIKE_RISE1 @@ -17814,6 +17814,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, + { // MT_ANGLEMAN + 758, // doomednum + S_INVISIBLE, // 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 + 8, // radius + 8, // height + 0, // display offset + 10, // mass + 0, // damage + sfx_None, // activesound + MF_NOTHINK|MF_NOBLOCKMAP|MF_NOGRAVITY, // flags + S_NULL // raisestate + }, + { // MT_POLYANCHOR 760, // doomednum S_INVISIBLE, // spawnstate diff --git a/src/info.h b/src/info.h index b757deec0..c9de82541 100644 --- a/src/info.h +++ b/src/info.h @@ -4305,6 +4305,7 @@ typedef enum mobj_type MT_PULL, MT_GHOST, MT_OVERLAY, + MT_ANGLEMAN, MT_POLYANCHOR, MT_POLYSPAWN, MT_POLYSPAWNCRUSH, diff --git a/src/lua_baselib.c b/src/lua_baselib.c index e1908bbd7..289babeba 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -14,6 +14,9 @@ #ifdef HAVE_BLUA #include "p_local.h" #include "p_setup.h" // So we can have P_SetupLevelSky +#ifdef ESLOPE +#include "p_slopes.h" // P_GetZAt +#endif #include "z_zone.h" #include "r_main.h" #include "r_things.h" @@ -2009,6 +2012,24 @@ static int lib_evStartCrumble(lua_State *L) return 0; } +#ifdef ESLOPE +// P_SLOPES +//////////// + +static int lib_pGetZAt(lua_State *L) +{ + pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE)); + fixed_t x = luaL_checkfixed(L, 2); + fixed_t y = luaL_checkfixed(L, 3); + //HUDSAFE + if (!slope) + return LUA_ErrInvalid(L, "pslope_t"); + + lua_pushfixed(L, P_GetZAt(slope, x, y)); + return 1; +} +#endif + // R_DEFS //////////// @@ -2633,6 +2654,11 @@ static luaL_Reg lib[] = { {"EV_CrumbleChain",lib_evCrumbleChain}, {"EV_StartCrumble",lib_evStartCrumble}, +#ifdef ESLOPE + // p_slopes + {"P_GetZAt",lib_pGetZAt}, +#endif + // r_defs {"R_PointToAngle",lib_rPointToAngle}, {"R_PointToAngle2",lib_rPointToAngle2}, diff --git a/src/lua_hook.h b/src/lua_hook.h index 822edf79f..2b113077c 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -48,6 +48,7 @@ enum hook { hook_MobjMoveBlocked, hook_MapThingSpawn, hook_FollowMobj, + hook_PlayerQuit, hook_MAX // last hook }; @@ -87,5 +88,6 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 #define LUAh_MobjMoveBlocked(mo) LUAh_MobjHook(mo, hook_MobjMoveBlocked) // Hook for P_XYMovement (when movement is blocked) boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing); // Hook for P_SpawnMapThing by mobj type boolean LUAh_FollowMobj(player_t *player, mobj_t *mo); // Hook for P_PlayerAfterThink Smiles mobj-following +void LUAh_PlayerQuit(player_t *plr, int reason); // Hook for player quitting #endif diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 53886f7ba..bb1f59729 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -59,6 +59,7 @@ const char *const hookNames[hook_MAX+1] = { "MobjMoveBlocked", "MapThingSpawn", "FollowMobj", + "PlayerQuit", NULL }; @@ -1192,4 +1193,30 @@ boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj) return hooked; } +void LUAh_PlayerQuit(player_t *plr, int reason) +{ + hook_p hookp; + if (!gL || !(hooksAvailable[hook_PlayerQuit/8] & (1<<(hook_PlayerQuit%8)))) + return; + + lua_settop(gL, 0); + + for (hookp = roothook; hookp; hookp = hookp->next) + if (hookp->type == hook_PlayerQuit) + { + if (lua_gettop(gL) == 0) + { + LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit + lua_pushinteger(gL, reason); // Reason for quitting + } + lua_pushfstring(gL, FMT_HOOKID, hookp->id); + lua_gettable(gL, LUA_REGISTRYINDEX); + lua_pushvalue(gL, -3); + lua_pushvalue(gL, -3); + LUA_Call(gL, 2); + } + + lua_settop(gL, 0); +} + #endif diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index afc81c37d..9c5a21ec9 100644 --- a/src/lua_hudlib.c +++ b/src/lua_hudlib.c @@ -814,6 +814,15 @@ 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 +// 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-cv_translucenthud.value)*V_10TRANS); // A bit weird that it's called "translucenthud" yet 10 is fully opaque :V + return 1; +} + static luaL_Reg lib_draw[] = { // cache {"patchExists", libd_patchExists}, @@ -844,6 +853,7 @@ static luaL_Reg lib_draw[] = { {"dupx", libd_dupx}, {"dupy", libd_dupy}, {"renderer", libd_renderer}, + {"localTransFlag", libd_getlocaltransflag}, {NULL, NULL} }; @@ -867,6 +877,19 @@ static int lib_huddisable(lua_State *L) return 0; } +// 30/10/18: Lat': How come this wasn't here before? +static int lib_hudenabled(lua_State *L) +{ + enum hud option = luaL_checkoption(L, 1, NULL, hud_disable_options); + if (hud_enabled[option/8] & (1<<(option%8))) + lua_pushboolean(L, true); + else + lua_pushboolean(L, false); + + return 1; +} + + // add a HUD element for rendering static int lib_hudadd(lua_State *L) { @@ -894,6 +917,7 @@ static int lib_hudadd(lua_State *L) static luaL_Reg lib_hud[] = { {"enable", lib_hudenable}, {"disable", lib_huddisable}, + {"enabled", lib_hudenabled}, {"add", lib_hudadd}, {NULL, NULL} }; diff --git a/src/lua_libs.h b/src/lua_libs.h index b63a3ceed..3ffdd8078 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -42,6 +42,11 @@ extern lua_State *gL; #define META_SEG "SEG_T*" #define META_NODE "NODE_T*" #endif +#ifdef ESLOPE +#define META_SLOPE "PSLOPE_T*" +#define META_VECTOR2 "VECTOR2_T" +#define META_VECTOR3 "VECTOR3_T" +#endif #define META_MAPHEADER "MAPHEADER_T*" #define META_CVAR "CONSVAR_T*" diff --git a/src/lua_maplib.c b/src/lua_maplib.c index f3d1cdf66..fca99d274 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -16,6 +16,10 @@ #include "p_local.h" #include "p_setup.h" #include "z_zone.h" +#ifdef ESLOPE +#include "p_slopes.h" +#endif +#include "r_main.h" #include "lua_script.h" #include "lua_libs.h" @@ -38,7 +42,13 @@ enum sector_e { sector_heightsec, sector_camsec, sector_lines, +#ifdef ESLOPE + sector_ffloors, + sector_fslope, + sector_cslope +#else sector_ffloors +#endif }; static const char *const sector_opt[] = { @@ -55,6 +65,10 @@ static const char *const sector_opt[] = { "camsec", "lines", "ffloors", +#ifdef ESLOPE + "f_slope", + "c_slope", +#endif NULL}; enum subsector_e { @@ -160,6 +174,10 @@ enum ffloor_e { ffloor_toplightlevel, ffloor_bottomheight, ffloor_bottompic, +#ifdef ESLOPE + ffloor_tslope, + ffloor_bslope, +#endif ffloor_sector, ffloor_flags, ffloor_master, @@ -176,6 +194,10 @@ static const char *const ffloor_opt[] = { "toplightlevel", "bottomheight", "bottompic", +#ifdef ESLOPE + "t_slope", + "b_slope", +#endif "sector", // secnum pushed as control sector userdata "flags", "master", // control linedef @@ -261,6 +283,47 @@ static const char *const bbox_opt[] = { "right", NULL}; +#ifdef ESLOPE +enum slope_e { + slope_valid = 0, + slope_o, + slope_d, + slope_zdelta, + slope_normal, + slope_zangle, + slope_xydirection, + slope_sourceline, + slope_refpos, + slope_flags +}; + +static const char *const slope_opt[] = { + "valid", + "o", + "d", + "zdelta", + "normal", + "zangle", + "xydirection", + "sourceline", + "refpos", + "flags", + NULL}; + +// shared by both vector2_t and vector3_t +enum vector_e { + vector_x = 0, + vector_y, + vector_z +}; + +static const char *const vector_opt[] = { + "x", + "y", + "z", + NULL}; +#endif + static const char *const array_opt[] ={"iterate",NULL}; static const char *const valid_opt[] ={"valid",NULL}; @@ -493,6 +556,14 @@ static int sector_get(lua_State *L) LUA_PushUserdata(L, sector->ffloors, META_FFLOOR); lua_pushcclosure(L, sector_iterate, 2); // push lib_iterateFFloors and sector->ffloors as upvalues for the function return 1; +#ifdef ESLOPE + case sector_fslope: // f_slope + LUA_PushUserdata(L, sector->f_slope, META_SLOPE); + return 1; + case sector_cslope: // c_slope + LUA_PushUserdata(L, sector->c_slope, META_SLOPE); + return 1; +#endif } return 0; } @@ -515,6 +586,10 @@ static int sector_set(lua_State *L) case sector_heightsec: // heightsec case sector_camsec: // camsec case sector_ffloors: // ffloors +#ifdef ESLOPE + case sector_fslope: // f_slope + case sector_cslope: // c_slope +#endif default: return luaL_error(L, "sector_t field " LUA_QS " cannot be set.", sector_opt[field]); case sector_floorheight: { // floorheight @@ -1602,6 +1677,14 @@ static int ffloor_get(lua_State *L) lua_pushlstring(L, levelflat->name, 8); return 1; } +#ifdef ESLOPE + case ffloor_tslope: + LUA_PushUserdata(L, *ffloor->t_slope, META_SLOPE); + return 1; + case ffloor_bslope: + LUA_PushUserdata(L, *ffloor->b_slope, META_SLOPE); + return 1; +#endif case ffloor_sector: LUA_PushUserdata(L, §ors[ffloor->secnum], META_SECTOR); return 1; @@ -1641,6 +1724,10 @@ static int ffloor_set(lua_State *L) switch(field) { case ffloor_valid: // valid +#ifdef ESLOPE + case ffloor_tslope: // t_slope + case ffloor_bslope: // b_slope +#endif case ffloor_sector: // sector case ffloor_master: // master case ffloor_target: // target @@ -1701,6 +1788,189 @@ static int ffloor_set(lua_State *L) return 0; } +#ifdef ESLOPE +////////////// +// pslope_t // +////////////// + +static int slope_get(lua_State *L) +{ + pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE)); + enum slope_e field = luaL_checkoption(L, 2, slope_opt[0], slope_opt); + + if (!slope) + { + if (field == slope_valid) { + lua_pushboolean(L, 0); + return 1; + } + return luaL_error(L, "accessed pslope_t doesn't exist anymore."); + } + + switch(field) + { + case slope_valid: // valid + lua_pushboolean(L, 1); + return 1; + case slope_o: // o + LUA_PushUserdata(L, &slope->o, META_VECTOR3); + return 1; + case slope_d: // d + LUA_PushUserdata(L, &slope->d, META_VECTOR2); + return 1; + case slope_zdelta: // zdelta + lua_pushfixed(L, slope->zdelta); + return 1; + case slope_normal: // normal + LUA_PushUserdata(L, &slope->normal, META_VECTOR3); + return 1; + case slope_zangle: // zangle + lua_pushangle(L, slope->zangle); + return 1; + case slope_xydirection: // xydirection + lua_pushangle(L, slope->xydirection); + return 1; + case slope_sourceline: // source linedef + LUA_PushUserdata(L, slope->sourceline, META_LINE); + return 1; + case slope_refpos: // refpos + lua_pushinteger(L, slope->refpos); + return 1; + case slope_flags: // flags + lua_pushinteger(L, slope->flags); + return 1; + } + return 0; +} + +static int slope_set(lua_State *L) +{ + pslope_t *slope = *((pslope_t **)luaL_checkudata(L, 1, META_SLOPE)); + enum slope_e field = luaL_checkoption(L, 2, slope_opt[0], slope_opt); + + if (!slope) + return luaL_error(L, "accessed pslope_t doesn't exist anymore."); + + if (hud_running) + return luaL_error(L, "Do not alter pslope_t in HUD rendering code!"); + + switch(field) // todo: reorganize this shit + { + case slope_valid: // valid + case slope_sourceline: // sourceline + case slope_d: // d + case slope_flags: // flags + case slope_normal: // normal + case slope_refpos: // refpos + default: + return luaL_error(L, "pslope_t field " LUA_QS " cannot be set.", slope_opt[field]); + case slope_o: { // o + luaL_checktype(L, 3, LUA_TTABLE); + + lua_getfield(L, 3, "x"); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_rawgeti(L, 3, 1); + } + if (!lua_isnil(L, -1)) + slope->o.x = luaL_checkfixed(L, -1); + else + slope->o.x = 0; + lua_pop(L, 1); + + lua_getfield(L, 3, "y"); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_rawgeti(L, 3, 2); + } + if (!lua_isnil(L, -1)) + slope->o.y = luaL_checkfixed(L, -1); + else + slope->o.y = 0; + lua_pop(L, 1); + + lua_getfield(L, 3, "z"); + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_rawgeti(L, 3, 3); + } + if (!lua_isnil(L, -1)) + slope->o.z = luaL_checkfixed(L, -1); + else + slope->o.z = 0; + lua_pop(L, 1); + break; + } + case slope_zdelta: { // zdelta, this is temp until i figure out wtf to do + slope->zdelta = luaL_checkfixed(L, 3); + slope->zangle = R_PointToAngle2(0, 0, FRACUNIT, -slope->zdelta); + P_CalculateSlopeNormal(slope); + break; + } + case slope_zangle: { // zangle + angle_t zangle = luaL_checkangle(L, 3); + if (zangle == ANGLE_90 || zangle == ANGLE_270) + return luaL_error(L, "invalid zangle for slope!"); + slope->zangle = zangle; + slope->zdelta = -FINETANGENT(((slope->zangle+ANGLE_90)>>ANGLETOFINESHIFT) & 4095); + P_CalculateSlopeNormal(slope); + break; + } + case slope_xydirection: // xydirection + slope->xydirection = luaL_checkangle(L, 3); + slope->d.x = -FINECOSINE((slope->xydirection>>ANGLETOFINESHIFT) & FINEMASK); + slope->d.y = -FINESINE((slope->xydirection>>ANGLETOFINESHIFT) & FINEMASK); + P_CalculateSlopeNormal(slope); + break; + } + return 0; +} + +/////////////// +// vector*_t // +/////////////// + +static int vector2_get(lua_State *L) +{ + vector2_t *vec = *((vector2_t **)luaL_checkudata(L, 1, META_VECTOR2)); + enum vector_e field = luaL_checkoption(L, 2, vector_opt[0], vector_opt); + + if (!vec) + return luaL_error(L, "accessed vector2_t doesn't exist anymore."); + + switch(field) + { + case vector_x: lua_pushfixed(L, vec->x); return 1; + case vector_y: lua_pushfixed(L, vec->y); return 1; + default: break; + } + + return 0; +} + +static int vector3_get(lua_State *L) +{ + vector3_t *vec = *((vector3_t **)luaL_checkudata(L, 1, META_VECTOR3)); + enum vector_e field = luaL_checkoption(L, 2, vector_opt[0], vector_opt); + + if (!vec) + return luaL_error(L, "accessed vector3_t doesn't exist anymore."); + + switch(field) + { + case vector_x: lua_pushfixed(L, vec->x); return 1; + case vector_y: lua_pushfixed(L, vec->y); return 1; + case vector_z: lua_pushfixed(L, vec->z); return 1; + default: break; + } + + return 0; +} +#endif + ///////////////////// // mapheaderinfo[] // ///////////////////// @@ -1932,6 +2202,26 @@ int LUA_MapLib(lua_State *L) lua_setfield(L, -2, "__index"); lua_pop(L, 1); +#ifdef ESLOPE + luaL_newmetatable(L, META_SLOPE); + lua_pushcfunction(L, slope_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, slope_set); + lua_setfield(L, -2, "__newindex"); + lua_pop(L, 1); + + luaL_newmetatable(L, META_VECTOR2); + lua_pushcfunction(L, vector2_get); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); + + luaL_newmetatable(L, META_VECTOR3); + lua_pushcfunction(L, vector3_get); + lua_setfield(L, -2, "__index"); + lua_pop(L, 1); +#endif + luaL_newmetatable(L, META_MAPHEADER); lua_pushcfunction(L, mapheaderinfo_get); lua_setfield(L, -2, "__index"); diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index da0e99ab2..a9db95d4b 100644 --- a/src/lua_mobjlib.c +++ b/src/lua_mobjlib.c @@ -83,7 +83,12 @@ enum mobj_e { mobj_extravalue1, mobj_extravalue2, mobj_cusval, +#ifdef ESLOPE + mobj_cvmem, + mobj_standingslope +#else mobj_cvmem +#endif }; static const char *const mobj_opt[] = { @@ -146,6 +151,9 @@ static const char *const mobj_opt[] = { "extravalue2", "cusval", "cvmem", +#ifdef ESLOPE + "standingslope", +#endif NULL}; #define UNIMPLEMENTED luaL_error(L, LUA_QL("mobj_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", mobj_opt[field]) @@ -358,6 +366,11 @@ static int mobj_get(lua_State *L) case mobj_cvmem: lua_pushinteger(L, mo->cvmem); break; +#ifdef ESLOPE + case mobj_standingslope: + LUA_PushUserdata(L, mo->standingslope, META_SLOPE); + break; +#endif default: // extra custom variables in Lua memory lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); @@ -675,6 +688,10 @@ static int mobj_set(lua_State *L) case mobj_cvmem: mo->cvmem = luaL_checkinteger(L, 3); break; +#ifdef ESLOPE + case mobj_standingslope: + return NOSET; +#endif default: lua_getfield(L, LUA_REGISTRYINDEX, LREG_EXTVARS); I_Assert(lua_istable(L, -1)); diff --git a/src/lua_script.c b/src/lua_script.c index d5736f9e5..69e275712 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -22,6 +22,9 @@ #include "byteptr.h" #include "p_saveg.h" #include "p_local.h" +#ifdef ESLOPE +#include "p_slopes.h" // for P_SlopeById +#endif #ifdef LUA_ALLOW_BYTECODE #include "d_netfil.h" // for LUA_DumpFile #endif @@ -193,25 +196,27 @@ void LUA_LoadLump(UINT16 wad, UINT16 lump) { MYFILE f; char *name; - + size_t len; f.wad = wad; f.size = W_LumpLengthPwad(wad, lump); f.data = Z_Malloc(f.size, PU_LUA, NULL); W_ReadLumpPwad(wad, lump, f.data); f.curpos = f.data; + len = strlen(wadfiles[wad]->filename); // length of file name + if (wadfiles[wad]->type == RET_LUA) { - name = malloc(strlen(wadfiles[wad]->filename)+1); + name = malloc(len+1); strcpy(name, wadfiles[wad]->filename); } else // If it's not a .lua file, copy the lump name in too. { lumpinfo_t *lump_p = &wadfiles[wad]->lumpinfo[lump]; - size_t length = strlen(wadfiles[wad]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name - name = malloc(length + 1); + len += 1 + strlen(lump_p->name2); // length of file name, '|', and lump name + name = malloc(len+1); sprintf(name, "%s|%s", wadfiles[wad]->filename, lump_p->name2); - name[length] = '\0'; + name[len] = '\0'; } LUA_LoadFile(&f, name); // actually load file! @@ -496,6 +501,9 @@ enum ARCH_NODE, #endif ARCH_FFLOOR, +#ifdef ESLOPE + ARCH_SLOPE, +#endif ARCH_MAPHEADER, ARCH_TEND=0xFF, @@ -520,6 +528,9 @@ static const struct { {META_NODE, ARCH_NODE}, #endif {META_FFLOOR, ARCH_FFLOOR}, +#ifdef ESLOPE + {META_SLOPE, ARCH_SLOPE}, +#endif {META_MAPHEADER, ARCH_MAPHEADER}, {NULL, ARCH_NULL} }; @@ -774,6 +785,19 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) } break; } +#ifdef ESLOPE + case ARCH_SLOPE: + { + pslope_t *slope = *((pslope_t **)lua_touserdata(gL, myindex)); + if (!slope) + WRITEUINT8(save_p, ARCH_NULL); + else { + WRITEUINT8(save_p, ARCH_SLOPE); + WRITEUINT16(save_p, slope->id); + } + break; + } +#endif case ARCH_MAPHEADER: { mapheader_t *header = *((mapheader_t **)lua_touserdata(gL, myindex)); @@ -995,8 +1019,13 @@ static UINT8 UnArchiveValue(int TABLESINDEX) LUA_PushUserdata(gL, rover, META_FFLOOR); break; } +#ifdef ESLOPE + case ARCH_SLOPE: + LUA_PushUserdata(gL, P_SlopeById(READUINT16(save_p)), META_SLOPE); + break; +#endif case ARCH_MAPHEADER: - LUA_PushUserdata(gL, §ors[READUINT16(save_p)], META_MAPHEADER); + LUA_PushUserdata(gL, mapheaderinfo[READUINT16(save_p)], META_MAPHEADER); break; case ARCH_TEND: return 1; diff --git a/src/m_anigif.c b/src/m_anigif.c index 6ae112ea8..4ebf2aff3 100644 --- a/src/m_anigif.c +++ b/src/m_anigif.c @@ -497,7 +497,9 @@ static void GIF_framewrite(void) // screen regions are handled in GIF_lzw { - UINT16 delay = 3; // todo + int d1 = (int)((100.0/NEWTICRATE)*(gif_frames+1)); + int d2 = (int)((100.0/NEWTICRATE)*(gif_frames)); + UINT16 delay = d1-d2; INT32 startline; WRITEMEM(p, gifframe_gchead, 4); diff --git a/src/m_menu.c b/src/m_menu.c index a0f99c86a..e973cea55 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -273,6 +273,7 @@ menu_t SP_MainDef, OP_MainDef; menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef; // Single Player +static void M_StartTutorial(INT32 choice); static void M_LoadGame(INT32 choice); static void M_TimeAttackLevelSelect(INT32 choice); static void M_TimeAttack(INT32 choice); @@ -440,6 +441,9 @@ static CV_PossibleValue_t serversort_cons_t[] = { }; consvar_t cv_serversort = {"serversort", "Ping", CV_HIDEN | CV_CALL, serversort_cons_t, M_SortServerList, 0, NULL, NULL, 0, 0, NULL}; +// first time memory +consvar_t cv_tutorialprompt = {"tutorialprompt", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; + // autorecord demos for time attack static consvar_t cv_autorecord = {"autorecord", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; @@ -731,6 +735,7 @@ 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}, @@ -739,6 +744,7 @@ static menuitem_t SP_MainMenu[] = enum { + sptutorial, sploadgame, sprecordattack, spnightsmode, @@ -1554,7 +1560,18 @@ menu_t SR_EmblemHintDef = }; // Single Player -menu_t SP_MainDef = CENTERMENUSTYLE(NULL, SP_MainMenu, &MainDef, 72); +menu_t SP_MainDef = //CENTERMENUSTYLE(NULL, SP_MainMenu, &MainDef, 72); +{ + NULL, + sizeof(SP_MainMenu)/sizeof(menuitem_t), + &MainDef, + SP_MainMenu, + M_DrawCenteredMenu, + BASEVIDWIDTH/2, 72, + 1, // start at "Start Game" on first entry + NULL +}; + menu_t SP_LoadDef = { "M_PICKG", @@ -6093,6 +6110,8 @@ static void M_CustomLevelSelect(INT32 choice) static void M_SinglePlayerMenu(INT32 choice) { (void)choice; + SP_MainMenu[sptutorial].status = + tutorialmap ? IT_CALL|IT_STRING : IT_NOTHING|IT_DISABLED; SP_MainMenu[sprecordattack].status = (M_SecretUnlocked(SECRET_RECORDATTACK)) ? IT_CALL|IT_STRING : IT_SECRET; SP_MainMenu[spnightsmode].status = @@ -6118,6 +6137,80 @@ static void M_LoadGameLevelSelect(INT32 choice) M_SetupNextMenu(&SP_LevelSelectDef); } +void M_TutorialSaveControlResponse(INT32 ch) +{ + if (ch == 'y' || ch == KEY_ENTER) + { + G_CopyControls(gamecontrol, gamecontroldefault[tutorialgcs], gcl_tutorial_full, num_gcl_tutorial_full); + CV_Set(&cv_usemouse, cv_usemouse.defaultvalue); + CV_Set(&cv_alwaysfreelook, cv_alwaysfreelook.defaultvalue); + CV_Set(&cv_mousemove, cv_mousemove.defaultvalue); + CV_Set(&cv_analog, cv_analog.defaultvalue); + S_StartSound(NULL, sfx_itemup); + } + else + S_StartSound(NULL, sfx_menu1); +} + +static void M_TutorialControlResponse(INT32 ch) +{ + if (ch != KEY_ESCAPE) + { + G_CopyControls(gamecontroldefault[gcs_custom], gamecontrol, NULL, 0); // using gcs_custom as temp storage for old controls + if (ch == 'y' || ch == KEY_ENTER) + { + tutorialgcs = gcs_fps; + tutorialusemouse = cv_usemouse.value; + tutorialfreelook = cv_alwaysfreelook.value; + tutorialmousemove = cv_mousemove.value; + tutorialanalog = cv_analog.value; + + G_CopyControls(gamecontrol, gamecontroldefault[tutorialgcs], gcl_tutorial_full, num_gcl_tutorial_full); + CV_Set(&cv_usemouse, cv_usemouse.defaultvalue); + CV_Set(&cv_alwaysfreelook, cv_alwaysfreelook.defaultvalue); + CV_Set(&cv_mousemove, cv_mousemove.defaultvalue); + CV_Set(&cv_analog, cv_analog.defaultvalue); + + //S_StartSound(NULL, sfx_itemup); + } + else + { + tutorialgcs = gcs_custom; + S_StartSound(NULL, sfx_menu1); + } + M_StartTutorial(INT32_MAX); + } + else + S_StartSound(NULL, sfx_menu1); + + MessageDef.prevMenu = &SP_MainDef; // if FirstPrompt -> ControlsPrompt -> ESC, we would go to the main menu unless we force this +} + +// Starts up the tutorial immediately (tbh I wasn't sure where else to put this) +static void M_StartTutorial(INT32 choice) +{ + if (!tutorialmap) + return; // no map to go to, don't bother + + if (choice != INT32_MAX && G_GetControlScheme(gamecontrol, gcl_tutorial_check, num_gcl_tutorial_check) != gcs_fps) + { + M_StartMessage("Do you want to try the \202recommended \202movement controls\x80?\n\nWe will set them just for this tutorial.\n\nPress 'Y' or 'Enter' to confirm\nPress 'N' or any key to keep \nyour current controls.\n",M_TutorialControlResponse,MM_YESNO); + return; + } + else if (choice != INT32_MAX) + tutorialgcs = gcs_custom; + + CV_SetValue(&cv_tutorialprompt, 0); // first-time prompt + + tutorialmode = true; // turn on tutorial mode + + emeralds = 0; + M_ClearMenus(true); + gamecomplete = false; + cursaveslot = 0; + G_DeferedInitNew(false, G_BuildMapName(tutorialmap), 0, false, false); +} + // ============== // LOAD GAME MENU // ============== @@ -6725,6 +6818,26 @@ static void M_HandleLoadSave(INT32 choice) } } +static void M_FirstTimeResponse(INT32 ch) +{ + S_StartSound(NULL, sfx_menu1); + + if (ch == KEY_ESCAPE) + return; + + if (ch != 'y' && ch != KEY_ENTER) + { + CV_SetValue(&cv_tutorialprompt, 0); + M_ReadSaveStrings(); + MessageDef.prevMenu = &SP_LoadDef; // calls M_SetupNextMenu + } + else + { + M_StartTutorial(0); + MessageDef.prevMenu = &MessageDef; // otherwise, the controls prompt won't fire + } +} + // // Selected from SRB2 menu // @@ -6732,6 +6845,13 @@ static void M_LoadGame(INT32 choice) { (void)choice; + if (tutorialmap && cv_tutorialprompt.value) + { + M_StartMessage("Do you want to \x82play a brief Tutorial\x80?\n\nWe highly recommend this because \nthe controls are slightly different \nfrom other games.\n\nPress 'Y' or 'Enter' to go\nPress 'N' or any key to skip\n", + M_FirstTimeResponse, MM_YESNO); + return; + } + M_ReadSaveStrings(); M_SetupNextMenu(&SP_LoadDef); } @@ -9198,9 +9318,17 @@ static void M_DrawControl(void) // draw title (or big pic) M_DrawMenuTitle(); - M_CentreText(30, - (setupcontrols_secondaryplayer ? "SET CONTROLS FOR SECONDARY PLAYER" : - "PRESS ENTER TO CHANGE, BACKSPACE TO CLEAR")); + if (tutorialmode && tutorialgcs) + { + if ((gametic / TICRATE) % 2) + M_CentreText(30, "\202EXIT THE TUTORIAL TO CHANGE THE CONTROLS"); + else + M_CentreText(30, "EXIT THE TUTORIAL TO CHANGE THE CONTROLS"); + } + else + M_CentreText(30, + (setupcontrols_secondaryplayer ? "SET CONTROLS FOR SECONDARY PLAYER" : + "PRESS ENTER TO CHANGE, BACKSPACE TO CLEAR")); if (i) V_DrawString(currentMenu->x - 16, y-(skullAnimCounter/5), V_YELLOWMAP, "\x1A"); // up arrow @@ -9335,6 +9463,9 @@ static void M_ChangeControl(INT32 choice) { static char tmp[55]; + if (tutorialmode && tutorialgcs) // don't allow control changes if temp control override is active + return; + controltochange = currentMenu->menuitems[choice].alphaKey; sprintf(tmp, M_GetText("Hit the new key for\n%s\nESC for Cancel"), currentMenu->menuitems[choice].text); @@ -9523,7 +9654,6 @@ static void M_ToggleMIDI(INT32 choice) default: break; } - if (midi_disabled) { midi_disabled = false; diff --git a/src/m_menu.h b/src/m_menu.h index 9df56e897..ad32de1b1 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -240,6 +240,8 @@ extern INT16 char_on, startchar; #define BwehHehHe() S_StartSound(NULL, sfx_bewar1+M_RandomKey(4)) // Bweh heh he +void M_TutorialSaveControlResponse(INT32 ch); + void M_ForceSaveSlotSelected(INT32 sslot); void M_CheatActivationResponder(INT32 ch); diff --git a/src/m_misc.c b/src/m_misc.c index 50b6d7a05..92c2aeea9 100644 --- a/src/m_misc.c +++ b/src/m_misc.c @@ -58,7 +58,7 @@ typedef off_t off64_t; #if defined(__MINGW32__) && ((__GNUC__ > 7) || (__GNUC__ == 6 && __GNUC_MINOR__ >= 3)) #define PRIdS "u" -#elif defined (_WIN32) +#elif defined (_WIN32) #define PRIdS "Iu" #elif defined (DJGPP) #define PRIdS "u" @@ -475,7 +475,8 @@ void M_FirstLoadConfig(void) } // load default control - G_Controldefault(); + G_DefineDefaultControls(); + G_CopyControls(gamecontrol, gamecontroldefault[gcs_fps], NULL, 0); // load config, make sure those commands doesnt require the screen... COM_BufInsertText(va("exec \"%s\"\n", configfile)); @@ -538,8 +539,28 @@ void M_SaveConfig(const char *filename) // FIXME: save key aliases if ever implemented.. - CV_SaveVariables(f); - if (!dedicated) G_SaveKeySetting(f); + if (tutorialmode && tutorialgcs) + { + CV_SetValue(&cv_usemouse, tutorialusemouse); + CV_SetValue(&cv_alwaysfreelook, tutorialfreelook); + CV_SetValue(&cv_mousemove, tutorialmousemove); + CV_SetValue(&cv_analog, tutorialanalog); + CV_SaveVariables(f); + CV_Set(&cv_usemouse, cv_usemouse.defaultvalue); + CV_Set(&cv_alwaysfreelook, cv_alwaysfreelook.defaultvalue); + CV_Set(&cv_mousemove, cv_mousemove.defaultvalue); + CV_Set(&cv_analog, cv_analog.defaultvalue); + } + else + CV_SaveVariables(f); + + if (!dedicated) + { + if (tutorialmode && tutorialgcs) + G_SaveKeySetting(f, gamecontroldefault[gcs_custom], gamecontrolbis); // using gcs_custom as temp storage + else + G_SaveKeySetting(f, gamecontrol, gamecontrolbis); + } fclose(f); } diff --git a/src/p_enemy.c b/src/p_enemy.c index 6c19ed5d3..bd5b3ac19 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -3324,6 +3324,11 @@ void A_MonitorPop(mobj_t *actor) newmobj->sprite = SPR_TV1P; } } + + // Run a linedef executor immediately upon popping + // You may want to delay your effects by 18 tics to sync with the reward giving + if (actor->spawnpoint && actor->lastlook) + P_LinedefExecute(actor->lastlook, actor->target, NULL); } // Function: A_GoldMonitorPop @@ -3407,6 +3412,11 @@ void A_GoldMonitorPop(mobj_t *actor) newmobj->sprite = SPR_TV1P; } } + + // Run a linedef executor immediately upon popping + // You may want to delay your effects by 18 tics to sync with the reward giving + if (actor->spawnpoint && actor->lastlook) + P_LinedefExecute(actor->lastlook, actor->target, NULL); } // Function: A_GoldMonitorRestore diff --git a/src/p_mobj.c b/src/p_mobj.c index 417716030..3c1418252 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -3729,14 +3729,15 @@ boolean P_CameraThinker(player_t *player, camera_t *thiscam, boolean resetcalled if (player->pflags & PF_FLIPCAM && !(player->powers[pw_carry] == CR_NIGHTSMODE) && player->mo->eflags & MFE_VERTICALFLIP) postimg = postimg_flip; - else if (player->awayviewtics) + else if (player->awayviewtics && player->awayviewmobj != NULL) // Camera must obviously exist { camera_t dummycam; dummycam.subsector = player->awayviewmobj->subsector; dummycam.x = player->awayviewmobj->x; dummycam.y = player->awayviewmobj->y; dummycam.z = player->awayviewmobj->z; - dummycam.height = 40*FRACUNIT; // alt view height is 20*FRACUNIT + //dummycam.height = 40*FRACUNIT; // alt view height is 20*FRACUNIT + dummycam.height = 0; // Why? Remote viewpoint cameras have no height. // Are we in water? if (P_CameraCheckWater(&dummycam)) postimg = postimg_water; @@ -4059,7 +4060,8 @@ void P_RecalcPrecipInSector(sector_t *sector) // void P_NullPrecipThinker(precipmobj_t *mobj) { - (void)mobj; + //(void)mobj; + mobj->precipflags &= ~PCF_THUNK; } void P_SnowThinker(precipmobj_t *mobj) @@ -4079,25 +4081,26 @@ void P_RainThinker(precipmobj_t *mobj) { // cycle through states, // calling action functions at transitions - if (mobj->tics > 0 && --mobj->tics == 0) - { - // you can cycle through multiple states in a tic - if (!P_SetPrecipMobjState(mobj, mobj->state->nextstate)) - return; // freed itself - } + if (mobj->tics <= 0) + return; + + if (--mobj->tics) + return; + + if (!P_SetPrecipMobjState(mobj, mobj->state->nextstate)) + return; + + if (mobj->state != &states[S_RAINRETURN]) + return; + + mobj->z = mobj->ceilingz; + P_SetPrecipMobjState(mobj, S_RAIN1); - if (mobj->state == &states[S_RAINRETURN]) - { - mobj->z = mobj->ceilingz; - P_SetPrecipMobjState(mobj, S_RAIN1); - } return; } // adjust height - mobj->z += mobj->momz; - - if (mobj->z <= mobj->floorz) + if ((mobj->z += mobj->momz) <= mobj->floorz) { // no splashes on sky or bottomless pits if (mobj->precipflags & PCF_PIT) @@ -7433,6 +7436,48 @@ void P_MobjThinker(mobj_t *mobj) if ((mobj->flags & MF_ENEMY) && (mobj->state->nextstate == mobj->info->spawnstate && mobj->tics == 1)) mobj->flags2 &= ~MF2_FRET; + // Angle-to-tracer to trigger a linedef exec + // See Linedef Exec 457 (Track mobj angle to point) + if ((mobj->eflags & MFE_TRACERANGLE) && mobj->tracer && mobj->extravalue2) + { + // mobj->lastlook - Don't disable behavior after first failure + // mobj->extravalue1 - Angle tolerance + // mobj->extravalue2 - Exec tag upon failure + // mobj->cvval - Allowable failure delay + // mobj->cvmem - Failure timer + + angle_t ang = mobj->angle - R_PointToAngle2(mobj->x, mobj->y, mobj->tracer->x, mobj->tracer->y); + + // \todo account for distance between mobj and tracer + // Because closer mobjs can be facing beyond the angle tolerance + // yet tracer is still in the camera view + + // failure state: mobj is not facing tracer + // Reasaonable defaults: ANGLE_67h, ANGLE_292h + if (ang >= (UINT32)mobj->extravalue1 && ang <= ANGLE_MAX - (UINT32)mobj->extravalue1) + { + if (mobj->cvmem) + mobj->cvmem--; + else + { + INT32 exectag = mobj->extravalue2; // remember this before we erase the values + + if (mobj->lastlook) + mobj->cvmem = mobj->cusval; // reset timer for next failure + else + { + // disable after first failure + mobj->eflags &= ~MFE_TRACERANGLE; + mobj->lastlook = mobj->extravalue1 = mobj->extravalue2 = mobj->cvmem = mobj->cusval = 0; + } + + P_LinedefExecute(exectag, mobj, NULL); + } + } + else + mobj->cvmem = mobj->cusval; // reset failure timer + } + switch (mobj->type) { case MT_WALLSPIKEBASE: @@ -8921,14 +8966,15 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype static inline precipmobj_t *P_SpawnRainMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) { precipmobj_t *mo = P_SpawnPrecipMobj(x,y,z,type); - mo->thinker.function.acp1 = (actionf_p1)P_RainThinker; + mo->precipflags |= PCF_RAIN; + //mo->thinker.function.acp1 = (actionf_p1)P_RainThinker; return mo; } static inline precipmobj_t *P_SpawnSnowMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) { precipmobj_t *mo = P_SpawnPrecipMobj(x,y,z,type); - mo->thinker.function.acp1 = (actionf_p1)P_SnowThinker; + //mo->thinker.function.acp1 = (actionf_p1)P_SnowThinker; return mo; } @@ -10891,6 +10937,16 @@ ML_EFFECT4 : Don't clip inside the ground mobj->flags2 |= MF2_OBJECTFLIP; } + // Extra functionality + if (mthing->options & MTF_EXTRA) + { + if (mobj->flags & MF_MONITOR && (mthing->angle & 16384)) + { + // Store line exec tag to run upon popping + mobj->lastlook = (mthing->angle & 16383); + } + } + // Final set of not being able to draw nightsitems. if (mobj->flags & MF_NIGHTSITEM) mobj->flags2 |= MF2_DONTDRAW; diff --git a/src/p_mobj.h b/src/p_mobj.h index 630600b54..eac5118b8 100644 --- a/src/p_mobj.h +++ b/src/p_mobj.h @@ -239,6 +239,9 @@ typedef enum MFE_SPRUNG = 1<<8, // Platform movement MFE_APPLYPMOMZ = 1<<9, + // Compute and trigger on mobj angle relative to tracer + // See Linedef Exec 457 (Track mobj angle to point) + MFE_TRACERANGLE = 1<<10, // free: to and including 1<<15 } mobjeflag_t; @@ -254,6 +257,10 @@ typedef enum { PCF_FOF = 4, // Above MOVING FOF (this means we need to keep floorz up to date...) PCF_MOVINGFOF = 8, + // Is rain. + PCF_RAIN = 16, + // Ran the thinker this tic. + PCF_THUNK = 32, } precipflag_t; // Map Object definition. typedef struct mobj_s @@ -449,7 +456,7 @@ boolean P_SupermanLook4Players(mobj_t *actor); void P_DestroyRobots(void); void P_SnowThinker(precipmobj_t *mobj); void P_RainThinker(precipmobj_t *mobj); -FUNCMATH void P_NullPrecipThinker(precipmobj_t *mobj); +void P_NullPrecipThinker(precipmobj_t *mobj); void P_RemovePrecipMobj(precipmobj_t *mobj); void P_SetScale(mobj_t *mobj, fixed_t newscale); void P_XYMovement(mobj_t *mo); diff --git a/src/p_saveg.c b/src/p_saveg.c index 05aaf6f6d..6a07e513f 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -2121,8 +2121,7 @@ static void P_NetArchiveThinkers(void) for (th = thinkercap.next; th != &thinkercap; th = th->next) { if (!(th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed - || th->function.acp1 == (actionf_p1)P_RainThinker - || th->function.acp1 == (actionf_p1)P_SnowThinker)) + || th->function.acp1 == (actionf_p1)P_NullPrecipThinker)) numsaved++; if (th->function.acp1 == (actionf_p1)P_MobjThinker) @@ -2131,8 +2130,7 @@ static void P_NetArchiveThinkers(void) continue; } #ifdef PARANOIA - else if (th->function.acp1 == (actionf_p1)P_RainThinker - || th->function.acp1 == (actionf_p1)P_SnowThinker); + else if (th->function.acp1 == (actionf_p1)P_NullPrecipThinker); #endif else if (th->function.acp1 == (actionf_p1)T_MoveCeiling) { diff --git a/src/p_setup.c b/src/p_setup.c index 4926606ee..2e86dffa1 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -1546,6 +1546,7 @@ static void P_LoadRawSideDefs2(void *data) } case 443: // Calls a named Lua function + case 459: // Control text prompt (named tag) { char process[8*3+1]; memset(process,0,8*3+1); @@ -2758,6 +2759,9 @@ boolean P_SetupLevel(boolean skipprecip) I_UpdateNoVsync(); } + // Close text prompt before freeing the old level + F_EndTextPrompt(false, true); + #ifdef HAVE_BLUA LUA_InvalidateLevel(); #endif diff --git a/src/p_slopes.c b/src/p_slopes.c index 7c84a2db5..b7cd597aa 100644 --- a/src/p_slopes.c +++ b/src/p_slopes.c @@ -29,7 +29,7 @@ static pslope_t *slopelist = NULL; static UINT16 slopecount = 0; // Calculate line normal -static void P_CalculateSlopeNormal(pslope_t *slope) { +void P_CalculateSlopeNormal(pslope_t *slope) { slope->normal.z = FINECOSINE(slope->zangle>>ANGLETOFINESHIFT); slope->normal.x = -FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.x); slope->normal.y = -FixedMul(FINESINE(slope->zangle>>ANGLETOFINESHIFT), slope->d.y); diff --git a/src/p_slopes.h b/src/p_slopes.h index f59c5b767..252bbdb3c 100644 --- a/src/p_slopes.h +++ b/src/p_slopes.h @@ -14,6 +14,7 @@ #define P_SLOPES_H__ #ifdef ESLOPE +void P_CalculateSlopeNormal(pslope_t *slope); void P_ResetDynamicSlopes(void); void P_RunDynamicSlopes(void); // P_SpawnSlope_Line diff --git a/src/p_spec.c b/src/p_spec.c index 5a53d5c86..e5aa1a76f 100644 --- a/src/p_spec.c +++ b/src/p_spec.c @@ -35,6 +35,7 @@ #include "m_misc.h" #include "m_cond.h" //unlock triggers #include "lua_hook.h" // LUAh_LinedefExecute +#include "f_finale.h" // control text prompt #ifdef HW3SOUND #include "hardware/hw3sound.h" @@ -2226,8 +2227,7 @@ void P_SwitchWeather(INT32 weathernum) for (think = thinkercap.next; think != &thinkercap; think = think->next) { - if ((think->function.acp1 != (actionf_p1)P_SnowThinker) - && (think->function.acp1 != (actionf_p1)P_RainThinker)) + if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker) continue; // not a precipmobj thinker precipmobj = (precipmobj_t *)think; @@ -2243,14 +2243,12 @@ void P_SwitchWeather(INT32 weathernum) for (think = thinkercap.next; think != &thinkercap; think = think->next) { + if (think->function.acp1 != (actionf_p1)P_NullPrecipThinker) + continue; // not a precipmobj thinker + precipmobj = (precipmobj_t *)think; + if (swap == PRECIP_RAIN) // Snow To Rain { - if (!(think->function.acp1 == (actionf_p1)P_SnowThinker - || think->function.acp1 == (actionf_p1)P_NullPrecipThinker)) - continue; // not a precipmobj thinker - - precipmobj = (precipmobj_t *)think; - precipmobj->flags = mobjinfo[MT_RAIN].flags; st = &states[mobjinfo[MT_RAIN].spawnstate]; precipmobj->state = st; @@ -2261,18 +2259,13 @@ void P_SwitchWeather(INT32 weathernum) precipmobj->precipflags &= ~PCF_INVISIBLE; - think->function.acp1 = (actionf_p1)P_RainThinker; + precipmobj->precipflags |= PCF_RAIN; + //think->function.acp1 = (actionf_p1)P_RainThinker; } else if (swap == PRECIP_SNOW) // Rain To Snow { INT32 z; - if (!(think->function.acp1 == (actionf_p1)P_RainThinker - || think->function.acp1 == (actionf_p1)P_NullPrecipThinker)) - continue; // not a precipmobj thinker - - precipmobj = (precipmobj_t *)think; - precipmobj->flags = mobjinfo[MT_SNOWFLAKE].flags; z = M_RandomByte(); @@ -2290,19 +2283,13 @@ void P_SwitchWeather(INT32 weathernum) precipmobj->frame = st->frame; precipmobj->momz = mobjinfo[MT_SNOWFLAKE].speed; - precipmobj->precipflags &= ~PCF_INVISIBLE; + precipmobj->precipflags &= ~(PCF_INVISIBLE|PCF_RAIN); - think->function.acp1 = (actionf_p1)P_SnowThinker; + //think->function.acp1 = (actionf_p1)P_SnowThinker; } else if (swap == PRECIP_BLANK || swap == PRECIP_STORM_NORAIN) // Remove precip, but keep it around for reuse. { - if (!(think->function.acp1 == (actionf_p1)P_RainThinker - || think->function.acp1 == (actionf_p1)P_SnowThinker)) - continue; - - precipmobj = (precipmobj_t *)think; - - think->function.acp1 = (actionf_p1)P_NullPrecipThinker; + //think->function.acp1 = (actionf_p1)P_NullPrecipThinker; precipmobj->precipflags |= PCF_INVISIBLE; } @@ -3093,6 +3080,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); sector_t *sec; // Sector that the FOF is visible in ffloor_t *rover; // FOF that we are going to crumble + boolean foundrover = false; // for debug, "Can't find a FOF" message for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;) { @@ -3107,16 +3095,18 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) for (rover = sec->ffloors; rover; rover = rover->next) { if (rover->master->frontsector->tag == foftag) - break; + { + foundrover = true; + + EV_CrumbleChain(sec, rover); + } } - if (!rover) + if (!foundrover) { CONS_Debug(DBG_GAMELOGIC, "Line type 436 Executor: Can't find a FOF control sector with tag %d\n", foftag); return; } - - EV_CrumbleChain(sec, rover); } } break; @@ -3277,6 +3267,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); sector_t *sec; // Sector that the FOF is visible (or not visible) in ffloor_t *rover; // FOF to vanish/un-vanish + boolean foundrover = false; // for debug, "Can't find a FOF" message ffloortype_e oldflags; // store FOF's old flags for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;) @@ -3292,26 +3283,28 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) for (rover = sec->ffloors; rover; rover = rover->next) { if (rover->master->frontsector->tag == foftag) - break; + { + foundrover = true; + + oldflags = rover->flags; + + // Abracadabra! + if (line->flags & ML_NOCLIMB) + rover->flags |= FF_EXISTS; + else + rover->flags &= ~FF_EXISTS; + + // if flags changed, reset sector's light list + if (rover->flags != oldflags) + sec->moved = true; + } } - if (!rover) + if (!foundrover) { CONS_Debug(DBG_GAMELOGIC, "Line type 445 Executor: Can't find a FOF control sector with tag %d\n", foftag); return; } - - oldflags = rover->flags; - - // Abracadabra! - if (line->flags & ML_NOCLIMB) - rover->flags |= FF_EXISTS; - else - rover->flags &= ~FF_EXISTS; - - // if flags changed, reset sector's light list - if (rover->flags != oldflags) - sec->moved = true; } } break; @@ -3322,6 +3315,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); sector_t *sec; // Sector that the FOF is visible in ffloor_t *rover; // FOF that we are going to make fall down + boolean foundrover = false; // for debug, "Can't find a FOF" message player_t *player = NULL; // player that caused FOF to fall boolean respawn = true; // should the fallen FOF respawn? @@ -3344,19 +3338,21 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) for (rover = sec->ffloors; rover; rover = rover->next) { if (rover->master->frontsector->tag == foftag) - break; + { + foundrover = true; + + if (line->flags & ML_BLOCKMONSTERS) // FOF flags determine respawn ability instead? + respawn = !(rover->flags & FF_NORETURN) ^ !!(line->flags & ML_NOCLIMB); // no climb inverts + + EV_StartCrumble(rover->master->frontsector, rover, (rover->flags & FF_FLOATBOB), player, rover->alpha, respawn); + } } - if (!rover) + if (!foundrover) { CONS_Debug(DBG_GAMELOGIC, "Line type 446 Executor: Can't find a FOF control sector with tag %d\n", foftag); return; } - - if (line->flags & ML_BLOCKMONSTERS) // FOF flags determine respawn ability instead? - respawn = !(rover->flags & FF_NORETURN) ^ !!(line->flags & ML_NOCLIMB); // no climb inverts - - EV_StartCrumble(rover->master->frontsector, rover, (rover->flags & FF_FLOATBOB), player, rover->alpha, respawn); } } break; @@ -3489,6 +3485,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); sector_t *sec; // Sector that the FOF is visible in ffloor_t *rover; // FOF that we are going to operate + boolean foundrover = false; // for debug, "Can't find a FOF" message for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;) { @@ -3503,38 +3500,40 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) for (rover = sec->ffloors; rover; rover = rover->next) { if (rover->master->frontsector->tag == foftag) - break; + { + foundrover = true; + + // If fading an invisible FOF whose render flags we did not yet set, + // initialize its alpha to 1 + // for relative alpha calc + if (!(line->flags & ML_NOCLIMB) && // do translucent + (rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE + !(rover->spawnflags & FF_RENDERSIDES) && + !(rover->spawnflags & FF_RENDERPLANES) && + !(rover->flags & FF_RENDERALL)) + rover->alpha = 1; + + P_RemoveFakeFloorFader(rover); + P_FadeFakeFloor(rover, + rover->alpha, + max(1, min(256, (line->flags & ML_EFFECT3) ? rover->alpha + destvalue : destvalue)), + 0, // set alpha immediately + false, NULL, // tic-based logic + false, // do not handle FF_EXISTS + !(line->flags & ML_NOCLIMB), // handle FF_TRANSLUCENT + false, // do not handle lighting + false, // do not handle colormap + false, // do not handle collision + false, // do not do ghost fade (no collision during fade) + true); // use exact alpha values (for opengl) + } } - if (!rover) + if (!foundrover) { CONS_Debug(DBG_GAMELOGIC, "Line type 452 Executor: Can't find a FOF control sector with tag %d\n", foftag); return; } - - // If fading an invisible FOF whose render flags we did not yet set, - // initialize its alpha to 1 - // for relative alpha calc - if (!(line->flags & ML_NOCLIMB) && // do translucent - (rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE - !(rover->spawnflags & FF_RENDERSIDES) && - !(rover->spawnflags & FF_RENDERPLANES) && - !(rover->flags & FF_RENDERALL)) - rover->alpha = 1; - - P_RemoveFakeFloorFader(rover); - P_FadeFakeFloor(rover, - rover->alpha, - max(1, min(256, (line->flags & ML_EFFECT3) ? rover->alpha + destvalue : destvalue)), - 0, // set alpha immediately - false, NULL, // tic-based logic - false, // do not handle FF_EXISTS - !(line->flags & ML_NOCLIMB), // handle FF_TRANSLUCENT - false, // do not handle lighting - false, // do not handle colormap - false, // do not handle collision - false, // do not do ghost fade (no collision during fade) - true); // use exact alpha values (for opengl) } break; } @@ -3549,6 +3548,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); sector_t *sec; // Sector that the FOF is visible in ffloor_t *rover; // FOF that we are going to operate + boolean foundrover = false; // for debug, "Can't find a FOF" message size_t j = 0; // sec->ffloors is saved as ffloor #0, ss->ffloors->next is #1, etc for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;) @@ -3564,64 +3564,66 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) for (rover = sec->ffloors; rover; rover = rover->next) { if (rover->master->frontsector->tag == foftag) - break; + { + foundrover = true; + + // Prevent continuous execs from interfering on an existing fade + if (!(line->flags & ML_EFFECT5) + && rover->fadingdata) + //&& ((fade_t*)rover->fadingdata)->timer > (ticbased ? 2 : speed*2)) + { + CONS_Debug(DBG_GAMELOGIC, "Line type 453 Executor: Fade FOF thinker already exists, timer: %d\n", ((fade_t*)rover->fadingdata)->timer); + continue; + } + + if (speed > 0) + P_AddFakeFloorFader(rover, secnum, j, + destvalue, + speed, + (line->flags & ML_EFFECT4), // tic-based logic + (line->flags & ML_EFFECT3), // Relative destvalue + !(line->flags & ML_BLOCKMONSTERS), // do not handle FF_EXISTS + !(line->flags & ML_NOCLIMB), // do not handle FF_TRANSLUCENT + !(line->flags & ML_EFFECT2), // do not handle lighting + !(line->flags & ML_EFFECT2), // do not handle colormap (ran out of flags) + !(line->flags & ML_BOUNCY), // do not handle collision + (line->flags & ML_EFFECT1), // do ghost fade (no collision during fade) + (line->flags & ML_TFERLINE)); // use exact alpha values (for opengl) + else + { + // If fading an invisible FOF whose render flags we did not yet set, + // initialize its alpha to 1 + // for relative alpha calc + if (!(line->flags & ML_NOCLIMB) && // do translucent + (rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE + !(rover->spawnflags & FF_RENDERSIDES) && + !(rover->spawnflags & FF_RENDERPLANES) && + !(rover->flags & FF_RENDERALL)) + rover->alpha = 1; + + P_RemoveFakeFloorFader(rover); + P_FadeFakeFloor(rover, + rover->alpha, + max(1, min(256, (line->flags & ML_EFFECT3) ? rover->alpha + destvalue : destvalue)), + 0, // set alpha immediately + false, NULL, // tic-based logic + !(line->flags & ML_BLOCKMONSTERS), // do not handle FF_EXISTS + !(line->flags & ML_NOCLIMB), // do not handle FF_TRANSLUCENT + !(line->flags & ML_EFFECT2), // do not handle lighting + !(line->flags & ML_EFFECT2), // do not handle colormap (ran out of flags) + !(line->flags & ML_BOUNCY), // do not handle collision + (line->flags & ML_EFFECT1), // do ghost fade (no collision during fade) + (line->flags & ML_TFERLINE)); // use exact alpha values (for opengl) + } + } j++; } - if (!rover) + if (!foundrover) { CONS_Debug(DBG_GAMELOGIC, "Line type 453 Executor: Can't find a FOF control sector with tag %d\n", foftag); return; } - - // Prevent continuous execs from interfering on an existing fade - if (!(line->flags & ML_EFFECT5) - && rover->fadingdata) - //&& ((fade_t*)rover->fadingdata)->timer > (ticbased ? 2 : speed*2)) - { - CONS_Debug(DBG_GAMELOGIC, "Line type 453 Executor: Fade FOF thinker already exists, timer: %d\n", ((fade_t*)rover->fadingdata)->timer); - continue; - } - - if (speed > 0) - P_AddFakeFloorFader(rover, secnum, j, - destvalue, - speed, - (line->flags & ML_EFFECT4), // tic-based logic - (line->flags & ML_EFFECT3), // Relative destvalue - !(line->flags & ML_BLOCKMONSTERS), // do not handle FF_EXISTS - !(line->flags & ML_NOCLIMB), // do not handle FF_TRANSLUCENT - !(line->flags & ML_EFFECT2), // do not handle lighting - !(line->flags & ML_EFFECT2), // do not handle colormap (ran out of flags) - !(line->flags & ML_BOUNCY), // do not handle collision - (line->flags & ML_EFFECT1), // do ghost fade (no collision during fade) - (line->flags & ML_TFERLINE)); // use exact alpha values (for opengl) - else - { - // If fading an invisible FOF whose render flags we did not yet set, - // initialize its alpha to 1 - // for relative alpha calc - if (!(line->flags & ML_NOCLIMB) && // do translucent - (rover->spawnflags & FF_NOSHADE) && // do not include light blocks, which don't set FF_NOSHADE - !(rover->spawnflags & FF_RENDERSIDES) && - !(rover->spawnflags & FF_RENDERPLANES) && - !(rover->flags & FF_RENDERALL)) - rover->alpha = 1; - - P_RemoveFakeFloorFader(rover); - P_FadeFakeFloor(rover, - rover->alpha, - max(1, min(256, (line->flags & ML_EFFECT3) ? rover->alpha + destvalue : destvalue)), - 0, // set alpha immediately - false, NULL, // tic-based logic - !(line->flags & ML_BLOCKMONSTERS), // do not handle FF_EXISTS - !(line->flags & ML_NOCLIMB), // do not handle FF_TRANSLUCENT - !(line->flags & ML_EFFECT2), // do not handle lighting - !(line->flags & ML_EFFECT2), // do not handle colormap (ran out of flags) - !(line->flags & ML_BOUNCY), // do not handle collision - (line->flags & ML_EFFECT1), // do ghost fade (no collision during fade) - (line->flags & ML_TFERLINE)); // use exact alpha values (for opengl) - } } break; } @@ -3632,6 +3634,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS); sector_t *sec; // Sector that the FOF is visible in ffloor_t *rover; // FOF that we are going to operate + boolean foundrover = false; // for debug, "Can't find a FOF" message for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;) { @@ -3646,17 +3649,19 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) for (rover = sec->ffloors; rover; rover = rover->next) { if (rover->master->frontsector->tag == foftag) - break; + { + foundrover = true; + + P_ResetFakeFloorFader(rover, NULL, + !(line->flags & ML_BLOCKMONSTERS)); // do not finalize collision flags + } } - if (!rover) + if (!foundrover) { CONS_Debug(DBG_GAMELOGIC, "Line type 454 Executor: Can't find a FOF control sector with tag %d\n", foftag); return; } - - P_ResetFakeFloorFader(rover, NULL, - !(line->flags & ML_BLOCKMONSTERS)); // do not finalize collision flags } break; } @@ -3755,6 +3760,68 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) P_ResetColormapFader(§ors[secnum]); break; + case 457: // Track mobj angle to point + if (mo) + { + INT32 failureangle = min(max(abs(sides[line->sidenum[0]].textureoffset>>FRACBITS), 0), 360) * ANG1; + INT32 failuredelay = abs(sides[line->sidenum[0]].rowoffset>>FRACBITS); + INT32 failureexectag = line->sidenum[1] != 0xffff ? + (INT32)(sides[line->sidenum[1]].textureoffset>>FRACBITS) : 0; + boolean persist = (line->flags & ML_EFFECT2); + mobj_t *anchormo; + + if ((secnum = P_FindSectorFromLineTag(line, -1)) < 0) + return; + + anchormo = P_GetObjectTypeInSectorNum(MT_ANGLEMAN, secnum); + if (!anchormo) + return; + + mo->eflags |= MFE_TRACERANGLE; + P_SetTarget(&mo->tracer, anchormo); + mo->lastlook = persist; // don't disable behavior after first failure + mo->extravalue1 = failureangle; // angle to exceed for failure state + mo->extravalue2 = failureexectag; // exec tag for failure state (angle is not within range) + mo->cusval = mo->cvmem = failuredelay; // cusval = tics to allow failure before line trigger; cvmem = decrement timer + } + break; + + case 458: // Stop tracking mobj angle to point + if (mo && (mo->eflags & MFE_TRACERANGLE)) + { + mo->eflags &= ~MFE_TRACERANGLE; + P_SetTarget(&mo->tracer, NULL); + mo->lastlook = mo->cvmem = mo->cusval = mo->extravalue1 = mo->extravalue2 = 0; + } + break; + + case 459: // Control Text Prompt + // console player only unless NOCLIMB is set + if (mo && mo->player && P_IsLocalPlayer(mo->player) && (!bot || bot != mo)) + { + INT32 promptnum = max(0, (sides[line->sidenum[0]].textureoffset>>FRACBITS)-1); + INT32 pagenum = max(0, (sides[line->sidenum[0]].rowoffset>>FRACBITS)-1); + INT32 postexectag = abs((line->sidenum[1] != 0xFFFF) ? sides[line->sidenum[1]].textureoffset>>FRACBITS : line->tag); + + boolean closetextprompt = (line->flags & ML_BLOCKMONSTERS); + //boolean allplayers = (line->flags & ML_NOCLIMB); + boolean runpostexec = (line->flags & ML_EFFECT1); + boolean blockcontrols = !(line->flags & ML_EFFECT2); + boolean freezerealtime = !(line->flags & ML_EFFECT3); + //boolean freezethinkers = (line->flags & ML_EFFECT4); + boolean callbynamedtag = (line->flags & ML_TFERLINE); + + if (closetextprompt) + F_EndTextPrompt(false, false); + else + { + if (callbynamedtag && sides[line->sidenum[0]].text && sides[line->sidenum[0]].text[0]) + F_GetPromptPageByNamedTag(sides[line->sidenum[0]].text, &promptnum, &pagenum); + F_StartTextPrompt(promptnum, pagenum, mo, runpostexec ? postexectag : 0, blockcontrols, freezerealtime); + } + } + break; + #ifdef POLYOBJECTS case 480: // Polyobj_DoorSlide case 481: // Polyobj_DoorSwing diff --git a/src/p_tick.c b/src/p_tick.c index a51ce2eb6..eb47e0c36 100644 --- a/src/p_tick.c +++ b/src/p_tick.c @@ -56,12 +56,12 @@ void Command_Numthinkers_f(void) CONS_Printf(M_GetText("numthinkers <#>: Count number of thinkers\n")); CONS_Printf( "\t1: P_MobjThinker\n" - "\t2: P_RainThinker\n" - "\t3: P_SnowThinker\n" - "\t4: P_NullPrecipThinker\n" - "\t5: T_Friction\n" - "\t6: T_Pusher\n" - "\t7: P_RemoveThinkerDelayed\n"); + /*"\t2: P_RainThinker\n" + "\t3: P_SnowThinker\n"*/ + "\t2: P_NullPrecipThinker\n" + "\t3: T_Friction\n" + "\t4: T_Pusher\n" + "\t5: P_RemoveThinkerDelayed\n"); return; } @@ -73,27 +73,27 @@ void Command_Numthinkers_f(void) action = (actionf_p1)P_MobjThinker; CONS_Printf(M_GetText("Number of %s: "), "P_MobjThinker"); break; - case 2: + /*case 2: action = (actionf_p1)P_RainThinker; CONS_Printf(M_GetText("Number of %s: "), "P_RainThinker"); break; case 3: action = (actionf_p1)P_SnowThinker; CONS_Printf(M_GetText("Number of %s: "), "P_SnowThinker"); - break; - case 4: + break;*/ + case 2: action = (actionf_p1)P_NullPrecipThinker; CONS_Printf(M_GetText("Number of %s: "), "P_NullPrecipThinker"); break; - case 5: + case 3: action = (actionf_p1)T_Friction; CONS_Printf(M_GetText("Number of %s: "), "T_Friction"); break; - case 6: + case 4: action = (actionf_p1)T_Pusher; CONS_Printf(M_GetText("Number of %s: "), "T_Pusher"); break; - case 7: + case 5: action = (actionf_p1)P_RemoveThinkerDelayed; CONS_Printf(M_GetText("Number of %s: "), "P_RemoveThinkerDelayed"); break; diff --git a/src/p_user.c b/src/p_user.c index 736743b3b..2639ab148 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -8777,9 +8777,15 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall subsector_t *newsubsec; fixed_t f1, f2; - cameranoclip = (player->powers[pw_carry] == CR_NIGHTSMODE || player->pflags & PF_NOCLIP) || (player->mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)); // Noclipping player camera noclips too!! + // We probably shouldn't move the camera if there is no player or player mobj somehow + if (!player || !player->mo) + return true; - if (!(player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD)) + mo = player->mo; + + cameranoclip = (player->powers[pw_carry] == CR_NIGHTSMODE || player->pflags & PF_NOCLIP) || (mo->flags & (MF_NOCLIP|MF_NOCLIPHEIGHT)); // Noclipping player camera noclips too!! + + if (!(player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || tutorialmode)) { if (player->spectator) // force cam off for spectators return true; @@ -8798,7 +8804,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall else if (player == &players[secondarydisplayplayer]) focusangle = localangle2; else - focusangle = player->mo->angle; + focusangle = mo->angle; if (thiscam == &camera) camrotate = cv_cam_rotate.value; else if (thiscam == &camera2) @@ -8810,17 +8816,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall return true; } - if (!player || !player->mo) - return true; - - mo = player->mo; - thiscam->radius = FixedMul(20*FRACUNIT, mo->scale); thiscam->height = FixedMul(16*FRACUNIT, mo->scale); - if (!mo) - return true; - // Don't run while respawning from a starpost // Inu 4/8/13 Why not?! // if (leveltime > 0 && timeinmap <= 0) @@ -8828,7 +8826,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (player->powers[pw_carry] == CR_NIGHTSMODE) { - focusangle = player->mo->angle; + focusangle = mo->angle; focusaiming = 0; } else if (player == &players[consoleplayer]) @@ -8843,14 +8841,23 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall } else { - focusangle = player->mo->angle; + focusangle = mo->angle; focusaiming = player->aiming; } if (P_CameraThinker(player, thiscam, resetcalled)) return true; - if (thiscam == &camera) + if (tutorialmode) + { + // force defaults because we have a camera look section + camspeed = (INT32)(atof(cv_cam_speed.defaultvalue) * FRACUNIT); + camstill = (!stricmp(cv_cam_still.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)); + } + else if (thiscam == &camera) { camspeed = cv_cam_speed.value; camstill = cv_cam_still.value; @@ -8890,12 +8897,12 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall angle = R_PointToAngle2(player->axis1->x, player->axis1->y, player->axis2->x, player->axis2->y); angle += ANGLE_90; } - else if (player->mo->target) + else if (mo->target) { - if (player->mo->target->flags2 & MF2_AMBUSH) - angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y); + if (mo->target->flags2 & MF2_AMBUSH) + angle = R_PointToAngle2(mo->target->x, mo->target->y, mo->x, mo->y); else - angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->target->x, player->mo->target->y); + angle = R_PointToAngle2(mo->x, mo->y, mo->target->x, mo->target->y); } } else if (P_AnalogMove(player)) // Analog @@ -8984,7 +8991,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall if (twodlevel || (mo->flags2 & MF2_TWOD)) { // Camera doesn't ALWAYS need to move, only when running... - if (abs(player->mo->momx) > 10) + if (abs(mo->momx) > 10) { // Move the camera all smooth-like, not jerk it around... if (mo->momx > 0) @@ -9291,24 +9298,20 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall // Make player translucent if camera is too close (only in single player). if (!(multiplayer || netgame) && !splitscreen) { - fixed_t vx = 0, vy = 0; - if (player->awayviewtics) { + fixed_t vx = thiscam->x, vy = thiscam->y; + if (player->awayviewtics && player->awayviewmobj != NULL) // Camera must obviously exist + { vx = player->awayviewmobj->x; vy = player->awayviewmobj->y; } - else - { - vx = thiscam->x; - vy = thiscam->y; - } - if (P_AproxDistance(vx - player->mo->x, vy - player->mo->y) < FixedMul(48*FRACUNIT, mo->scale)) - player->mo->flags2 |= MF2_SHADOW; + if (P_AproxDistance(vx - mo->x, vy - mo->y) < FixedMul(48*FRACUNIT, mo->scale)) + mo->flags2 |= MF2_SHADOW; else - player->mo->flags2 &= ~MF2_SHADOW; + mo->flags2 &= ~MF2_SHADOW; } else - player->mo->flags2 &= ~MF2_SHADOW; + mo->flags2 &= ~MF2_SHADOW; /* if (!resetcalled && (player->powers[pw_carry] == CR_NIGHTSMODE && player->exiting)) { @@ -9623,8 +9626,9 @@ void P_PlayerThink(player_t *player) if (player->flashcount) player->flashcount--; - if (player->awayviewtics) - player->awayviewtics--; + // By the time P_MoveChaseCamera is called, this might be zero. Do not do it here. + //if (player->awayviewtics) + // player->awayviewtics--; /// \note do this in the cheat code if (player->pflags & PF_NOCLIP) @@ -10597,6 +10601,9 @@ void P_PlayerAfterThink(player_t *player) } } + if (player->awayviewtics) + player->awayviewtics--; + // spectator invisibility and nogravity. if ((netgame || multiplayer) && player->spectator) { diff --git a/src/r_draw8.c b/src/r_draw8.c index 76eb58c0d..bda146546 100644 --- a/src/r_draw8.c +++ b/src/r_draw8.c @@ -261,7 +261,7 @@ void R_Draw2sMultiPatchTranslucentColumn_8(void) val = source[frac>>FRACBITS]; if (val != TRANSPARENTPIXEL) - *dest = colormap[*(transmap + (val<<8) + (*dest))]; + *dest = *(transmap + (colormap[val]<<8) + (*dest)); dest += vid.width; @@ -281,12 +281,12 @@ void R_Draw2sMultiPatchTranslucentColumn_8(void) { val = source[(frac>>FRACBITS) & heightmask]; if (val != TRANSPARENTPIXEL) - *dest = colormap[*(transmap + (val<<8) + (*dest))]; + *dest = *(transmap + (colormap[val]<<8) + (*dest)); dest += vid.width; frac += fracstep; val = source[(frac>>FRACBITS) & heightmask]; if (val != TRANSPARENTPIXEL) - *dest = colormap[*(transmap + (val<<8) + (*dest))]; + *dest = *(transmap + (colormap[val]<<8) + (*dest)); dest += vid.width; frac += fracstep; } @@ -294,7 +294,7 @@ void R_Draw2sMultiPatchTranslucentColumn_8(void) { val = source[(frac>>FRACBITS) & heightmask]; if (val != TRANSPARENTPIXEL) - *dest = colormap[*(transmap + (val<<8) + (*dest))]; + *dest = *(transmap + (colormap[val]<<8) + (*dest)); } } } diff --git a/src/r_main.c b/src/r_main.c index bfca180d0..98ce978a6 100644 --- a/src/r_main.c +++ b/src/r_main.c @@ -913,7 +913,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 || gamestate == GS_TITLESCREEN) + if (player->climbing || (player->powers[pw_carry] == CR_NIGHTSMODE) || player->playerstate == PST_DEAD || gamestate == GS_TITLESCREEN || tutorialmode) chasecam = true; // force chasecam on else if (player->spectator) // no spectator chasecam chasecam = false; // force chasecam off diff --git a/src/r_plane.h b/src/r_plane.h index 16c8c12a4..dff58669a 100644 --- a/src/r_plane.h +++ b/src/r_plane.h @@ -87,7 +87,7 @@ extern lighttable_t **planezlight; extern fixed_t *yslope; extern fixed_t distscale[MAXVIDWIDTH]; -FUNCMATH void R_InitPlanes(void); +void R_InitPlanes(void); void R_PortalStoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor, fixed_t *scale); void R_PortalRestoreClipValues(INT32 start, INT32 end, INT16 *ceil, INT16 *floor, fixed_t *scale); void R_ClearPlanes(void); diff --git a/src/r_splats.h b/src/r_splats.h index c0ba6881c..349d8fa7a 100644 --- a/src/r_splats.h +++ b/src/r_splats.h @@ -63,11 +63,7 @@ typedef struct floorsplat_s fixed_t P_SegLength(seg_t *seg); // call at P_SetupLevel() -#if !(defined (WALLSPLATS) || defined (FLOORSPLATS)) -FUNCMATH void R_ClearLevelSplats(void); -#else void R_ClearLevelSplats(void); -#endif #ifdef WALLSPLATS void R_AddWallSplat(line_t *wallline, INT16 sectorside, const char *patchname, fixed_t top, diff --git a/src/r_things.c b/src/r_things.c index f4a0fd28c..a6a7d9877 100644 --- a/src/r_things.c +++ b/src/r_things.c @@ -1525,6 +1525,17 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) return; } + // okay, we can't return now except for vertical clipping... this is a hack, but weather isn't networked, so it should be ok + if (!(thing->precipflags & PCF_THUNK)) + { + if (thing->precipflags & PCF_RAIN) + P_RainThinker(thing); + else + P_SnowThinker(thing); + thing->precipflags |= PCF_THUNK; + } + + //SoM: 3/17/2000: Disregard sprites that are out of view.. gzt = thing->z + spritecachedinfo[lump].topoffset; gz = gzt - spritecachedinfo[lump].height; @@ -1642,8 +1653,10 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) approx_dist = P_AproxDistance(viewx-thing->x, viewy-thing->y); - if (approx_dist <= limit_dist) - R_ProjectSprite(thing); + if (approx_dist > limit_dist) + continue; + + R_ProjectSprite(thing); } } else @@ -1664,8 +1677,10 @@ void R_AddSprites(sector_t *sec, INT32 lightlevel) approx_dist = P_AproxDistance(viewx-precipthing->x, viewy-precipthing->y); - if (approx_dist <= limit_dist) - R_ProjectPrecipitationSprite(precipthing); + if (approx_dist > limit_dist) + continue; + + R_ProjectPrecipitationSprite(precipthing); } } else diff --git a/src/s_sound.c b/src/s_sound.c index 2bf60b087..f25e4af13 100644 --- a/src/s_sound.c +++ b/src/s_sound.c @@ -37,6 +37,7 @@ extern INT32 msg_id; #include "r_sky.h" // skyflatnum #include "p_local.h" // camera info #include "fastcmp.h" +#include "m_misc.h" // for tunes command #if defined(HAVE_BLUA) && defined(HAVE_LUA_MUSICPLUS) #include "lua_hook.h" // MusicChange hook @@ -51,6 +52,8 @@ static INT32 S_AdjustSoundParams(const mobj_t *listener, const mobj_t *source, I CV_PossibleValue_t soundvolume_cons_t[] = {{0, "MIN"}, {31, "MAX"}, {0, NULL}}; static void SetChannelsNum(void); +static void Command_Tunes_f(void); +static void Command_RestartAudio_f(void); // commands for music and sound servers #ifdef MUSSERV @@ -96,6 +99,7 @@ consvar_t cv_closedcaptioning = {"closedcaptioning", "Off", CV_SAVE|CV_CALL, CV_ consvar_t cv_numChannels = {"snd_channels", "32", CV_SAVE|CV_CALL, CV_Unsigned, SetChannelsNum, 0, NULL, NULL, 0, 0, NULL}; static consvar_t surround = {"surround", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; +consvar_t cv_resetmusic = {"resetmusic", "No", CV_SAVE, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL}; #define S_MAX_VOLUME 127 @@ -251,6 +255,11 @@ void S_RegisterSoundStuff(void) #endif CV_RegisterVar(&surround); CV_RegisterVar(&cv_samplerate); + CV_RegisterVar(&cv_resetmusic); + + COM_AddCommand("tunes", Command_Tunes_f); + COM_AddCommand("restartaudio", Command_RestartAudio_f); + #if defined (macintosh) && !defined (HAVE_SDL) // mp3 playlist stuff { @@ -630,7 +639,7 @@ void S_StartSoundAtVolume(const void *origin_p, sfxenum_t sfx_id, INT32 volume) // Assigns the handle to one of the channels in the // mix/output buffer. - channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority); + channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority, cnum); } dontplay: @@ -683,7 +692,7 @@ dontplay: // Assigns the handle to one of the channels in the // mix/output buffer. - channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority); + channels[cnum].handle = I_StartSound(sfx_id, volume, sep, pitch, priority, cnum); } void S_StartSound(const void *origin, sfxenum_t sfx_id) @@ -1747,3 +1756,88 @@ void S_Start(void) S_StopMusic(); S_ChangeMusicEx(mapmusname, mapmusflags, true, mapmusposition, 0, 0); } + +static void Command_Tunes_f(void) +{ + const char *tunearg; + UINT16 tunenum, track = 0; + const size_t argc = COM_Argc(); + + if (argc < 2) //tunes slot ... + { + CONS_Printf("tunes [track] [speed] / <-show> / <-default> / <-none>:\n"); + CONS_Printf(M_GetText("Play an arbitrary music lump. If a map number is used, 'MAP##M' is played.\n")); + CONS_Printf(M_GetText("If the format supports multiple songs, you can specify which one to play.\n\n")); + CONS_Printf(M_GetText("* With \"-show\", shows the currently playing tune and track.\n")); + CONS_Printf(M_GetText("* With \"-default\", returns to the default music for the map.\n")); + CONS_Printf(M_GetText("* With \"-none\", any music playing will be stopped.\n")); + return; + } + + tunearg = COM_Argv(1); + tunenum = (UINT16)atoi(tunearg); + track = 0; + + if (!strcasecmp(tunearg, "-show")) + { + CONS_Printf(M_GetText("The current tune is: %s [track %d]\n"), + mapmusname, (mapmusflags & MUSIC_TRACKMASK)); + return; + } + if (!strcasecmp(tunearg, "-none")) + { + S_StopMusic(); + return; + } + else if (!strcasecmp(tunearg, "-default")) + { + tunearg = mapheaderinfo[gamemap-1]->musname; + track = mapheaderinfo[gamemap-1]->mustrack; + } + else if (!tunearg[2] && toupper(tunearg[0]) >= 'A' && toupper(tunearg[0]) <= 'Z') + tunenum = (UINT16)M_MapNumber(tunearg[0], tunearg[1]); + + if (tunenum && tunenum >= 1036) + { + CONS_Alert(CONS_NOTICE, M_GetText("Valid music slots are 1 to 1035.\n")); + return; + } + if (!tunenum && strlen(tunearg) > 6) // This is automatic -- just show the error just in case + CONS_Alert(CONS_NOTICE, M_GetText("Music name too long - truncated to six characters.\n")); + + if (argc > 2) + track = (UINT16)atoi(COM_Argv(2))-1; + + if (tunenum) + snprintf(mapmusname, 7, "%sM", G_BuildMapName(tunenum)); + else + strncpy(mapmusname, tunearg, 7); + mapmusname[6] = 0; + mapmusflags = (track & MUSIC_TRACKMASK); + + S_ChangeMusic(mapmusname, mapmusflags, true); + + if (argc > 3) + { + float speed = (float)atof(COM_Argv(3)); + if (speed > 0.0f) + S_SpeedMusic(speed); + } +} + +static void Command_RestartAudio_f(void) +{ + S_StopMusic(); + S_StopSounds(); + I_ShutdownMusic(); + I_ShutdownSound(); + I_StartupSound(); + I_InitMusic(); + +// These must be called or no sound and music until manually set. + + I_SetSfxVolume(cv_soundvolume.value); + S_SetMusicVolume(cv_digmusicvolume.value, cv_midimusicvolume.value); + if (Playing()) // Gotta make sure the player is in a level + P_RestoreMusic(&players[consoleplayer]); +} diff --git a/src/s_sound.h b/src/s_sound.h index 7a962d11a..236805ab9 100644 --- a/src/s_sound.h +++ b/src/s_sound.h @@ -26,6 +26,7 @@ extern consvar_t stereoreverse; extern consvar_t cv_soundvolume, cv_closedcaptioning, cv_digmusicvolume, cv_midimusicvolume; extern consvar_t cv_numChannels; +extern consvar_t cv_resetmusic; #ifdef SNDSERV extern consvar_t sndserver_cmd, sndserver_arg; diff --git a/src/screen.c b/src/screen.c index 9bbcb995c..b3e94091f 100644 --- a/src/screen.c +++ b/src/screen.c @@ -69,11 +69,7 @@ consvar_t cv_scr_height = {"scr_height", "800", CV_SAVE, CV_Unsigned, NULL, 0, N consvar_t cv_scr_depth = {"scr_depth", "16 bits", CV_SAVE, scr_depth_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL}; consvar_t cv_renderview = {"renderview", "On", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; -#ifdef DIRECTFULLSCREEN -static FUNCMATH void SCR_ChangeFullscreen (void); -#else static void SCR_ChangeFullscreen (void); -#endif consvar_t cv_fullscreen = {"fullscreen", "Yes", CV_SAVE|CV_CALL, CV_YesNo, SCR_ChangeFullscreen, 0, NULL, NULL, 0, 0, NULL}; diff --git a/src/sdl/i_cdmus.c b/src/sdl/i_cdmus.c index 3105f5122..5d086e73a 100644 --- a/src/sdl/i_cdmus.c +++ b/src/sdl/i_cdmus.c @@ -12,19 +12,19 @@ consvar_t cd_volume = {"cd_volume","31",CV_SAVE,soundvolume_cons_t, NULL, 0, NUL consvar_t cdUpdate = {"cd_update","1",CV_SAVE, NULL, NULL, 0, NULL, NULL, 0, 0, NULL}; -FUNCMATH void I_InitCD(void){} +void I_InitCD(void){} -FUNCMATH void I_StopCD(void){} +void I_StopCD(void){} -FUNCMATH void I_PauseCD(void){} +void I_PauseCD(void){} -FUNCMATH void I_ResumeCD(void){} +void I_ResumeCD(void){} -FUNCMATH void I_ShutdownCD(void){} +void I_ShutdownCD(void){} -FUNCMATH void I_UpdateCD(void){} +void I_UpdateCD(void){} -FUNCMATH void I_PlayCD(UINT8 track, UINT8 looping) +void I_PlayCD(UINT8 track, UINT8 looping) { (void)track; (void)looping; diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index c8fcd080e..0963cc288 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -1,8 +1,11 @@ // Emacs style mode select -*- C++ -*- +// +// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // // Copyright (C) 1993-1996 by id Software, Inc. // Portions Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 2014-2018 by Sonic Team Junior. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -124,6 +127,10 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T); #include "macosx/mac_resources.h" #endif +#ifndef errno +#include +#endif + // Locations for searching the srb2.pk3 #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) #define DEFAULTWADLOCATION1 "/usr/local/share/games/SRB2" @@ -1149,6 +1156,7 @@ static void I_ShutdownJoystick2(void) D_PostEvent(&event); } + joystick2_started = 0; JoyReset(&JoyInfo2); if (!joystick_started && !joystick2_started && SDL_WasInit(SDL_INIT_JOYSTICK) == SDL_INIT_JOYSTICK) { @@ -1678,7 +1686,7 @@ static void I_ShutdownMouse2(void) EscapeCommFunction(mouse2filehandle, CLRRTS); PurgeComm(mouse2filehandle, PURGE_TXABORT | PURGE_RXABORT | - PURGE_TXCLEAR | PURGE_RXCLEAR); + PURGE_TXCLEAR | PURGE_RXCLEAR); CloseHandle(mouse2filehandle); @@ -1871,11 +1879,11 @@ void I_StartupMouse2(void) { // COM file handle mouse2filehandle = CreateFileA(cv_mouse2port.string, GENERIC_READ | GENERIC_WRITE, - 0, // exclusive access - NULL, // no security attrs - OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL, - NULL); + 0, // exclusive access + NULL, // no security attrs + OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, + NULL); if (mouse2filehandle == INVALID_HANDLE_VALUE) { INT32 e = GetLastError(); @@ -1895,7 +1903,7 @@ void I_StartupMouse2(void) // purge buffers PurgeComm(mouse2filehandle, PURGE_TXABORT | PURGE_RXABORT - | PURGE_TXCLEAR | PURGE_RXCLEAR); + | PURGE_TXCLEAR | PURGE_RXCLEAR); // setup port to 1200 7N1 dcb.DCBlength = sizeof (DCB); @@ -1921,14 +1929,14 @@ void I_StartupMouse2(void) // // I_Tactile // -FUNCMATH void I_Tactile(FFType pFFType, const JoyFF_t *FFEffect) +void I_Tactile(FFType pFFType, const JoyFF_t *FFEffect) { // UNUSED. (void)pFFType; (void)FFEffect; } -FUNCMATH void I_Tactile2(FFType pFFType, const JoyFF_t *FFEffect) +void I_Tactile2(FFType pFFType, const JoyFF_t *FFEffect) { // UNUSED. (void)pFFType; @@ -1939,7 +1947,7 @@ FUNCMATH void I_Tactile2(FFType pFFType, const JoyFF_t *FFEffect) */ static ticcmd_t emptycmd; -FUNCMATH ticcmd_t *I_BaseTiccmd(void) +ticcmd_t *I_BaseTiccmd(void) { return &emptycmd; } @@ -1948,7 +1956,7 @@ FUNCMATH ticcmd_t *I_BaseTiccmd(void) */ static ticcmd_t emptycmd2; -FUNCMATH ticcmd_t *I_BaseTiccmd2(void) +ticcmd_t *I_BaseTiccmd2(void) { return &emptycmd2; } @@ -2024,7 +2032,7 @@ static void I_ShutdownTimer(void) tic_t I_GetTime (void) { static Uint32 basetime = 0; - Uint32 ticks = SDL_GetTicks(); + Uint32 ticks = SDL_GetTicks(); if (!basetime) basetime = ticks; @@ -2042,7 +2050,7 @@ tic_t I_GetTime (void) // //I_StartupTimer // -FUNCMATH void I_StartupTimer(void) +void I_StartupTimer(void) { #ifdef _WIN32 // for win2k time bug @@ -2090,7 +2098,6 @@ INT32 I_StartupSystem(void) return 0; } - // // I_Quit // @@ -2142,11 +2149,11 @@ void I_WaitVBL(INT32 count) SDL_Delay(count); } -FUNCMATH void I_BeginRead(void) +void I_BeginRead(void) { } -FUNCMATH void I_EndRead(void) +void I_EndRead(void) { } @@ -2369,7 +2376,7 @@ void I_GetDiskFreeSpace(INT64 *freespace) { DWORD SectorsPerCluster, BytesPerSector, NumberOfFreeClusters, TotalNumberOfClusters; GetDiskFreeSpace(NULL, &SectorsPerCluster, &BytesPerSector, - &NumberOfFreeClusters, &TotalNumberOfClusters); + &NumberOfFreeClusters, &TotalNumberOfClusters); *freespace = BytesPerSector*SectorsPerCluster*NumberOfFreeClusters; } #else // Dummy for platform independent; 1GB should be enough @@ -2576,22 +2583,22 @@ static const char *locateWad(void) #ifdef CMAKECONFIG #ifndef NDEBUG - I_OutputMsg(","CMAKE_ASSETS_DIR); - strcpy(returnWadPath, CMAKE_ASSETS_DIR); - if (isWadPathOk(returnWadPath)) - { - return returnWadPath; - } + I_OutputMsg(","CMAKE_ASSETS_DIR); + strcpy(returnWadPath, CMAKE_ASSETS_DIR); + if (isWadPathOk(returnWadPath)) + { + return returnWadPath; + } #endif #endif #ifdef __APPLE__ - OSX_GetResourcesPath(returnWadPath); - I_OutputMsg(",%s", returnWadPath); - if (isWadPathOk(returnWadPath)) - { - return returnWadPath; - } + OSX_GetResourcesPath(returnWadPath); + I_OutputMsg(",%s", returnWadPath); + if (isWadPathOk(returnWadPath)) + { + return returnWadPath; + } #endif // examine default dirs @@ -2696,7 +2703,30 @@ const char *I_LocateWad(void) #ifdef __linux__ #define MEMINFO_FILE "/proc/meminfo" #define MEMTOTAL "MemTotal:" +#define MEMAVAILABLE "MemAvailable:" #define MEMFREE "MemFree:" +#define CACHED "Cached:" +#define BUFFERS "Buffers:" +#define SHMEM "Shmem:" + +/* Parse the contents of /proc/meminfo (in buf), return value of "name" + * (example: MemTotal) */ +static long get_entry(const char* name, const char* buf) +{ + long val; + char* hit = strstr(buf, name); + if (hit == NULL) { + return -1; + } + + errno = 0; + val = strtol(hit + strlen(name), NULL, 10); + if (errno != 0) { + CONS_Alert(CONS_ERROR, M_GetText("get_entry: strtol() failed: %s\n"), strerror(errno)); + return -1; + } + return val; +} #endif // quick fix for compil @@ -2758,6 +2788,11 @@ UINT32 I_GetFreeMem(UINT32 *total) UINT32 totalKBytes; INT32 n; INT32 meminfo_fd = -1; + long Cached; + long MemFree; + long Buffers; + long Shmem; + long MemAvailable = -1; meminfo_fd = open(MEMINFO_FILE, O_RDONLY); n = read(meminfo_fd, buf, 1023); @@ -2783,16 +2818,28 @@ UINT32 I_GetFreeMem(UINT32 *total) memTag += sizeof (MEMTOTAL); totalKBytes = atoi(memTag); - if ((memTag = strstr(buf, MEMFREE)) == NULL) + if ((memTag = strstr(buf, MEMAVAILABLE)) == NULL) { - // Error - if (total) - *total = 0L; - return 0; - } + Cached = get_entry(CACHED, buf); + MemFree = get_entry(MEMFREE, buf); + Buffers = get_entry(BUFFERS, buf); + Shmem = get_entry(SHMEM, buf); + MemAvailable = Cached + MemFree + Buffers - Shmem; - memTag += sizeof (MEMFREE); - freeKBytes = atoi(memTag); + if (MemAvailable == -1) + { + // Error + if (total) + *total = 0L; + return 0; + } + freeKBytes = MemAvailable; + } + else + { + memTag += sizeof (MEMAVAILABLE); + freeKBytes = atoi(memTag); + } if (total) *total = totalKBytes << 10; @@ -2869,5 +2916,5 @@ const CPUInfoFlags *I_CPUInfo(void) } // note CPUAFFINITY code used to reside here -FUNCMATH void I_RegisterSysCommands(void) {} +void I_RegisterSysCommands(void) {} #endif diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 48bb61649..05ed527de 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -1,8 +1,10 @@ // Emacs style mode select -*- C++ -*- +// SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // // Copyright (C) 1993-1996 by id Software, Inc. // Portions Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 2014-2018 by Sonic Team Junior. // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License @@ -1057,7 +1059,7 @@ void I_SetPalette(RGBA_t *palette) } // return number of fullscreen + X11 modes -FUNCMATH INT32 VID_NumModes(void) +INT32 VID_NumModes(void) { if (USE_FULLSCREEN && numVidModes != -1) return numVidModes - firstEntry; @@ -1065,7 +1067,7 @@ FUNCMATH INT32 VID_NumModes(void) return MAXWINMODES; } -FUNCMATH const char *VID_GetModeName(INT32 modeNum) +const char *VID_GetModeName(INT32 modeNum) { #if 0 if (USE_FULLSCREEN && numVidModes != -1) // fullscreen modes @@ -1095,7 +1097,7 @@ FUNCMATH const char *VID_GetModeName(INT32 modeNum) return &vidModeName[modeNum][0]; } -FUNCMATH INT32 VID_GetModeForSize(INT32 w, INT32 h) +INT32 VID_GetModeForSize(INT32 w, INT32 h) { int i; for (i = 0; i < MAXWINMODES; i++) diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index 6782d8ba6..128faaab8 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -1,3 +1,11 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2014-2018 by Sonic Team Junior. +// +// This program is free software distributed under the +// terms of the GNU General Public License, version 2. +// See the 'LICENSE' file for more details. +//----------------------------------------------------------------------------- /// \file /// \brief SDL Mixer interface for sound @@ -44,10 +52,8 @@ #include "gme/gme.h" #define GME_TREBLE 5.0 #define GME_BASS 1.0 -#ifdef HAVE_PNG /// TODO: compile with zlib support without libpng - -#define HAVE_ZLIB +#ifdef HAVE_ZLIB #ifndef _MSC_VER #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE @@ -63,8 +69,8 @@ #endif #include "zlib.h" -#endif -#endif +#endif // HAVE_ZLIB +#endif // HAVE_LIBGME UINT8 sound_started = false; @@ -128,8 +134,6 @@ void I_StartupSound(void) var_cleanup(); music = NULL; - music_volume = sfx_volume = 0; - #if SDL_MIXER_VERSION_ATLEAST(1,2,11) Mix_Init(MIX_INIT_FLAC|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG); #endif @@ -165,7 +169,7 @@ void I_ShutdownSound(void) #endif } -FUNCMATH void I_UpdateSound(void) +void I_UpdateSound(void) { } @@ -221,7 +225,7 @@ static Mix_Chunk *ds2chunk(void *stream) return NULL; // would and/or did wrap, can't store. break; } - sound = malloc(newsamples<<2); // samples * frequency shift * bytes per sample * channels + sound = Z_Malloc(newsamples<<2, PU_SOUND, NULL); // samples * frequency shift * bytes per sample * channels s = (SINT8 *)stream; d = (INT16 *)sound; @@ -289,6 +293,7 @@ void *I_GetSfx(sfxinfo_t *sfx) { void *lump; Mix_Chunk *chunk; + SDL_RWops *rw; #ifdef HAVE_LIBGME Music_Emu *emu; gme_info_t *info; @@ -405,7 +410,7 @@ void *I_GetSfx(sfxinfo_t *sfx) } Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up #else - //CONS_Alert(CONS_ERROR,"Cannot decompress VGZ; no zlib support\n"); + return NULL; // No zlib support #endif } // Try to read it as a GME sound @@ -432,21 +437,43 @@ void *I_GetSfx(sfxinfo_t *sfx) #endif // Try to load it as a WAVE or OGG using Mixer. - return Mix_LoadWAV_RW(SDL_RWFromMem(lump, sfx->length), 1); + rw = SDL_RWFromMem(lump, sfx->length); + if (rw != NULL) + { + chunk = Mix_LoadWAV_RW(rw, 1); + return chunk; + } + + return NULL; // haven't been able to get anything } void I_FreeSfx(sfxinfo_t *sfx) { if (sfx->data) + { + Mix_Chunk *chunk = (Mix_Chunk*)sfx->data; + UINT8 *abufdata = NULL; + if (chunk->allocated == 0) + { + // We allocated the data in this chunk, so get the abuf from mixer, then let it free the chunk, THEN we free the data + // I believe this should ensure the sound is not playing when we free it + abufdata = chunk->abuf; + } Mix_FreeChunk(sfx->data); + if (abufdata) + { + // I'm going to assume we used Z_Malloc to allocate this data. + Z_Free(abufdata); + } + } sfx->data = NULL; sfx->lumpnum = LUMPERROR; } -INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority) +INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel) { UINT8 volume = (((UINT16)vol + 1) * (UINT16)sfx_volume) / 62; // (256 * 31) / 62 == 127 - INT32 handle = Mix_PlayChannel(-1, S_sfx[id].data, 0); + INT32 handle = Mix_PlayChannel(channel, S_sfx[id].data, 0); Mix_Volume(handle, volume); Mix_SetPanning(handle, min((UINT16)(0xff-sep)<<1, 0xff), min((UINT16)(sep)<<1, 0xff)); (void)pitch; // Mixer can't handle pitch @@ -601,7 +628,7 @@ static void mix_gme(void *udata, Uint8 *stream, int len) /// Music System /// ------------------------ -FUNCMATH void I_InitMusic(void) +void I_InitMusic(void) { } @@ -883,6 +910,7 @@ boolean I_LoadSong(char *data, size_t len) size_t wstart, wp; char *p = data; + SDL_RWops *rw; if (music #ifdef HAVE_LIBGME @@ -978,7 +1006,8 @@ boolean I_LoadSong(char *data, size_t len) } Z_Free(inflatedData); // GME didn't open jack, but don't let that stop us from freeing this up #else - //CONS_Alert(CONS_ERROR,"Cannot decompress VGZ; no zlib support\n"); + CONS_Alert(CONS_ERROR,"Cannot decompress VGZ; no zlib support\n"); + return true; #endif } else if (!gme_open_data(data, len, &gme, 44100)) @@ -989,7 +1018,11 @@ boolean I_LoadSong(char *data, size_t len) } #endif - music = Mix_LoadMUS_RW(SDL_RWFromMem(data, len), SDL_FALSE); + rw = SDL_RWFromMem(data, len); + if (rw != NULL) + { + music = Mix_LoadMUS_RW(rw, 1); + } if (!music) { CONS_Alert(CONS_ERROR, "Mix_LoadMUS_RW: %s\n", Mix_GetError()); @@ -1033,6 +1066,7 @@ boolean I_LoadSong(char *data, size_t len) song_length = (float)(atoi(p) / 1000.0L); } // below: search MP3 or other tags that use wide char encoding + // \todo this isn't actually how MP3 stores its tags. We're just using endian markers as context clues. else if (!loop_point && !memcmp(p, key1w, key1len*2)) // LOOP wide char { p += key1len*2; diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c index b62f3abb1..349baea32 100644 --- a/src/sdl/sdl_sound.c +++ b/src/sdl/sdl_sound.c @@ -219,7 +219,7 @@ static void Snd_UnlockAudio(void) //Alam: Unlock audio data and reinstall audio #endif } -FUNCMATH static inline Uint16 Snd_LowerRate(Uint16 sr) +static inline Uint16 Snd_LowerRate(Uint16 sr) { if (sr <= audio.freq) // already lowered rate? return sr; // good then @@ -604,10 +604,11 @@ void I_FreeSfx(sfxinfo_t * sfx) // Pitching (that is, increased speed of playback) // is set, but currently not used by mixing. // -INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority) +INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel) { (void)priority; (void)pitch; + (void)channel; if (sound_disabled) return 0; @@ -1350,12 +1351,12 @@ musictype_t I_SongType(void) boolean I_SongPlaying(void) { - return musicStarted; + return false; } boolean I_SongPaused(void) { - return Mix_PausedMusic(); + return false; } /// ------------------------ @@ -1379,8 +1380,8 @@ UINT32 I_GetSongLength(void) boolean I_SetSongLoopPoint(UINT32 looppoint) { - (void)looppoint; - return false; + (void)looppoint; + return false; } UINT32 I_GetSongLoopPoint(void) diff --git a/src/st_stuff.c b/src/st_stuff.c index fa13e008a..26d9678a3 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -603,6 +603,9 @@ static void ST_drawDebugInfo(void) static void ST_drawScore(void) { + if (F_GetPromptHideHud(hudinfo[HUD_SCORE].y)) + return; + // SCORE: ST_DrawPatchFromHud(HUD_SCORE, sboscore, V_HUDTRANS); if (objectplacing) @@ -712,6 +715,9 @@ static void ST_drawTime(void) tictrn = G_TicsToCentiseconds(tics); } + if (F_GetPromptHideHud(hudinfo[HUD_TIME].y)) + return; + // TIME: ST_DrawPatchFromHud(HUD_TIME, ((downwards && (tics < 30*TICRATE) && (leveltime/5 & 1)) ? sboredtime : sbotime), V_HUDTRANS); @@ -738,6 +744,9 @@ static inline void ST_drawRings(void) { INT32 ringnum; + if (F_GetPromptHideHud(hudinfo[HUD_RINGS].y)) + return; + ST_DrawPatchFromHud(HUD_RINGS, ((!stplyr->spectator && stplyr->rings <= 0 && leveltime/5 & 1) ? sboredrings : sborings), ((stplyr->spectator) ? V_HUDTRANSHALF : V_HUDTRANS)); ringnum = ((objectplacing) ? op_currentdoomednum : max(stplyr->rings, 0)); @@ -756,6 +765,9 @@ static void ST_drawLivesArea(void) if (!stplyr->skincolor) return; // Just joined a server, skin isn't loaded yet! + if (F_GetPromptHideHud(hudinfo[HUD_LIVES].y)) + return; + // face background V_DrawSmallScaledPatch(hudinfo[HUD_LIVES].x, hudinfo[HUD_LIVES].y, hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, livesback); @@ -927,6 +939,9 @@ static void ST_drawInput(void) if (stplyr->powers[pw_carry] == CR_NIGHTSMODE) y -= 16; + if (F_GetPromptHideHud(y)) + return; + // O backing V_DrawFill(x, y-1, 16, 16, hudinfo[HUD_LIVES].f|20); V_DrawFill(x, y+15, 16, 1, hudinfo[HUD_LIVES].f|29); @@ -1202,6 +1217,9 @@ static void ST_drawPowerupHUD(void) static INT32 flagoffs[2] = {0, 0}, shieldoffs[2] = {0, 0}; #define ICONSEP (16+4) // matches weapon rings HUD + if (F_GetPromptHideHud(hudinfo[HUD_POWERUPS].y)) + return; + if (stplyr->spectator || stplyr->playerstate != PST_LIVE) return; @@ -1364,7 +1382,7 @@ static void ST_drawFirstPersonHUD(void) p = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE); // Display the countdown drown numbers! - if (p) + if (p && !F_GetPromptHideHud(60 - SHORT(p->topoffset))) V_DrawScaledPatch((BASEVIDWIDTH/2) - (SHORT(p->width)/2) + SHORT(p->leftoffset), 60 - SHORT(p->topoffset), V_PERPLAYER|V_PERPLAYER|V_TRANSLUCENT, p); } @@ -1908,6 +1926,9 @@ static void ST_drawMatchHUD(void) const INT32 y = 176; // HUD_LIVES INT32 offset = (BASEVIDWIDTH / 2) - (NUM_WEAPONS * 10) - 6; + if (F_GetPromptHideHud(y)) + return; + if (!G_RingSlingerGametype()) return; @@ -1954,6 +1975,9 @@ static void ST_drawTextHUD(void) y -= 8;\ } + if (F_GetPromptHideHud(y)) + return; + if ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && (!stplyr->spectator)) { if (leveltime < hidetime * TICRATE) @@ -2087,6 +2111,9 @@ static void ST_drawTeamHUD(void) patch_t *p; #define SEP 20 + if (F_GetPromptHideHud(0)) // y base is 0 + return; + if (gametype == GT_CTF) p = bflagico; else @@ -2200,7 +2227,8 @@ static INT32 ST_drawEmeraldHuntIcon(mobj_t *hunt, patch_t **patches, INT32 offse interval = 0; } - V_DrawScaledPatch(hudinfo[HUD_HUNTPICS].x+offset, hudinfo[HUD_HUNTPICS].y, hudinfo[HUD_HUNTPICS].f|V_PERPLAYER|V_HUDTRANS, patches[i]); + if (!F_GetPromptHideHud(hudinfo[HUD_HUNTPICS].y)) + V_DrawScaledPatch(hudinfo[HUD_HUNTPICS].x+offset, hudinfo[HUD_HUNTPICS].y, hudinfo[HUD_HUNTPICS].f|V_PERPLAYER|V_HUDTRANS, patches[i]); return interval; } @@ -2298,7 +2326,8 @@ static void ST_overlayDrawer(void) //hu_showscores = auto hide score/time/rings when tab rankings are shown if (!(hu_showscores && (netgame || multiplayer))) { - if (maptol & TOL_NIGHTS || G_IsSpecialStage(gamemap)) + if ((maptol & TOL_NIGHTS || G_IsSpecialStage(gamemap)) && + !F_GetPromptHideHudAll()) ST_drawNiGHTSHUD(); else { diff --git a/src/st_stuff.h b/src/st_stuff.h index df0adace3..3886d737c 100644 --- a/src/st_stuff.h +++ b/src/st_stuff.h @@ -24,7 +24,7 @@ // // Called by main loop. -FUNCMATH void ST_Ticker(void); +void ST_Ticker(void); // Called by main loop. void ST_Drawer(void); diff --git a/src/v_video.c b/src/v_video.c index 765965ffd..ae7c08511 100644 --- a/src/v_video.c +++ b/src/v_video.c @@ -532,7 +532,6 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t { UINT8 (*patchdrawfunc)(const UINT8*, const UINT8*, fixed_t); UINT32 alphalevel = 0; - boolean flip = false; fixed_t col, ofs, colfrac, rowfrac, fdup; INT32 dupx, dupy; @@ -610,22 +609,32 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t colfrac = FixedDiv(FRACUNIT, fdup); rowfrac = FixedDiv(FRACUNIT, fdup); - if (scrn & V_OFFSET) // Crosshair shit + // So it turns out offsets aren't scaled in V_NOSCALESTART unless V_OFFSET is applied ...poo, that's terrible + // For now let's just at least give V_OFFSET the ability to support V_FLIP + // I'll probably make a better fix for 2.2 where I don't have to worry about breaking existing support for stuff + // -- Monster Iestyn 29/10/18 { - y -= FixedMul((SHORT(patch->topoffset)*dupy)<leftoffset)*dupx)<topoffset)<width) - SHORT(patch->leftoffset))<width) - SHORT(patch->leftoffset))<leftoffset)<leftoffset)<topoffset)<>FRACBITS) < SHORT(patch->width); col += colfrac, ++offx, desttop++) { INT32 topdelta, prevdelta = -1; - if (flip) // offx is measured from right edge instead of left + if (scrn & V_FLIP) // offx is measured from right edge instead of left { if (x+pwidth-offx < 0) // don't draw off the left of the screen (WRAP PREVENTION) break; @@ -798,7 +807,7 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t prevdelta = topdelta; source = (const UINT8 *)(column) + 3; dest = desttop; - if (flip) + if (scrn & V_FLIP) dest = deststart + (destend - desttop); dest += FixedInt(FixedMul(topdelta<= numskins || (skins[skinnum].flags & SF_HIRES)) V_DrawScaledPatch(x - 10, y - 14, flags, W_CachePatchName("CONTINS", PU_CACHE)); else { @@ -1480,6 +1489,49 @@ void V_DrawFadeConsBack(INT32 plines) *buf = consolebgmap[*buf]; } +// Very similar to F_DrawFadeConsBack, except we draw from the middle(-ish) of the screen to the bottom. +void V_DrawPromptBack(INT32 boxheight, INT32 color) +{ + UINT8 *deststop, *buf; + + boxheight *= vid.dupy; + + if (color == INT32_MAX) + color = cons_backcolor.value; + +#ifdef HWRENDER + if (rendermode != render_soft && rendermode != render_none) + { + UINT32 hwcolor; + switch (color) + { + case 0: hwcolor = 0xffffff00; break; // White + case 1: hwcolor = 0x00000000; break; // Gray // Note this is different from V_DrawFadeConsBack + case 2: hwcolor = 0x40201000; break; // Brown + case 3: hwcolor = 0xff000000; break; // Red + case 4: hwcolor = 0xff800000; break; // Orange + case 5: hwcolor = 0x80800000; break; // Yellow + case 6: hwcolor = 0x00800000; break; // Green + case 7: hwcolor = 0x0000ff00; break; // Blue + case 8: hwcolor = 0x4080ff00; break; // Cyan + // Default green + default: hwcolor = 0x00800000; break; + } + HWR_DrawTutorialBack(hwcolor, boxheight); + return; + } +#endif + + CON_SetupBackColormapEx(color, true); + + // 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 * 4) + (boxheight/2)*5); // 4 lines of space plus gaps between and some leeway + for (; buf < deststop; ++buf) + *buf = promptbgmap[*buf]; +} + // Gets string colormap, used for 0x80 color codes // static const UINT8 *V_GetStringColormap(INT32 colorflags) diff --git a/src/v_video.h b/src/v_video.h index 6113d08ec..84a027963 100644 --- a/src/v_video.h +++ b/src/v_video.h @@ -158,6 +158,7 @@ void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum); void V_DrawFadeScreen(UINT16 color, UINT8 strength); void V_DrawFadeConsBack(INT32 plines); +void V_DrawPromptBack(INT32 boxheight, INT32 color); // draw a single character void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed); diff --git a/src/w_wad.c b/src/w_wad.c index 7babd22ef..905616542 100644 --- a/src/w_wad.c +++ b/src/w_wad.c @@ -234,10 +234,10 @@ static inline void W_LoadDehackedLumps(UINT16 wadnum) for (lump = 0; lump < wadfiles[wadnum]->numlumps; lump++, lump_p++) if (memcmp(lump_p->name,"SOC_",4)==0) // Check for generic SOC lump { // shameless copy+paste of code from LUA_LoadLump - size_t length = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name - char *name = malloc(length + 1); + size_t len = strlen(wadfiles[wadnum]->filename) + 1 + strlen(lump_p->name2); // length of file name, '|', and lump name + char *name = malloc(len+1); sprintf(name, "%s|%s", wadfiles[wadnum]->filename, lump_p->name2); - name[length] = '\0'; + name[len] = '\0'; CONS_Printf(M_GetText("Loading SOC from %s\n"), name); DEH_LoadDehackedLumpPwad(wadnum, lump); @@ -389,6 +389,8 @@ UINT16 W_InitFile(const char *filename) if (!memcmp(wadfiles[i]->md5sum, md5sum, 16)) { CONS_Alert(CONS_ERROR, M_GetText("%s is already loaded\n"), filename); + if (handle) + fclose(handle); return INT16_MAX; } } @@ -634,6 +636,8 @@ UINT16 W_InitFile(const char *filename) if (fread(&header, 1, sizeof header, handle) < sizeof header) { CONS_Alert(CONS_ERROR, M_GetText("Can't read wad header from %s because %s\n"), filename, strerror(ferror(handle))); + if (handle) + fclose(handle); return INT16_MAX; } @@ -644,6 +648,8 @@ UINT16 W_InitFile(const char *filename) && memcmp(header.identification, "SDLL", 4) != 0) { CONS_Alert(CONS_ERROR, M_GetText("%s does not have a valid WAD header\n"), filename); + if (handle) + fclose(handle); return INT16_MAX; } @@ -658,6 +664,8 @@ UINT16 W_InitFile(const char *filename) { CONS_Alert(CONS_ERROR, M_GetText("Wadfile directory in %s is corrupted (%s)\n"), filename, strerror(ferror(handle))); free(fileinfov); + if (handle) + fclose(handle); return INT16_MAX; } diff --git a/src/win32/win_snd.c b/src/win32/win_snd.c index 543de3231..371384328 100644 --- a/src/win32/win_snd.c +++ b/src/win32/win_snd.c @@ -17,10 +17,8 @@ #include "gme/gme.h" #define GME_TREBLE 5.0 #define GME_BASS 1.0 -#ifdef HAVE_PNG /// TODO: compile with zlib support without libpng - -#define HAVE_ZLIB +#ifdef HAVE_ZLIB #ifndef _MSC_VER #ifndef _LARGEFILE64_SOURCE #define _LARGEFILE64_SOURCE @@ -36,8 +34,8 @@ #endif #include "zlib.h" -#endif -#endif +#endif // HAVE_ZLIB +#endif // HAVE_LIBGME static FMOD_SYSTEM *fsys; static FMOD_SOUND *music_stream; @@ -354,12 +352,13 @@ void I_FreeSfx(sfxinfo_t *sfx) sfx->data = NULL; } -INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority) +INT32 I_StartSound(sfxenum_t id, UINT8 vol, UINT8 sep, UINT8 pitch, UINT8 priority, INT32 channel) { FMOD_SOUND *sound; FMOD_CHANNEL *chan; INT32 i; float frequency; + (void)channel; sound = (FMOD_SOUND *)S_sfx[id].data; I_Assert(sound != NULL); @@ -927,6 +926,7 @@ boolean I_FadeSongFromVolume(UINT8 target_volume, UINT8 source_volume, UINT32 ms (void)target_volume; (void)source_volume; (void)ms; + (void)callback; return false; } @@ -934,6 +934,7 @@ boolean I_FadeSong(UINT8 target_volume, UINT32 ms, void (*callback)(void)) { (void)target_volume; (void)ms; + (void)callback; return false; }