1 //
2 // Copyright(C) 1993-1996 Id Software, Inc.
3 // Copyright(C) 1993-2008 Raven Software
4 // Copyright(C) 2005-2014 Simon Howard
5 //
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
15 //
16
17 // D_main.c
18
19 #include <stdio.h>
20 #include <stdlib.h>
21
22 #include "txt_main.h"
23 #include "txt_io.h"
24
25 #include "net_client.h"
26
27 #include "config.h"
28 #include "ct_chat.h"
29 #include "doomdef.h"
30 #include "deh_main.h"
31 #include "d_iwad.h"
32 #include "i_endoom.h"
33 #include "i_input.h"
34 #include "i_joystick.h"
35 #include "i_sound.h"
36 #include "i_swap.h" // [crispy] SHORT()
37 #include "i_system.h"
38 #include "i_timer.h"
39 #include "i_video.h"
40 #include "m_argv.h"
41 #include "m_config.h"
42 #include "m_controls.h"
43 #include "m_misc.h"
44 #include "p_local.h"
45 #include "s_sound.h"
46 #include "w_main.h"
47 #include "v_video.h"
48 #include "v_trans.h" // [crispy] dp_translation
49
50 #define CT_KEY_GREEN 'g'
51 #define CT_KEY_YELLOW 'y'
52 #define CT_KEY_RED 'r'
53 #define CT_KEY_BLUE 'b'
54
55 #define STARTUP_WINDOW_X 17
56 #define STARTUP_WINDOW_Y 7
57
58 GameMode_t gamemode = indetermined;
59 const char *gamedescription = "unknown";
60
61 boolean nomonsters; // checkparm of -nomonsters
62 boolean respawnparm; // checkparm of -respawn
63 boolean debugmode; // checkparm of -debug
64 boolean ravpic; // checkparm of -ravpic
65 boolean cdrom; // true if cd-rom mode active
66 boolean noartiskip; // whether shift-enter skips an artifact
67
68 skill_t startskill;
69 int startepisode;
70 int startmap;
71 int UpdateState;
72 static int graphical_startup = 0;
73 static boolean using_graphical_startup;
74 static boolean main_loop_started = false;
75 boolean autostart;
76 extern boolean automapactive;
77
78 boolean advancedemo;
79
80 FILE *debugfile;
81
82 static int show_endoom = 0;
83
84 void D_ConnectNetGame(void);
85 void D_CheckNetGame(void);
86 void D_PageDrawer(void);
87 void D_AdvanceDemo(void);
88 boolean F_Responder(event_t * ev);
89
90 //---------------------------------------------------------------------------
91 //
92 // PROC D_ProcessEvents
93 //
94 // Send all the events of the given timestamp down the responder chain.
95 //
96 //---------------------------------------------------------------------------
97
D_ProcessEvents(void)98 void D_ProcessEvents(void)
99 {
100 event_t *ev;
101
102 while ((ev = D_PopEvent()) != NULL)
103 {
104 if (F_Responder(ev))
105 {
106 continue;
107 }
108 if (MN_Responder(ev))
109 {
110 continue;
111 }
112 G_Responder(ev);
113 }
114 }
115
116 //---------------------------------------------------------------------------
117 //
118 // PROC DrawMessage
119 //
120 //---------------------------------------------------------------------------
121
DrawMessage(void)122 void DrawMessage(void)
123 {
124 player_t *player;
125
126 player = &players[consoleplayer];
127 if (player->messageTics <= 0 || !player->message)
128 { // No message
129 return;
130 }
131 MN_DrTextA(player->message, 160 - MN_TextAWidth(player->message) / 2, 1);
132 }
133
134 //---------------------------------------------------------------------------
135 //
136 // PROC DrawCenterMessage
137 //
138 // [crispy]
139 //
140 //---------------------------------------------------------------------------
141
DrawCenterMessage(void)142 void DrawCenterMessage(void)
143 {
144 player_t* player;
145
146 player = &players[consoleplayer];
147 if (player->centerMessageTics <= 0 || !player->centerMessage)
148 { // No message
149 return;
150 }
151 // Place message above quit game message position so they don't overlap
152 dp_translation = cr[CR_GOLD];
153 MN_DrTextA(player->centerMessage, 160 - MN_TextAWidth(player->centerMessage) / 2, 70);
154 dp_translation = NULL;
155 }
156
157 //---------------------------------------------------------------------------
158 //
159 // PROC D_Display
160 //
161 // Draw current display, possibly wiping it from the previous.
162 //
163 //---------------------------------------------------------------------------
164
CrispyDrawStats(void)165 static void CrispyDrawStats (void)
166 {
167 static short height, coord_x;
168 char str[32];
169 player_t *const player = &players[consoleplayer];
170
171 if (!height || !coord_x)
172 {
173 const int FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1;
174 const patch_t *const p = W_CacheLumpNum(FontABaseLump + 'A' - 33, PU_CACHE);
175
176 height = SHORT(p->height) + 1;
177 coord_x = ORIGWIDTH - 7 * SHORT(p->width);
178 }
179
180 if (crispy->automapstats == WIDGETS_ALWAYS || (automapactive && crispy->automapstats == WIDGETS_AUTOMAP))
181 {
182 M_snprintf(str, sizeof(str), "K %d/%d", player->killcount, totalkills);
183 MN_DrTextA(str, 0, 1*height);
184
185 M_snprintf(str, sizeof(str), "I %d/%d", player->itemcount, totalitems);
186 MN_DrTextA(str, 0, 2*height);
187
188 M_snprintf(str, sizeof(str), "S %d/%d", player->secretcount, totalsecret);
189 MN_DrTextA(str, 0, 3*height);
190 }
191
192 if (crispy->leveltime == WIDGETS_ALWAYS || (automapactive && crispy->leveltime == WIDGETS_AUTOMAP))
193 {
194 const int time = leveltime / TICRATE;
195
196 M_snprintf(str, sizeof(str), "%02d:%02d", time/60, time%60);
197 MN_DrTextA(str, 0, 4*height);
198 }
199
200 if (crispy->playercoords == WIDGETS_ALWAYS || (automapactive && crispy->playercoords == WIDGETS_AUTOMAP))
201 {
202 M_snprintf(str, sizeof(str), "X %-5d", player->mo->x>>FRACBITS);
203 MN_DrTextA(str, coord_x, 1*height);
204
205 M_snprintf(str, sizeof(str), "Y %-5d", player->mo->y>>FRACBITS);
206 MN_DrTextA(str, coord_x, 2*height);
207
208 M_snprintf(str, sizeof(str), "A %-5d", player->mo->angle/ANG1);
209 MN_DrTextA(str, coord_x, 3*height);
210 }
211 }
212
213 // [crispy] Draw the current FPS if show fps cheat is active
CrispyDrawFps(void)214 static void CrispyDrawFps(void)
215 {
216 short coord_x, height;
217 char str[32];
218 player_t* const player = &players[consoleplayer];
219
220 const int FontABaseLump = W_GetNumForName(DEH_String("FONTA_S")) + 1;
221 const patch_t* const p = W_CacheLumpNum(FontABaseLump + 'A' - 33, PU_CACHE);
222
223 height = SHORT(p->height) + 1;
224 coord_x = ORIGWIDTH - 6 * SHORT(p->width);
225
226 // [crispy] Only show FPS outside automap as it could obscure level coords
227 if (player->cheats & CF_SHOWFPS && !automapactive)
228 {
229 M_snprintf(str, sizeof(str), "%d F", crispy->fps);
230 MN_DrTextA(str, coord_x, height);
231 }
232 }
233
234 void R_ExecuteSetViewSize(void);
235
236 extern boolean finalestage;
237
D_Display(void)238 void D_Display(void)
239 {
240 extern boolean askforquit;
241
242 // Change the view size if needed
243 if (setsizeneeded)
244 {
245 R_ExecuteSetViewSize();
246 }
247
248 //
249 // do buffered drawing
250 //
251 switch (gamestate)
252 {
253 case GS_LEVEL:
254 if (!gametic)
255 break;
256 if (automapactive && !crispy->automapoverlay)
257 {
258 // [crispy] update automap while playing
259 R_RenderPlayerView (&players[displayplayer]);
260 AM_Drawer();
261 }
262 else
263 R_RenderPlayerView(&players[displayplayer]);
264 if (automapactive && crispy->automapoverlay)
265 {
266 AM_Drawer();
267 BorderNeedRefresh = true;
268 }
269 CT_Drawer();
270 UpdateState |= I_FULLVIEW;
271 SB_Drawer();
272 CrispyDrawStats();
273 CrispyDrawFps();
274 break;
275 case GS_INTERMISSION:
276 IN_Drawer();
277 break;
278 case GS_FINALE:
279 F_Drawer();
280 break;
281 case GS_DEMOSCREEN:
282 D_PageDrawer();
283 break;
284 }
285
286 if (testcontrols)
287 {
288 V_DrawMouseSpeedBox(testcontrols_mousespeed);
289 }
290
291 if (paused && !MenuActive && !askforquit)
292 {
293 if (!netgame)
294 {
295 V_DrawPatch(160, (viewwindowy >> crispy->hires) + 5, W_CacheLumpName(DEH_String("PAUSED"),
296 PU_CACHE));
297 }
298 else
299 {
300 V_DrawPatch(160, 70, W_CacheLumpName(DEH_String("PAUSED"), PU_CACHE));
301 }
302 }
303 // Handle player messages
304 DrawMessage();
305
306 // [crispy] Handle centered player messages
307 DrawCenterMessage();
308
309 // Menu drawing
310 MN_Drawer();
311
312 // Send out any new accumulation
313 NetUpdate();
314
315 // Flush buffered stuff to screen
316 I_FinishUpdate();
317 }
318
319 //
320 // D_GrabMouseCallback
321 //
322 // Called to determine whether to grab the mouse pointer
323 //
324
D_GrabMouseCallback(void)325 boolean D_GrabMouseCallback(void)
326 {
327 // when menu is active or game is paused, release the mouse
328
329 if (MenuActive || paused)
330 return false;
331
332 // only grab mouse when playing levels (but not demos)
333
334 return (gamestate == GS_LEVEL) && !demoplayback && !advancedemo;
335 }
336
337 //---------------------------------------------------------------------------
338 //
339 // PROC D_DoomLoop
340 //
341 //---------------------------------------------------------------------------
342
D_DoomLoop(void)343 void D_DoomLoop(void)
344 {
345 if (M_CheckParm("-debugfile"))
346 {
347 char filename[20];
348 M_snprintf(filename, sizeof(filename), "debug%i.txt", consoleplayer);
349 debugfile = fopen(filename, "w");
350 }
351 I_GraphicsCheckCommandLine();
352 I_SetGrabMouseCallback(D_GrabMouseCallback);
353 I_InitGraphics();
354
355 main_loop_started = true;
356
357 while (1)
358 {
359 // Frame syncronous IO operations
360 I_StartFrame();
361
362 // Process one or more tics
363 // Will run at least one tic
364 TryRunTics();
365
366 // Move positional sounds
367 S_UpdateSounds(players[consoleplayer].mo);
368 D_Display();
369
370 // [crispy] post-rendering function pointer to apply config changes
371 // that affect rendering and that are better applied after the current
372 // frame has finished rendering
373 if (crispy->post_rendering_hook)
374 {
375 crispy->post_rendering_hook();
376 crispy->post_rendering_hook = NULL;
377 }
378 }
379 }
380
381 /*
382 ===============================================================================
383
384 DEMO LOOP
385
386 ===============================================================================
387 */
388
389 static int demosequence;
390 static int pagetic;
391 static const char *pagename;
392
393
394 /*
395 ================
396 =
397 = D_PageTicker
398 =
399 = Handles timing for warped projection
400 =
401 ================
402 */
403
D_PageTicker(void)404 void D_PageTicker(void)
405 {
406 if (--pagetic < 0)
407 D_AdvanceDemo();
408 }
409
410
411 /*
412 ================
413 =
414 = D_PageDrawer
415 =
416 ================
417 */
418
D_PageDrawer(void)419 void D_PageDrawer(void)
420 {
421 V_DrawRawScreen(W_CacheLumpName(pagename, PU_CACHE));
422 if (demosequence == 1)
423 {
424 V_DrawPatch(4, 160, W_CacheLumpName(DEH_String("ADVISOR"), PU_CACHE));
425 }
426 UpdateState |= I_FULLSCRN;
427 }
428
429 /*
430 =================
431 =
432 = D_AdvanceDemo
433 =
434 = Called after each demo or intro demosequence finishes
435 =================
436 */
437
D_AdvanceDemo(void)438 void D_AdvanceDemo(void)
439 {
440 advancedemo = true;
441 }
442
D_DoAdvanceDemo(void)443 void D_DoAdvanceDemo(void)
444 {
445 players[consoleplayer].playerstate = PST_LIVE; // don't reborn
446 advancedemo = false;
447 usergame = false; // can't save / end game here
448 paused = false;
449 gameaction = ga_nothing;
450 demosequence = (demosequence + 1) % 7;
451 switch (demosequence)
452 {
453 case 0:
454 pagetic = 210;
455 gamestate = GS_DEMOSCREEN;
456 pagename = DEH_String("TITLE");
457 S_StartSong(mus_titl, false);
458 break;
459 case 1:
460 pagetic = 140;
461 gamestate = GS_DEMOSCREEN;
462 pagename = DEH_String("TITLE");
463 break;
464 case 2:
465 BorderNeedRefresh = true;
466 UpdateState |= I_FULLSCRN;
467 G_DeferedPlayDemo(DEH_String("demo1"));
468 break;
469 case 3:
470 pagetic = 200;
471 gamestate = GS_DEMOSCREEN;
472 pagename = DEH_String("CREDIT");
473 break;
474 case 4:
475 BorderNeedRefresh = true;
476 UpdateState |= I_FULLSCRN;
477 G_DeferedPlayDemo(DEH_String("demo2"));
478 break;
479 case 5:
480 pagetic = 200;
481 gamestate = GS_DEMOSCREEN;
482 if (gamemode == shareware)
483 {
484 pagename = DEH_String("ORDER");
485 }
486 else
487 {
488 pagename = DEH_String("CREDIT");
489 }
490 break;
491 case 6:
492 BorderNeedRefresh = true;
493 UpdateState |= I_FULLSCRN;
494 G_DeferedPlayDemo(DEH_String("demo3"));
495 break;
496 }
497 }
498
499
500 /*
501 =================
502 =
503 = D_StartTitle
504 =
505 =================
506 */
507
D_StartTitle(void)508 void D_StartTitle(void)
509 {
510 gameaction = ga_nothing;
511 demosequence = -1;
512 D_AdvanceDemo();
513 }
514
515
516 /*
517 ==============
518 =
519 = D_CheckRecordFrom
520 =
521 = -recordfrom <savegame num> <demoname>
522 ==============
523 */
524
D_CheckRecordFrom(void)525 void D_CheckRecordFrom(void)
526 {
527 int p;
528 char *filename;
529
530 //!
531 // @vanilla
532 // @category demo
533 // @arg <savenum> <demofile>
534 //
535 // Record a demo, loading from the given filename. Equivalent
536 // to -loadgame <savenum> -record <demofile>.
537
538 p = M_CheckParmWithArgs("-recordfrom", 2);
539 if (!p)
540 return;
541
542 filename = SV_Filename(myargv[p + 1][0] - '0');
543 G_LoadGame(filename);
544 G_DoLoadGame(); // load the gameskill etc info from savegame
545
546 G_RecordDemo(gameskill, 1, gameepisode, gamemap, myargv[p + 2]);
547 D_DoomLoop(); // never returns
548 free(filename);
549 }
550
551 /*
552 ===============
553 =
554 = D_AddFile
555 =
556 ===============
557 */
558
559 // MAPDIR should be defined as the directory that holds development maps
560 // for the -wart # # command
561
562 #define MAPDIR "\\data\\"
563
564 #define SHAREWAREWADNAME "heretic1.wad"
565
566 char *iwadfile;
567
568
wadprintf(void)569 void wadprintf(void)
570 {
571 if (debugmode)
572 {
573 return;
574 }
575 }
576
D_AddFile(char * file)577 boolean D_AddFile(char *file)
578 {
579 wad_file_t *handle;
580
581 printf(" adding %s\n", file);
582
583 handle = W_AddFile(file);
584
585 return handle != NULL;
586 }
587
588 //==========================================================
589 //
590 // Startup Thermo code
591 //
592 //==========================================================
593 #define MSG_Y 9
594 #define THERM_X 14
595 #define THERM_Y 14
596
597 int thermMax;
598 int thermCurrent;
599 char smsg[80]; // status bar line
600
601 //
602 // Heretic startup screen shit
603 //
604
605 static int startup_line = STARTUP_WINDOW_Y;
606
hprintf(const char * string)607 void hprintf(const char *string)
608 {
609 if (using_graphical_startup)
610 {
611 TXT_BGColor(TXT_COLOR_CYAN, 0);
612 TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
613
614 TXT_GotoXY(STARTUP_WINDOW_X, startup_line);
615 ++startup_line;
616 TXT_Puts(string);
617
618 TXT_UpdateScreen();
619 }
620
621 // haleyjd: shouldn't be WATCOMC-only
622 if (debugmode)
623 puts(string);
624 }
625
drawstatus(void)626 void drawstatus(void)
627 {
628 int i;
629
630 TXT_GotoXY(1, 24);
631 TXT_BGColor(TXT_COLOR_BLUE, 0);
632 TXT_FGColor(TXT_COLOR_BRIGHT_WHITE);
633
634 for (i=0; smsg[i] != '\0'; ++i)
635 {
636 TXT_PutChar(smsg[i]);
637 }
638 }
639
status(const char * string)640 static void status(const char *string)
641 {
642 if (using_graphical_startup)
643 {
644 M_StringConcat(smsg, string, sizeof(smsg));
645 drawstatus();
646 }
647 }
648
DrawThermo(void)649 void DrawThermo(void)
650 {
651 static int last_progress = -1;
652 int progress;
653 int i;
654
655 if (!using_graphical_startup)
656 {
657 return;
658 }
659
660 // No progress? Don't update the screen.
661
662 progress = (50 * thermCurrent) / thermMax + 2;
663
664 if (last_progress == progress)
665 {
666 return;
667 }
668
669 last_progress = progress;
670
671 TXT_GotoXY(THERM_X, THERM_Y);
672
673 TXT_FGColor(TXT_COLOR_BRIGHT_GREEN);
674 TXT_BGColor(TXT_COLOR_GREEN, 0);
675
676 for (i = 0; i < progress; i++)
677 {
678 TXT_PutChar(0xdb);
679 }
680
681 TXT_UpdateScreen();
682 }
683
initStartup(void)684 void initStartup(void)
685 {
686 byte *textScreen;
687 byte *loading;
688
689 if (!graphical_startup || debugmode || testcontrols)
690 {
691 using_graphical_startup = false;
692 return;
693 }
694
695 if (!TXT_Init())
696 {
697 using_graphical_startup = false;
698 return;
699 }
700
701 I_InitWindowTitle();
702 I_InitWindowIcon();
703
704 // Blit main screen
705 textScreen = TXT_GetScreenData();
706 loading = W_CacheLumpName(DEH_String("LOADING"), PU_CACHE);
707 memcpy(textScreen, loading, 4000);
708
709 // Print version string
710
711 TXT_BGColor(TXT_COLOR_RED, 0);
712 TXT_FGColor(TXT_COLOR_YELLOW);
713 TXT_GotoXY(46, 2);
714 TXT_Puts(HERETIC_VERSION_TEXT);
715
716 TXT_UpdateScreen();
717
718 using_graphical_startup = true;
719 }
720
finishStartup(void)721 static void finishStartup(void)
722 {
723 if (using_graphical_startup)
724 {
725 TXT_Shutdown();
726 }
727 }
728
729 char tmsg[300];
tprintf(const char * msg,int initflag)730 void tprintf(const char *msg, int initflag)
731 {
732 printf("%s", msg);
733 }
734
735 // haleyjd: moved up, removed WATCOMC code
CleanExit(void)736 void CleanExit(void)
737 {
738 DEH_printf("Exited from HERETIC.\n");
739 exit(1);
740 }
741
CheckAbortStartup(void)742 void CheckAbortStartup(void)
743 {
744 // haleyjd: removed WATCOMC
745 // haleyjd FIXME: this should actually work in text mode too, but how to
746 // get input before SDL video init?
747 if(using_graphical_startup)
748 {
749 if(TXT_GetChar() == 27)
750 CleanExit();
751 }
752 }
753
IncThermo(void)754 void IncThermo(void)
755 {
756 thermCurrent++;
757 DrawThermo();
758 CheckAbortStartup();
759 }
760
InitThermo(int max)761 void InitThermo(int max)
762 {
763 thermMax = max;
764 thermCurrent = 0;
765 }
766
767 //
768 // Add configuration file variable bindings.
769 //
770
D_BindVariables(void)771 void D_BindVariables(void)
772 {
773 extern int screenblocks;
774 extern int snd_Channels;
775 int i;
776
777 M_ApplyPlatformDefaults();
778
779 I_BindInputVariables();
780 I_BindVideoVariables();
781 I_BindJoystickVariables();
782 I_BindSoundVariables();
783
784 M_BindBaseControls();
785 M_BindHereticControls();
786 M_BindWeaponControls();
787 M_BindChatControls(MAXPLAYERS);
788
789 key_multi_msgplayer[0] = CT_KEY_GREEN;
790 key_multi_msgplayer[1] = CT_KEY_YELLOW;
791 key_multi_msgplayer[2] = CT_KEY_RED;
792 key_multi_msgplayer[3] = CT_KEY_BLUE;
793
794 M_BindMenuControls();
795 M_BindMapControls();
796
797 NET_BindVariables();
798
799 M_BindIntVariable("mouse_sensitivity", &mouseSensitivity);
800 M_BindIntVariable("mouse_sensitivity_x2", &mouseSensitivity_x2);
801 M_BindIntVariable("mouse_sensitivity_y", &mouseSensitivity_y);
802 M_BindIntVariable("sfx_volume", &snd_MaxVolume);
803 M_BindIntVariable("music_volume", &snd_MusicVolume);
804 M_BindIntVariable("screenblocks", &screenblocks);
805 M_BindIntVariable("snd_channels", &snd_Channels);
806 M_BindIntVariable("vanilla_savegame_limit", &vanilla_savegame_limit);
807 M_BindIntVariable("vanilla_demo_limit", &vanilla_demo_limit);
808 M_BindIntVariable("show_endoom", &show_endoom);
809 M_BindIntVariable("graphical_startup", &graphical_startup);
810
811 for (i=0; i<10; ++i)
812 {
813 char buf[12];
814
815 M_snprintf(buf, sizeof(buf), "chatmacro%i", i);
816 M_BindStringVariable(buf, &chat_macros[i]);
817 }
818
819 // [crispy] bind "crispness" config variables
820 M_BindIntVariable("crispy_hires", &crispy->hires);
821 M_BindIntVariable("crispy_smoothscaling", &crispy->smoothscaling);
822 M_BindIntVariable("crispy_automapstats", &crispy->automapstats);
823 M_BindIntVariable("crispy_leveltime", &crispy->leveltime);
824 M_BindIntVariable("crispy_playercoords", &crispy->playercoords);
825 M_BindIntVariable("crispy_secretmessage", &crispy->secretmessage);
826 M_BindIntVariable("crispy_uncapped", &crispy->uncapped);
827 M_BindIntVariable("crispy_vsync", &crispy->vsync);
828 }
829
830 //
831 // Called at exit to display the ENDOOM screen (ENDTEXT in Heretic)
832 //
833
D_Endoom(void)834 static void D_Endoom(void)
835 {
836 byte *endoom_data;
837
838 // Disable ENDOOM?
839
840 if (!show_endoom || testcontrols || !main_loop_started)
841 {
842 return;
843 }
844
845 endoom_data = W_CacheLumpName(DEH_String("ENDTEXT"), PU_STATIC);
846
847 I_Endoom(endoom_data);
848 }
849
850 //---------------------------------------------------------------------------
851 //
852 // PROC D_DoomMain
853 //
854 //---------------------------------------------------------------------------
855
D_DoomMain(void)856 void D_DoomMain(void)
857 {
858 GameMission_t gamemission;
859 int p;
860 char file[256];
861 char demolumpname[9];
862
863 I_PrintBanner(PACKAGE_STRING);
864
865 I_AtExit(D_Endoom, false);
866
867 //!
868 // @category game
869 // @vanilla
870 //
871 // Disable monsters.
872 //
873
874 nomonsters = M_ParmExists("-nomonsters");
875
876 //!
877 // @category game
878 // @vanilla
879 //
880 // Monsters respawn after being killed.
881 //
882
883 respawnparm = M_ParmExists("-respawn");
884
885 //!
886 // @vanilla
887 //
888 // Take screenshots when F1 is pressed.
889 //
890
891 ravpic = M_ParmExists("-ravpic");
892
893 //!
894 // @category obscure
895 // @vanilla
896 //
897 // Allow artifacts to be used when the run key is held down.
898 //
899
900 noartiskip = M_ParmExists("-noartiskip");
901
902 debugmode = M_ParmExists("-debug");
903 startskill = sk_medium;
904 startepisode = 1;
905 startmap = 1;
906 autostart = false;
907
908 //
909 // get skill / episode / map from parms
910 //
911
912 //!
913 // @vanilla
914 // @category net
915 //
916 // Start a deathmatch game.
917 //
918
919 if (M_ParmExists("-deathmatch"))
920 {
921 deathmatch = true;
922 }
923
924 //!
925 // @category game
926 // @arg <skill>
927 // @vanilla
928 //
929 // Set the game skill, 1-5 (1: easiest, 5: hardest). A skill of
930 // 0 disables all monsters.
931 //
932
933 p = M_CheckParmWithArgs("-skill", 1);
934 if (p)
935 {
936 startskill = myargv[p + 1][0] - '1';
937 autostart = true;
938 }
939
940 //!
941 // @category game
942 // @arg <n>
943 // @vanilla
944 //
945 // Start playing on episode n (1-4)
946 //
947
948 p = M_CheckParmWithArgs("-episode", 1);
949 if (p)
950 {
951 startepisode = myargv[p + 1][0] - '0';
952 startmap = 1;
953 autostart = true;
954 }
955
956 //!
957 // @category game
958 // @arg <x> <y>
959 // @vanilla
960 //
961 // Start a game immediately, warping to level ExMy.
962 //
963
964 p = M_CheckParmWithArgs("-warp", 2);
965 if (p && p < myargc - 2)
966 {
967 startepisode = myargv[p + 1][0] - '0';
968 startmap = myargv[p + 2][0] - '0';
969 autostart = true;
970
971 // [crispy] if used with -playdemo, fast-forward demo up to the desired map
972 crispy->demowarp = startmap;
973 }
974
975 //
976 // init subsystems
977 //
978 DEH_printf("V_Init: allocate screens.\n");
979 V_Init();
980
981 // Check for -CDROM
982
983 cdrom = false;
984
985 #ifdef _WIN32
986
987 //!
988 // @category obscure
989 // @platform windows
990 // @vanilla
991 //
992 // Save configuration data and savegames in c:\heretic.cd,
993 // allowing play from CD.
994 //
995
996 if (M_CheckParm("-cdrom"))
997 {
998 cdrom = true;
999 }
1000 #endif
1001
1002 if (cdrom)
1003 {
1004 M_SetConfigDir(DEH_String("c:\\heretic.cd"));
1005 }
1006 else
1007 {
1008 M_SetConfigDir(NULL);
1009 }
1010
1011 // Load defaults before initing other systems
1012 DEH_printf("M_LoadDefaults: Load system defaults.\n");
1013 D_BindVariables();
1014 M_SetConfigFilenames("heretic.cfg", PROGRAM_PREFIX "heretic.cfg");
1015 M_LoadDefaults();
1016
1017 I_AtExit(M_SaveDefaults, false);
1018
1019 DEH_printf("Z_Init: Init zone memory allocation daemon.\n");
1020 Z_Init();
1021
1022 DEH_printf("W_Init: Init WADfiles.\n");
1023
1024 iwadfile = D_FindIWAD(IWAD_MASK_HERETIC, &gamemission);
1025
1026 if (iwadfile == NULL)
1027 {
1028 I_Error("Game mode indeterminate. No IWAD was found. Try specifying\n"
1029 "one with the '-iwad' command line parameter.");
1030 }
1031
1032 D_AddFile(iwadfile);
1033 W_CheckCorrectIWAD(heretic);
1034
1035 //!
1036 // @category game
1037 // @category mod
1038 //
1039 // Automatic wand start when advancing from one level to the next. At the
1040 // beginning of each level, the player's health is reset to 100, their
1041 // armor to 0 and their inventory is reduced to the following: wand, staff
1042 // and 50 ammo for the wand. This option is not allowed when recording a
1043 // demo, playing back a demo or when starting a network game.
1044 //
1045
1046 crispy->pistolstart = M_ParmExists("-wandstart");
1047
1048 //!
1049 // @category mod
1050 //
1051 // Disable auto-loading of .wad files.
1052 //
1053 if (!M_ParmExists("-noautoload"))
1054 {
1055 char *autoload_dir;
1056 autoload_dir = M_GetAutoloadDir("heretic.wad", true);
1057 DEH_AutoLoadPatches(autoload_dir);
1058 W_AutoLoadWADs(autoload_dir);
1059 free(autoload_dir);
1060 }
1061
1062 // Load dehacked patches specified on the command line.
1063 DEH_ParseCommandLine();
1064
1065 // Load PWAD files.
1066 W_ParseCommandLine();
1067
1068 //!
1069 // @arg <demo>
1070 // @category demo
1071 // @vanilla
1072 //
1073 // Play back the demo named demo.lmp.
1074 //
1075
1076 p = M_CheckParmWithArgs("-playdemo", 1);
1077 if (!p)
1078 {
1079 //!
1080 // @arg <demo>
1081 // @category demo
1082 // @vanilla
1083 //
1084 // Play back the demo named demo.lmp, determining the framerate
1085 // of the screen.
1086 //
1087
1088 p = M_CheckParmWithArgs("-timedemo", 1);
1089 }
1090
1091 if (p)
1092 {
1093 char *uc_filename = strdup(myargv[p + 1]);
1094 M_ForceUppercase(uc_filename);
1095
1096 // In Vanilla, the filename must be specified without .lmp,
1097 // but make that optional.
1098 if (M_StringEndsWith(uc_filename, ".LMP"))
1099 {
1100 M_StringCopy(file, myargv[p + 1], sizeof(file));
1101 }
1102 else
1103 {
1104 DEH_snprintf(file, sizeof(file), "%s.lmp", myargv[p + 1]);
1105 }
1106
1107 free(uc_filename);
1108
1109 if (D_AddFile(file))
1110 {
1111 M_StringCopy(demolumpname, lumpinfo[numlumps - 1]->name,
1112 sizeof(demolumpname));
1113 }
1114 else
1115 {
1116 // The file failed to load, but copy the original arg as a
1117 // demo name to make tricks like -playdemo demo1 possible.
1118 M_StringCopy(demolumpname, myargv[p + 1], sizeof(demolumpname));
1119 }
1120
1121 printf("Playing demo %s.\n", file);
1122 }
1123
1124 // Generate the WAD hash table. Speed things up a bit.
1125 W_GenerateHashTable();
1126
1127 //!
1128 // @category demo
1129 //
1130 // Record or playback a demo, automatically quitting
1131 // after either level exit or player respawn.
1132 //
1133
1134 demoextend = (!M_ParmExists("-nodemoextend"));
1135 //[crispy] make demoextend the default
1136
1137 if (W_CheckNumForName(DEH_String("E2M1")) == -1)
1138 {
1139 gamemode = shareware;
1140 gamedescription = "Heretic (shareware)";
1141 }
1142 else if (W_CheckNumForName("EXTENDED") != -1)
1143 {
1144 // Presence of the EXTENDED lump indicates the retail version
1145
1146 gamemode = retail;
1147 gamedescription = "Heretic: Shadow of the Serpent Riders";
1148 }
1149 else
1150 {
1151 gamemode = registered;
1152 gamedescription = "Heretic (registered)";
1153 }
1154
1155 I_SetWindowTitle(gamedescription);
1156
1157 savegamedir = M_GetSaveGameDir("heretic.wad");
1158
1159 I_PrintStartupBanner(gamedescription);
1160
1161 if (M_ParmExists("-testcontrols"))
1162 {
1163 startepisode = 1;
1164 startmap = 1;
1165 autostart = true;
1166 testcontrols = true;
1167 }
1168
1169 I_InitTimer();
1170 I_InitSound(false);
1171 I_InitMusic();
1172
1173 tprintf("NET_Init: Init network subsystem.\n", 1);
1174 NET_Init ();
1175
1176 D_ConnectNetGame();
1177
1178 // haleyjd: removed WATCOMC
1179 initStartup();
1180
1181 //
1182 // Build status bar line!
1183 //
1184 smsg[0] = 0;
1185 if (deathmatch)
1186 status(DEH_String("DeathMatch..."));
1187 if (nomonsters)
1188 status(DEH_String("No Monsters..."));
1189 if (respawnparm)
1190 status(DEH_String("Respawning..."));
1191 if (autostart)
1192 {
1193 char temp[64];
1194 DEH_snprintf(temp, sizeof(temp),
1195 "Warp to Episode %d, Map %d, Skill %d ",
1196 startepisode, startmap, startskill + 1);
1197 status(temp);
1198 }
1199 wadprintf(); // print the added wadfiles
1200
1201 tprintf(DEH_String("MN_Init: Init menu system.\n"), 1);
1202 MN_Init();
1203
1204 CT_Init();
1205
1206 tprintf(DEH_String("R_Init: Init Heretic refresh daemon."), 1);
1207 hprintf(DEH_String("Loading graphics"));
1208 R_Init();
1209 tprintf("\n", 0);
1210
1211 tprintf(DEH_String("P_Init: Init Playloop state.\n"), 1);
1212 hprintf(DEH_String("Init game engine."));
1213 P_Init();
1214 IncThermo();
1215
1216 tprintf(DEH_String("I_Init: Setting up machine state.\n"), 1);
1217 I_CheckIsScreensaver();
1218 I_InitJoystick();
1219 IncThermo();
1220
1221 tprintf(DEH_String("S_Init: Setting up sound.\n"), 1);
1222 S_Init();
1223 //IO_StartupTimer();
1224 S_Start();
1225
1226 tprintf(DEH_String("D_CheckNetGame: Checking network game status.\n"), 1);
1227 hprintf(DEH_String("Checking network game status."));
1228 D_CheckNetGame();
1229 IncThermo();
1230
1231 // haleyjd: removed WATCOMC
1232
1233 tprintf(DEH_String("SB_Init: Loading patches.\n"), 1);
1234 SB_Init();
1235 IncThermo();
1236
1237 //
1238 // start the appropriate game based on params
1239 //
1240
1241 D_CheckRecordFrom();
1242
1243 //!
1244 // @arg <x>
1245 // @category demo
1246 // @vanilla
1247 //
1248 // Record a demo named x.lmp.
1249 //
1250
1251 p = M_CheckParmWithArgs("-record", 1);
1252 if (p)
1253 {
1254 G_RecordDemo(startskill, 1, startepisode, startmap, myargv[p + 1]);
1255 D_DoomLoop(); // Never returns
1256 }
1257
1258 p = M_CheckParmWithArgs("-playdemo", 1);
1259 if (p)
1260 {
1261 singledemo = true; // Quit after one demo
1262 G_DeferedPlayDemo(demolumpname);
1263 D_DoomLoop(); // Never returns
1264 }
1265
1266 // [crispy] we don't play a demo, so don't skip maps
1267 crispy->demowarp = 0;
1268
1269 p = M_CheckParmWithArgs("-timedemo", 1);
1270 if (p)
1271 {
1272 G_TimeDemo(demolumpname);
1273 D_DoomLoop(); // Never returns
1274 }
1275
1276 //!
1277 // @category game
1278 // @arg <s>
1279 // @vanilla
1280 //
1281 // Load the game in savegame slot s.
1282 //
1283
1284 p = M_CheckParmWithArgs("-loadgame", 1);
1285 if (p && p < myargc - 1)
1286 {
1287 char *filename;
1288
1289 filename = SV_Filename(myargv[p + 1][0] - '0');
1290 G_LoadGame(filename);
1291 free(filename);
1292 }
1293
1294 // Check valid episode and map
1295 if (autostart || netgame)
1296 {
1297 if (!D_ValidEpisodeMap(heretic, gamemode, startepisode, startmap))
1298 {
1299 startepisode = 1;
1300 startmap = 1;
1301 }
1302 }
1303
1304 if (gameaction != ga_loadgame)
1305 {
1306 UpdateState |= I_FULLSCRN;
1307 BorderNeedRefresh = true;
1308 if (autostart || netgame)
1309 {
1310 G_InitNew(startskill, startepisode, startmap);
1311 }
1312 else
1313 {
1314 D_StartTitle();
1315 }
1316 }
1317
1318 finishStartup();
1319
1320 D_DoomLoop(); // Never returns
1321 }
1322