1 //-------------------------------------------------------------------------
2 /*
3 Copyright (C) 1997, 2005 - 3D Realms Entertainment
4
5 This file is part of Shadow Warrior version 1.2
6
7 Shadow Warrior is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; either version 2
10 of the License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
15
16 See the GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with this program; if not, write to the Free Software
20 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21
22 Original Source: 1997 - Frank Maddin and Jim Norwood
23 Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms
24 */
25 //-------------------------------------------------------------------------
26
27 //#define MAIN
28 //#define QUIET
29 #include "build.h"
30 #include "cache1d.h"
31 #include "osd.h"
32
33 #include "keys.h"
34 #include "names2.h"
35 #include "panel.h"
36 #include "game.h"
37 #include "net.h"
38
39 #include "mytypes.h"
40 #include "control.h"
41 #include "function.h"
42 #include "demo.h"
43
44 #include "player.h"
45 #include "menus.h"
46
47
48 DFILE DemoFileIn = DF_ERR;
49 FILE *DemoFileOut;
50 BOOL DemoPlaying = FALSE;
51 BOOL DemoRecording = FALSE;
52 BOOL DemoEdit = FALSE;
53 BOOL DemoMode = FALSE;
54 BOOL DemoModeMenuState = FALSE;
55 BOOL DemoOverride = FALSE;
56 char DemoFileName[16] = "demo.dmo";
57 char DemoLevelName[16] = "";
58 extern BOOL NewGame;
59
60 // Demo sync stuff
61 FILE *DemoSyncFile;
62 BOOL DemoSyncTest = FALSE, DemoSyncRecord = FALSE;
63 char DemoTmpName[16] = "";
64
65 SW_PACKET DemoBuffer[DEMO_BUFFER_MAX];
66 int DemoRecCnt = 0; // Can only record 1-player game
67
68 BOOL DemoDone;
69
70 VOID DemoWriteHeader(VOID);
71 VOID DemoReadHeader(VOID);
72 VOID DemoReadBuffer(VOID);
73
74
75 //
76 // DemoDebug Vars
77 //
78
79 // DemoDebugMode will close the file after every write
80 BOOL DemoDebugMode = FALSE;
81 //BOOL DemoDebugMode = TRUE;
82 BOOL DemoInitOnce = FALSE;
83 short DemoDebugBufferMax = 1;
84
85 extern char LevelName[];
86 extern char LevelSong[16];
87 extern BYTE FakeMultiNumPlayers;
88 extern BOOL QuitFlag;
89
90 ///////////////////////////////////////////
91 //
92 // Demo File Manipulation
93 //
94 ///////////////////////////////////////////
95
DemoSyncFileName(VOID)96 char *DemoSyncFileName(VOID)
97 {
98 static char file_name[32];
99 char *ptr;
100
101 strcpy(file_name, DemoFileName);
102
103 if ((ptr = strchr(file_name, '.')) == 0)
104 strcat(file_name, ".dms");
105 else
106 {
107 *ptr = '\0';
108 strcat(file_name, ".dms");
109 }
110
111 return(file_name);
112 }
113
114 VOID
DemoSetup(VOID)115 DemoSetup(VOID)
116 {
117 if (DemoRecording)
118 {
119 if (DemoSyncRecord)
120 DemoSyncFile = fopen(DemoSyncFileName(),"wb");
121
122 DemoWriteHeader();
123 memset(&DemoBuffer, -1, sizeof(DemoBuffer));
124 }
125
126 if (DemoPlaying)
127 {
128 if (DemoSyncRecord)
129 DemoSyncFile = fopen(DemoSyncFileName(),"wb");
130 if (DemoSyncTest)
131 DemoSyncFile = fopen(DemoSyncFileName(),"rb");
132
133 DemoReadHeader();
134 memset(&DemoBuffer, -1, sizeof(DemoBuffer));
135 DemoReadBuffer();
136 }
137 }
138
139 VOID
DemoRecordSetup(VOID)140 DemoRecordSetup(VOID)
141 {
142 if (DemoRecording)
143 {
144 if (DemoSyncRecord)
145 DemoSyncFile = fopen(DemoSyncFileName(),"wb");
146
147 DemoWriteHeader();
148 memset(&DemoBuffer, -1, sizeof(DemoBuffer));
149 }
150 }
151
152 VOID
DemoPlaySetup(VOID)153 DemoPlaySetup(VOID)
154 {
155 if (DemoPlaying)
156 {
157 if (DemoSyncRecord)
158 DemoSyncFile = fopen(DemoSyncFileName(),"wb");
159 if (DemoSyncTest)
160 DemoSyncFile = fopen(DemoSyncFileName(),"rb");
161
162 DemoReadHeader();
163 memset(&DemoBuffer, -1, sizeof(DemoBuffer));
164 DemoReadBuffer();
165 }
166 }
167
168 VOID
DemoWriteHeader(VOID)169 DemoWriteHeader(VOID)
170 {
171 DEMO_HEADER dh;
172 DEMO_START_POS dsp;
173 PLAYERp pp;
174
175 DemoFileOut = fopen(DemoFileName, "wb");
176
177 if (!DemoFileOut)
178 return;
179
180 strcpy(dh.map_name, LevelName);
181 strcpy(dh.LevelSong, LevelSong);
182 dh.Level = Level;
183
184 if (FakeMultiNumPlayers)
185 dh.numplayers = FakeMultiNumPlayers;
186 else
187 dh.numplayers = CommPlayers;
188
189 fwrite(&dh, sizeof(dh), 1, DemoFileOut);
190
191 for (pp = Player; pp < Player + dh.numplayers; pp++)
192 {
193 dsp.x = pp->posx;
194 dsp.y = pp->posy;
195 dsp.z = pp->posz;
196 fwrite(&dsp, sizeof(dsp), 1, DemoFileOut);
197 fwrite(&pp->Flags, sizeof(pp->Flags), 1, DemoFileOut);
198 fwrite(&pp->pang, sizeof(pp->pang), 1, DemoFileOut);
199 }
200
201 fwrite(&Skill, sizeof(Skill), 1, DemoFileOut);
202 fwrite(&gNet, sizeof(gNet), 1, DemoFileOut);
203
204 if (DemoDebugMode)
205 {
206 DemoDebugBufferMax = CommPlayers;
207 fclose(DemoFileOut);
208 }
209 }
210
211 VOID
DemoReadHeader(VOID)212 DemoReadHeader(VOID)
213 {
214 DEMO_HEADER dh;
215 DEMO_START_POS dsp;
216 PLAYERp pp;
217
218 #if DEMO_FILE_TYPE != DEMO_FILE_GROUP
219 if (DemoEdit)
220 {
221 DemoFileIn = fopen(DemoFileName, "rb+");
222 }
223 else
224 #endif
225 {
226 //DemoFileIn = fopen(DemoFileName, "rb");
227 DemoFileIn = DOPEN_READ(DemoFileName);
228 }
229
230 if (DemoFileIn == DF_ERR)
231 {
232 TerminateGame();
233 printf("File %s is not a valid demo file.",DemoFileName);
234 exit(0);
235 }
236
237 DREAD(&dh, sizeof(dh), 1, DemoFileIn);
238
239 strcpy(DemoLevelName, dh.map_name);
240 strcpy(LevelSong, dh.LevelSong);
241 Level = dh.Level;
242 if (dh.numplayers > 1)
243 {
244 FakeMultiNumPlayers = dh.numplayers;
245 }
246 else
247 CommPlayers = dh.numplayers;
248
249 for (pp = Player; pp < Player + dh.numplayers; pp++)
250 {
251 DREAD(&dsp, sizeof(dsp), 1, DemoFileIn);
252 pp->posx = dsp.x;
253 pp->posy = dsp.y;
254 pp->posz = dsp.z;
255 COVERupdatesector(pp->posx, pp->posy, &pp->cursectnum);
256 //pp->cursectnum = 0;
257 //updatesectorz(pp->posx, pp->posy, pp->posz, &pp->cursectnum);
258 DREAD(&pp->Flags, sizeof(pp->Flags), 1, DemoFileIn);
259 DREAD(&pp->pang, sizeof(pp->pang), 1, DemoFileIn);
260 }
261
262 DREAD(&Skill, sizeof(Skill), 1, DemoFileIn);
263 DREAD(&gNet, sizeof(gNet), 1, DemoFileIn);
264 }
265
266 VOID
DemoDebugWrite(VOID)267 DemoDebugWrite(VOID)
268 {
269 int size;
270
271 DemoFileOut = fopen(DemoFileName, "ab");
272
273 ASSERT(DemoFileOut);
274
275 size = sizeof(SW_PACKET) * DemoDebugBufferMax;
276 fwrite(&DemoBuffer, size, 1, DemoFileOut);
277 memset(&DemoBuffer, -1, size);
278
279 fclose(DemoFileOut);
280 }
281
282 VOID
DemoWriteBuffer(VOID)283 DemoWriteBuffer(VOID)
284 {
285 fwrite(&DemoBuffer, sizeof(DemoBuffer), 1, DemoFileOut);
286 memset(&DemoBuffer, -1, sizeof(DemoBuffer));
287 }
288
289 VOID
DemoReadBuffer(VOID)290 DemoReadBuffer(VOID)
291 {
292 memset(&DemoBuffer, -1, sizeof(DemoBuffer));
293 DREAD(&DemoBuffer, sizeof(DemoBuffer), 1, DemoFileIn);
294 }
295
296 VOID
DemoBackupBuffer(VOID)297 DemoBackupBuffer(VOID)
298 {
299 #if DEMO_FILE_TYPE != DEMO_FILE_GROUP
300 FILE *NewDemoFile;
301 FILE *OldDemoFile = DemoFileIn;
302 int pos,i;
303 char copy_buffer;
304 char NewDemoFileName[16] = "!";
305
306 // seek backwards to beginning of last buffer
307 fseek(OldDemoFile, -sizeof(DemoBuffer), SEEK_CUR);
308 pos = ftell(OldDemoFile);
309
310 // open a new edit file
311 strcat(NewDemoFileName, DemoFileName);
312 NewDemoFile = fopen(NewDemoFileName, "wb");
313
314 rewind(OldDemoFile);
315
316 // copy old demo to new demo
317 for (i = 0; i < pos; i++)
318 {
319 fread(©_buffer, sizeof(copy_buffer), 1, OldDemoFile);
320 fwrite(©_buffer,sizeof(copy_buffer), 1, NewDemoFile);
321 }
322
323 DemoFileOut = NewDemoFile;
324 fclose(OldDemoFile);
325 #endif
326 }
327
328 VOID
DemoTerm(VOID)329 DemoTerm(VOID)
330 {
331 if (DemoRecording)
332 {
333 // if already closed
334 if (DemoFileOut == NULL)
335 return;
336
337 if (DemoDebugMode)
338 {
339 DemoFileOut = fopen(DemoFileName, "ab");
340 ASSERT(DemoFileOut);
341 }
342 else
343 {
344 // paste on a -1 record to the current buffer
345 if (DemoRecCnt < DEMO_BUFFER_MAX)
346 memset(&DemoBuffer[DemoRecCnt], -1, sizeof(DemoBuffer[DemoRecCnt]));
347
348 DemoWriteBuffer();
349 }
350
351 // write at least 1 record at the end filled with -1
352 // just for good measure
353 memset(&DemoBuffer[0], -1, sizeof(DemoBuffer[0]));
354 fwrite(&DemoBuffer[0], sizeof(DemoBuffer[0]), 1, DemoFileOut);
355
356 fclose(DemoFileOut);
357 DemoFileOut = NULL;
358 }
359
360 if (DemoPlaying)
361 {
362 if (DemoFileIn == DF_ERR)
363 return;
364
365 DCLOSE(DemoFileIn);
366 DemoFileIn = DF_ERR;
367 }
368
369 if (DemoSyncTest||DemoSyncRecord)
370 {
371 fclose(DemoSyncFile);
372 DemoSyncFile = NULL;
373 }
374 }
375
376 ///////////////////////////////////////////
377 //
378 // Demo Play Back
379 //
380 ///////////////////////////////////////////
381
382
383 VOID
DemoPlayBack(VOID)384 DemoPlayBack(VOID)
385 {
386 int pnum, cnt;
387 static int buf_ndx;
388 PLAYERp pp;
389 ControlInfo info;
390 int Xdim, Ydim, ScreenSize;
391
392 if (SW_SHAREWARE) {
393 // code here needs to be similar to RunLevel startup code
394 PlaySong(LevelSong, -1, TRUE, TRUE);
395 }
396
397
398 // Initialize Game part of network code (When ready2send != 0)
399 InitNetVars();
400
401 // IMPORTANT - MUST be right before game loop
402 InitTimingVars();
403
404 // THIS STUFF DEPENDS ON MYCONNECTINDEX BEING SET RIGHT
405 pp = Player + myconnectindex;
406 SetRedrawScreen(pp);
407
408 if (!DemoInitOnce)
409 buf_ndx = 0;
410
411 // everything has been inited at least once for PLAYBACK
412 DemoInitOnce = TRUE;
413
414 cnt = 0;
415 ready2send = 0;
416 DemoDone = FALSE;
417
418 while (TRUE)
419 {
420 // makes code run at the same rate
421 while (totalclock > totalsynctics)
422 {
423 handleevents();
424
425 TRAVERSE_CONNECT(pnum)
426 {
427 pp = Player + pnum;
428 pp->inputfifo[pp->movefifoend & (MOVEFIFOSIZ-1)] = DemoBuffer[buf_ndx];
429 pp->movefifoend++;
430 buf_ndx++;
431
432 if (pp->inputfifo[(pp->movefifoend - 1) & (MOVEFIFOSIZ-1)].bits == -1)
433 {
434 DemoDone = TRUE;
435 break;
436 }
437
438 if (buf_ndx > DEMO_BUFFER_MAX - 1)
439 {
440 DemoReadBuffer();
441 buf_ndx = 0;
442 }
443 }
444
445 if (DemoDone)
446 break;
447
448 cnt++;
449
450 if (!UsingMenus)
451 CONTROL_GetInput(&info);
452
453 domovethings();
454
455 OSD_DispatchQueued();
456 MNU_CheckForMenus();
457
458 // fast forward and slow mo
459 if (DemoEdit)
460 {
461 if (KEY_PRESSED(KEYSC_F))
462 {
463 if (KEY_PRESSED(KEYSC_LSHIFT) || KEY_PRESSED(KEYSC_RSHIFT))
464 totalclock += synctics;
465 else
466 totalclock += synctics-1;
467 }
468
469 if (KEY_PRESSED(KEYSC_S))
470 totalclock += 1-synctics;
471 }
472 else
473 {
474 #if DEBUG
475 if (KEY_PRESSED(KEYSC_ALT) && KEY_PRESSED(KEYSC_CTRL) && KEY_PRESSED(KEYSC_S))
476 {
477 KEY_PRESSED(KEYSC_ALT) = KEY_PRESSED(KEYSC_CTRL) = KEY_PRESSED(KEYSC_S) = 0;
478 saveboard("demosave.map", &Player->posx, &Player->posy, &Player->posz, &Player->pang, &Player->cursectnum);
479 }
480 #endif
481
482 if (BUTTON(gamefunc_See_Co_Op_View))
483 {
484 CONTROL_ClearButton(gamefunc_See_Co_Op_View);
485 NextScreenPeek();
486 }
487
488 #if DEBUG
489 if (KEY_PRESSED(KEYSC_RIGHT) || KEY_PRESSED(KEYSC_UP))
490 {
491 if (KEY_PRESSED(KEYSC_LSHIFT) || KEY_PRESSED(KEYSC_RSHIFT))
492 totalclock += synctics;
493 else
494 totalclock += synctics-1;
495 }
496
497 if (KEY_PRESSED(KEYSC_LEFT) || KEY_PRESSED(KEYSC_DOWN))
498 totalclock += 1-synctics;
499 #endif
500 }
501
502
503 if (DemoSyncRecord)
504 demosync_record();
505 if (DemoSyncTest)
506 demosync_test(cnt);
507 }
508
509 // Put this back in later when keyboard stuff is stable
510 if (DemoEdit)
511 {
512 //CONTROL_GetButtonInput();
513 CONTROL_GetInput(&info);
514
515 // if a key is pressed, start recording from the point the key
516 // was pressed
517 if (BUTTON(gamefunc_Move_Forward) ||
518 BUTTON(gamefunc_Move_Backward) ||
519 BUTTON(gamefunc_Turn_Left) ||
520 BUTTON(gamefunc_Turn_Right) ||
521 BUTTON(gamefunc_Fire) ||
522 BUTTON(gamefunc_Open) ||
523 BUTTON(gamefunc_Jump) ||
524 BUTTON(gamefunc_Crouch) ||
525 BUTTON(gamefunc_Look_Up) ||
526 BUTTON(gamefunc_Look_Down))
527 {
528 DemoBackupBuffer();
529
530 DemoRecCnt = buf_ndx;
531 DemoPlaying = FALSE;
532 DemoRecording = TRUE;
533 return;
534 }
535 }
536
537 if (BUTTON(gamefunc_See_Co_Op_View))
538 {
539 NextScreenPeek();
540 }
541
542 // demo is over
543 if (DemoDone)
544 break;
545
546 if (QuitFlag)
547 {
548 DemoMode = FALSE;
549 break;
550 }
551
552 if (ExitLevel)
553 {
554 // Quiting Demo
555 ExitLevel = FALSE;
556 if (DemoMode)
557 {
558 DemoPlaying = FALSE;
559 DemoMode = FALSE;
560 }
561 break;
562 }
563
564 drawscreen(Player + screenpeek);
565 }
566
567 // only exit if conditions are write
568 if (DemoDone && !DemoMode && !NewGame)
569 {
570 TerminateLevel();
571 TerminateGame();
572 exit(0);
573 }
574
575 }
576
577 //
578 // Still using old method of playback - this was for opening demo
579 //
580
581 VOID
ScenePlayBack(VOID)582 ScenePlayBack(VOID)
583 {
584 int buf_ndx, pnum, cnt;
585 PLAYERp pp;
586
587 if (SW_SHAREWARE) {
588 // code here needs to be similar to RunLevel startup code
589 strcpy(LevelSong,"yokoha03.mid");
590 PlaySong(LevelSong, -1, TRUE, TRUE);
591 }
592
593 // IMPORTANT - MUST be right before game loop
594 InitTimingVars();
595
596 buf_ndx = 0;
597 cnt = 0;
598 ready2send = 0;
599 DemoDone = FALSE;
600
601 ResetKeys();
602
603 while (TRUE)
604 {
605 // makes code run at the same rate
606 while ((totalclock > totalsynctics))
607 {
608 TRAVERSE_CONNECT(pnum)
609 {
610 pp = Player + pnum;
611 pp->inputfifo[pp->movefifoend & (MOVEFIFOSIZ - 1)] = DemoBuffer[buf_ndx];
612 pp->movefifoend++;
613 buf_ndx++;
614
615 if (pp->inputfifo[(pp->movefifoend - 1) & (MOVEFIFOSIZ - 1)].bits == -1)
616 {
617 DemoDone = TRUE;
618 break;
619 }
620
621 if (buf_ndx > DEMO_BUFFER_MAX - 1)
622 {
623 DemoReadBuffer();
624 buf_ndx = 0;
625 }
626 }
627
628 if (KeyPressed())
629 DemoDone = TRUE;
630
631 if (DemoDone)
632 break;
633
634 cnt++;
635
636 //movethings();
637 domovethings();
638
639 MNU_CheckForMenus();
640 }
641
642 // demo is over
643 if (DemoDone)
644 break;
645
646 drawscreen(Player + screenpeek);
647 }
648 }
649
650
651
652