1 /* $Id: newdemo.c,v 1.12 2003-03-18 02:31:16 btb Exp $ */
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
17 * Code to make a complete demo playback system.
19 * $Log: not supported by cvs2svn $
20 * Revision 1.11 2003/03/17 07:59:11 btb
21 * also look in shared data dir for demos
23 * Revision 1.10 2003/03/17 07:10:21 btb
26 * Revision 1.12 1995/10/31 10:19:43 allender
29 * Revision 1.11 1995/10/17 13:19:16 allender
30 * close boxes for demo save
32 * Revision 1.10 1995/10/05 10:36:07 allender
33 * fixed calls to digi_play_sample to account for differing volume and angle calculations
35 * Revision 1.9 1995/09/12 15:49:13 allender
36 * define __useAppleExts__ if not already defined
38 * Revision 1.8 1995/09/05 14:28:32 allender
39 * fixed previous N_players bug in newdemo_goto_end
41 * Revision 1.7 1995/09/05 14:16:51 allender
42 * added space to allowable demo filename characters
43 * and fixed bug with netgame demos N_players got getting
46 * Revision 1.6 1995/09/01 16:10:47 allender
47 * fixed bug with reading in N_players variable on
48 * netgame demo playback
50 * Revision 1.5 1995/08/24 16:04:11 allender
51 * fix signed byte problem
53 * Revision 1.4 1995/08/12 12:21:59 allender
54 * made call to create_shortpos not swap the shortpos
57 * Revision 1.3 1995/08/01 16:04:19 allender
58 * made random picking of demo work
60 * Revision 1.2 1995/08/01 13:56:56 allender
61 * demo system working on the mac
63 * Revision 1.1 1995/05/16 15:28:59 allender
66 * Revision 2.7 1995/05/26 16:16:06 john
67 * Split SATURN into define's for requiring cd, using cd, etc.
68 * Also started adding all the Rockwell stuff.
70 * Revision 2.6 1995/03/21 14:39:38 john
71 * Ifdef'd out the NETWORK code.
73 * Revision 2.5 1995/03/14 18:24:31 john
74 * Force Destination Saturn to use CD-ROM drive.
76 * Revision 2.4 1995/03/14 16:22:29 john
77 * Added cdrom alternate directory stuff.
79 * Revision 2.3 1995/03/10 12:58:33 allender
80 * only display rear view cockpit when cockpit mode was CM_FULL_COCKPIT.
82 * Revision 2.2 1995/03/08 16:12:15 allender
83 * changes for Destination Saturn
85 * Revision 2.1 1995/03/08 12:11:26 allender
86 * fix shortpos reading
88 * Revision 2.0 1995/02/27 11:29:40 john
89 * New version 2.0, which has no anonymous unions, builds with
90 * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
92 * Revision 1.189 1995/02/22 14:53:42 allender
93 * missed some anonymous stuff
95 * Revision 1.188 1995/02/22 13:24:53 john
96 * Removed the vecmat anonymous unions.
98 * Revision 1.187 1995/02/22 13:13:54 allender
99 * remove anonymous unions from object structure
101 * Revision 1.186 1995/02/14 15:36:41 allender
102 * fix fix for morph effect
104 * Revision 1.185 1995/02/14 11:25:48 allender
105 * save cockpit mode and restore after playback. get orientation for morph
106 * effect when object is morph vclip
108 * Revision 1.184 1995/02/13 12:18:14 allender
109 * change to decide about interpolating or not
111 * Revision 1.183 1995/02/12 00:46:23 adam
112 * don't decide to skip frames until after at least 10 frames have
115 * Revision 1.182 1995/02/11 22:34:01 john
116 * Made textures page in for newdemos before playback time.
118 * Revision 1.181 1995/02/11 17:28:32 allender
119 * strip frames from end of demo
121 * Revision 1.180 1995/02/11 16:40:35 allender
122 * start of frame stripping debug code
124 * Revision 1.179 1995/02/10 17:40:06 allender
125 * put back in wall_hit_process code to fix door problem
127 * Revision 1.178 1995/02/10 17:17:24 adam
130 * Revision 1.177 1995/02/10 17:16:24 allender
131 * fix possible tmap problems
133 * Revision 1.176 1995/02/10 15:54:37 allender
134 * changes for out of space on device.
136 * Revision 1.175 1995/02/09 19:55:00 allender
137 * fix bug with morph recording -- force rendertype to RT_POLYOBJ when
138 * playing back since it won't render until fully morphed otherwise
140 * Revision 1.174 1995/02/07 17:15:35 allender
143 * Revision 1.173 1995/02/07 17:14:21 allender
144 * immediately return when loading bogus level stuff when reading a frame
146 * Revision 1.172 1995/02/02 11:15:03 allender
147 * after loading new level, read next frame (forward or back) always because
148 * of co-op ships showing up when level is loaded
150 * Revision 1.171 1995/02/02 10:24:16 allender
151 * removed cfile stuff. Use standard FILE functions for demo playback
153 * Revision 1.170 1995/01/30 13:54:32 allender
154 * support for missions
156 * Revision 1.169 1995/01/27 16:27:35 allender
157 * put game mode to demo_game_mode when sorting multiplayer kill (and score)
160 * Revision 1.168 1995/01/27 09:52:25 allender
161 * minor changes because of object/segment linking problems
163 * Revision 1.167 1995/01/27 09:22:28 allender
164 * changed way multi-player score is recorded. Record difference, not
167 * Revision 1.166 1995/01/25 14:32:44 allender
168 * changed with recorded player flags. More checks for paused state
169 * during interpolation reading of objects
171 * Revision 1.165 1995/01/25 11:23:32 allender
172 * found bug with out of disk space problem
174 * Revision 1.164 1995/01/25 11:11:33 allender
175 * coupla' things. Fix problem with objects apparently being linked in
176 * the wrong segment. Put an Int3 in to check why demos will write to
177 * end of space on drive. close demo file if demo doens't start playing
178 * back. Put obj->type == OBJ_ROBOT around checking for boss cloaking
180 * Revision 1.163 1995/01/24 19:44:30 allender
181 * fix obscure problem with rewinding and having the wrong object linked
182 * to the wrong segments. will investigate further.
184 * Revision 1.162 1995/01/23 09:31:28 allender
185 * add team score in team mode playback
187 * Revision 1.161 1995/01/20 22:47:39 matt
188 * Mission system implemented, though imcompletely
190 * Revision 1.160 1995/01/20 09:30:37 allender
191 * don't call LoadLevel with bogus data
193 * Revision 1.159 1995/01/20 09:13:23 allender
196 * Revision 1.158 1995/01/20 09:12:04 allender
197 * record team names during demo recoring in GM_TEAM
199 * Revision 1.157 1995/01/19 16:31:09 allender
200 * forgot to bump demo version for new weapon change stuff
202 * Revision 1.156 1995/01/19 16:29:33 allender
203 * added new byte for weapon change (old weapon) so rewinding works
204 * correctly for weapon changes in registered
206 * Revision 1.155 1995/01/19 15:00:05 allender
207 * remove code to take away blastable walls in multiplayer demo playbacks
209 * Revision 1.154 1995/01/19 11:07:05 allender
210 * put in psuedo cloaking for boss robots. Problem is that cloaking is
211 * time based, and that don't get done in demos, so bosses just disappear.
214 * Revision 1.153 1995/01/19 09:42:29 allender
215 * record laser levels in demos
217 * Revision 1.152 1995/01/18 20:43:12 allender
218 * fix laser level stuff on goto-beginning and goto-end
220 * Revision 1.151 1995/01/18 20:28:18 allender
221 * cloak robots now cloak (except maybe for boss........) Put in function
222 * to deal with control center triggers
224 * Revision 1.150 1995/01/18 18:55:07 allender
227 * Revision 1.149 1995/01/18 18:49:03 allender
228 * lots 'o stuff....record laser level. Record beginning of door opening
229 * sequence. Fix some problems with control center stuff. Control center
230 * triggers now work in reverse
232 * Revision 1.148 1995/01/18 08:51:40 allender
233 * forgot to record ammo counts at beginning of demo
235 * Revision 1.147 1995/01/17 17:42:07 allender
236 * added primary and secondary ammo counts. Changed goto_end routine
237 * to be more efficient
239 * Revision 1.146 1995/01/17 13:46:35 allender
240 * fix problem with destroyed control center and rewinding a demo.
241 * Save callsign and restore after demo playback
243 * Revision 1.145 1995/01/12 10:21:53 allender
244 * fixes for 1.0 to 1.1 demo incompatibility
246 * Revision 1.144 1995/01/05 13:51:43 allender
247 * fixed type of player num variable
249 * Revision 1.143 1995/01/04 16:58:28 allender
250 * bumped up demo version number
252 * Revision 1.142 1995/01/04 14:59:02 allender
253 * added more information to end of demo for registered. Forced game mode
254 * to be GM_NORMAL on demo playback
256 * Revision 1.141 1995/01/03 17:30:47 allender
257 * fixed logic problem with cloak stuf
259 * Revision 1.140 1995/01/03 17:12:23 allender
260 * fix for getting cloak stuff at end of demo for shareware
262 * Revision 1.139 1995/01/03 15:20:24 allender
263 * fix goto_end for shareware -- changes to goto_end for registered
265 * Revision 1.138 1995/01/03 13:13:26 allender
268 * Revision 1.137 1995/01/03 13:10:29 allender
269 * make score work forwards and backwards
271 * Revision 1.136 1995/01/03 11:45:20 allender
272 * added code to record players scores
274 * Revision 1.135 1994/12/30 10:03:57 allender
275 * put cloak stuff at end of demo for fast forward to the end
277 * Revision 1.134 1994/12/29 17:02:55 allender
278 * spelling fix on SHAREWARE
280 * Revision 1.133 1994/12/29 16:43:41 allender
281 * lots of new multiplayer stuff. wrapped much code with SHAREWARE defines
283 * Revision 1.132 1994/12/28 14:15:01 allender
284 * added routines to deal with connecting and reconnecting players when
285 * recording multiplayer demos
287 * Revision 1.131 1994/12/21 12:57:59 allender
290 * Revision 1.130 1994/12/21 12:46:53 allender
291 * record multi player deaths and kills
293 * Revision 1.129 1994/12/19 16:37:27 allender
294 * pick good filename when trying to save in network play and player
295 * gets bumped out of menu by multi-player code
297 * Revision 1.128 1994/12/14 10:49:01 allender
298 * reset bad_read variable when starting demo playback
300 * Revision 1.127 1994/12/14 08:53:06 allender
301 * lowered watermark for out of space
303 * Revision 1.126 1994/12/14 08:49:52 allender
304 * put up warning when starting demo recording if not enough space and
305 * not let them record
307 * Revision 1.125 1994/12/13 00:01:37 allender
308 * CLOAK FIX -- (I'm tempted to take cloak out of game because I can't
309 * seem to get it right in demo playback)
311 * Revision 1.124 1994/12/12 14:51:21 allender
312 * more fixed to multiplayer cloak stuff
314 * Revision 1.123 1994/12/12 11:33:11 allender
315 * fixed rearview mode to work again
317 * Revision 1.122 1994/12/12 11:00:16 matt
318 * Added code to handle confusion with attached objects
320 * Revision 1.121 1994/12/12 00:31:29 allender
321 * give better warning when out of space when recording. Don't record
322 * until no space left. We have 500K watermark when we now stop recording
324 * Revision 1.120 1994/12/10 16:44:54 matt
325 * Added debugging code to track down door that turns into rock
327 * Revision 1.119 1994/12/09 18:46:15 matt
328 * Added code to handle odd error condition
330 * Revision 1.118 1994/12/09 17:27:37 allender
331 * force playernum to 0 when demo is done playing
333 * Revision 1.117 1994/12/09 16:40:39 allender
334 * yet more cloak stuff. Assign cloak/invuln time when starting demo
335 * if flags are set. Check cloak and invuln time when demo
338 * Revision 1.116 1994/12/09 14:59:22 matt
339 * Added system to attach a fireball to another object for rendering purposes,
340 * so the fireball always renders on top of (after) the object.
342 * Revision 1.115 1994/12/09 12:21:45 allender
343 * only allow valid chars when typing in demo filename
345 * Revision 1.114 1994/12/08 23:19:02 allender
346 * final(?) fix for getting cloak gauge to work on demo playback
347 * with forward and reverse
349 * Revision 1.113 1994/12/08 21:34:38 allender
350 * record old and new player flags to accuratedly record cloaking and
354 * Revision 1.112 1994/12/08 18:04:47 allender
355 * bashed playernum right after reading it in demo header so shields
356 * and energy are put in right place
358 * Revision 1.111 1994/12/08 17:10:07 allender
359 * encode playernum in demo header. Bash viewer segment to 0 if in
360 * bogus segnum. Don't link render objs for same reason
362 * Revision 1.110 1994/12/08 15:36:12 allender
363 * cloak stuff works forwards and backwards
365 * Revision 1.109 1994/12/08 13:46:03 allender
366 * don't record rearview anymore, but leave in case statement for playback
367 * purposes. change the way letterbox <--> cockpit transitions happen
369 * Revision 1.108 1994/12/08 12:36:06 matt
370 * Added new object allocation & deallocation functions so other code
371 * could stop messing around with internal object data structures.
373 * Revision 1.107 1994/12/08 11:19:04 allender
374 * handle out of space (more) gracefully then before
376 * Revision 1.106 1994/12/08 00:29:49 allender
377 * fixed bug that didn't load level on goto_beginning
379 * Revision 1.105 1994/12/08 00:11:51 mike
380 * change matrix interpolation.
382 * Revision 1.104 1994/12/07 23:46:37 allender
383 * changed invulnerability and cloak to work (almost) correctly both
384 * in single and multi player
386 * Revision 1.103 1994/12/07 11:48:49 adam
387 * BY ALLENDER -- added dampening of interpolation factor to 1 if greater
388 * than 1 (although I have not seen this happen). this is attempt to
389 * get wobbling problem solved
391 * Revision 1.102 1994/12/07 11:23:56 allender
392 * attempt at getting rid of wobbling on demo playback
394 * Revision 1.101 1994/12/06 19:31:17 allender
395 * moved blastable wall stuff code to where we load level during demo
398 * Revision 1.100 1994/12/06 19:21:51 allender
399 * multi games, destroy blastable walls. Do wall toggle when control center
402 * Revision 1.99 1994/12/06 16:54:48 allender
403 * fixed code so if demo automatically started from menu, don't bring up
404 * message if demo is too old
406 * Revision 1.98 1994/12/06 13:55:15 matt
407 * Use new rounding func, f2ir()
409 * Revision 1.97 1994/12/06 13:44:45 allender
410 * suppressed compiler warnings
412 * Revision 1.96 1994/12/06 13:38:03 allender
413 * removed recording of wall hit process. I think that all bases are covered
416 * Revision 1.95 1994/12/06 12:57:35 allender
417 * added recording of multi_decloaking. Fixed some other cloaking code so
418 * that cloak should last as long as player was cloaked. We will lose the
419 * guage effect, but the time is probably more important on playback
421 * Revision 1.94 1994/12/05 23:37:17 matt
422 * Took out calls to warning() function
424 * Revision 1.93 1994/12/03 17:52:04 yuan
425 * Localization 380ish
427 * Revision 1.92 1994/12/02 12:53:39 allender
428 * fixed goto_beginning and goto_end on demo playback
430 * Revision 1.91 1994/12/01 12:01:49 allender
431 * added multi player cloak stuff
433 * Revision 1.90 1994/11/30 09:33:58 allender
434 * added field in header to tell what version (shareware or registered)
435 * demo was recorded with. Don't allow demo recorded on one to playback
438 * Revision 1.89 1994/11/29 00:31:01 allender
439 * major changes -- added level recording feature which records level
440 * advancement. Changes to internal code to handle this.
442 * Revision 1.88 1994/11/27 23:13:54 matt
443 * Made changes for new mprintf calling convention
445 * Revision 1.87 1994/11/27 23:07:35 allender
446 * starting on code to get all level transitions recorded. not done yet
448 * Revision 1.86 1994/11/27 17:39:47 matt
449 * Don't xlate tmap numbers when editor compiled out
451 * Revision 1.85 1994/11/23 09:27:21 allender
452 * put up info box with message if demo version is too old or level
455 * Revision 1.84 1994/11/22 19:37:39 allender
458 * Revision 1.83 1994/11/22 19:35:09 allender
459 * record player ship colors in multiplayer demo recordings
461 * Revision 1.82 1994/11/19 15:36:42 mike
464 * Revision 1.81 1994/11/19 15:23:21 mike
465 * rip out unused code
467 * Revision 1.80 1994/11/16 14:51:49 rob
468 * Fixed network/demo incompatibility.
470 * Revision 1.79 1994/11/15 10:55:48 allender
471 * made start of demo playback read initial demo information so
472 * level will get loaded. Made demo record to single file which
473 * will get renamed. Added numerics after old filename so
474 * sequential filenames would be defaulted to
476 * Revision 1.78 1994/11/15 09:46:06 allender
477 * added versioning. Fixed problems with trying to interpolating a completely
478 * 0 orientation matrix
480 * Revision 1.77 1994/11/14 14:34:31 matt
481 * Fixed up handling when textures can't be found during remap
483 * Revision 1.76 1994/11/14 09:15:29 allender
484 * make ESC from file save menu exit w/o saving. Fix letterbox, rear view,
485 * to normal cockpit mode transition to work correctly when skipping and
486 * interpolating frames
488 * Revision 1.75 1994/11/11 16:22:07 allender
489 * made morphing objects record only the object being morphed.
491 * Revision 1.74 1994/11/08 14:59:19 john
492 * Added code to respond to network while in menus.
494 * Revision 1.73 1994/11/08 14:52:20 adam
495 * *** empty log message ***
497 * Revision 1.72 1994/11/07 15:47:04 allender
498 * prompt for filename when done recording demo
500 * Revision 1.71 1994/11/07 11:47:19 allender
501 * when interpolating frames, delete weapon, fireball, and debris objects
502 * from an inpolated frame if they don't appear in the next recorded
505 * Revision 1.70 1994/11/07 11:02:41 allender
506 * more with interpolation. I believe that I have it right now
508 * Revision 1.69 1994/11/07 08:47:40 john
509 * Made wall state record.
511 * Revision 1.68 1994/11/05 17:22:51 john
512 * Fixed lots of sequencing problems with newdemo stuff.
514 * Revision 1.67 1994/11/04 20:11:52 john
515 * Neatening up palette stuff with demos.
517 * Revision 1.66 1994/11/04 16:49:44 allender
518 * changed newdemo_do_interpolate to default to on
520 * Revision 1.65 1994/11/04 16:44:51 allender
521 * added filename support for demo recording. more auto demo stuff
523 * Revision 1.64 1994/11/04 13:05:31 allender
524 * fixing the lifeleft variable again. (I think I got it right this time)
526 * Revision 1.63 1994/11/04 11:37:37 allender
527 * commented out fprintfs and fixed compiler warning
529 * Revision 1.62 1994/11/04 11:33:50 allender
530 * added OBJ_FLARE and OBJ_LIGHT to obj->lifeleft recording
532 * Revision 1.61 1994/11/04 11:29:21 allender
533 * more interpolation stuff -- not done yet. Fixed so hostage vclips
534 * render correctly. Changed lifeleft to full precision, but only
535 * write it when object is fireball or weapon type of object
537 * Revision 1.60 1994/11/03 10:00:11 allender
538 * fixed divide by zero in calculating render time. more interpolation
539 * stuff which isn't quite done
541 * Revision 1.59 1994/11/02 17:10:59 allender
542 * never play recorded frames when interpolation is occuring
544 * Revision 1.58 1994/11/02 14:28:58 allender
545 * profile total playback time and average frame render time
547 * Revision 1.57 1994/11/02 14:09:03 allender
548 * record rear view. start of playback interpolation code -- this
551 * Revision 1.56 1994/11/01 13:25:30 allender
552 * drop frames if playing back demo on slower machine
554 * Revision 1.55 1994/10/31 16:10:40 allender
555 * record letterbox mode on death seq, and then restore
557 * Revision 1.54 1994/10/29 16:01:38 allender
558 * added ND_STATE_NODEMOS to indicate that there are no demos currently
559 * available for playback
561 * Revision 1.53 1994/10/29 15:38:42 allender
562 * in newdemo_start_playback, make Newdemo_at_eof = 0
564 * Revision 1.52 1994/10/28 14:45:28 john
565 * fixed typo from last checkin.
567 * Revision 1.51 1994/10/28 14:42:55 john
568 * Added sound volumes to all sound calls.
570 * Revision 1.50 1994/10/28 14:31:57 allender
571 * homing missle and autodemo stuff
573 * Revision 1.49 1994/10/28 12:42:14 allender
574 * record homing distance
576 * Revision 1.48 1994/10/27 16:57:54 allender
577 * changed demo vcr to be able to play any number of frames by storing
578 * frame length (in bytes) in the demo file. Added blowing up monitors
580 * Revision 1.47 1994/10/26 16:50:50 allender
581 * put two functions inside of VCR_MODE ifdef
583 * Revision 1.46 1994/10/26 15:20:32 allender
584 * added CT_REMOTE as valid control type for recording
586 * Revision 1.45 1994/10/26 14:45:35 allender
587 * completed hacked in vcr demo playback stuff
589 * Revision 1.44 1994/10/26 13:40:52 allender
590 * vcr playback of demo stuff
592 * Revision 1.43 1994/10/26 08:51:57 allender
593 * record player weapon change
595 * Revision 1.42 1994/10/25 15:48:01 allender
596 * add shields, energy, and player flags to demo recording.
599 * Revision 1.41 1994/10/24 08:19:35 allender
600 * fixed compilation errors
602 * Revision 1.40 1994/10/23 19:17:08 matt
603 * Fixed bug with "no key" messages
605 * Revision 1.39 1994/10/22 14:15:08 mike
606 * Suppress compiler warnings.
608 * Revision 1.38 1994/10/21 15:24:55 allender
609 * compressed writing of object structures with specialized code
610 * to write out only pertinent object structures.
612 * Revision 1.37 1994/10/20 13:03:17 matt
613 * Replaced old save files (MIN/SAV/HOT) with new LVL files
615 * Revision 1.36 1994/09/28 23:13:10 matt
616 * Macroized palette flash system
618 * Revision 1.35 1994/09/26 17:28:32 matt
619 * Made new multiple-object morph code work with the demo system
621 * Revision 1.34 1994/09/10 13:31:54 matt
622 * Made exploding walls a type of blastable walls.
623 * Cleaned up blastable walls, making them tmap2 bitmaps.
625 * Revision 1.33 1994/08/15 18:05:28 john
626 * *** empty log message ***
628 * Revision 1.32 1994/08/15 17:56:38 john
631 * Revision 1.31 1994/08/10 09:44:54 john
632 * *** empty log message ***
634 * Revision 1.30 1994/07/22 12:35:48 matt
635 * Cleaned up editor/game interactions some more.
637 * Revision 1.29 1994/07/21 13:06:45 matt
638 * Ripped out remants of old demo system, and added demo only system that
639 * disables object movement and game options from menu.
641 * Revision 1.28 1994/07/18 16:22:44 john
642 * Made all file read/writes call the same routine.
644 * Revision 1.27 1994/07/14 22:38:27 matt
645 * Added exploding doors
647 * Revision 1.26 1994/07/05 12:49:04 john
648 * Put functionality of New Hostage spec into code.
650 * Revision 1.25 1994/06/29 11:05:38 john
651 * Made demos read in compressed.
653 * Revision 1.24 1994/06/29 09:14:06 john
654 * Made files write out uncompressed and read in compressed.
656 * Revision 1.23 1994/06/28 11:55:28 john
657 * Made newdemo system record/play directly to/from disk, so
658 * we don't need the 4 MB buffer anymore.
660 * Revision 1.22 1994/06/27 15:52:38 john
661 * #define'd out the newdemo stuff
664 * Revision 1.21 1994/06/22 00:29:04 john
665 * Fixed bug with playing demo then playing game without
668 * Revision 1.20 1994/06/22 00:14:23 john
669 * Attempted to fix sign.
671 * Revision 1.19 1994/06/21 23:57:54 john
672 * Hopefully fixed bug with negative countdowns.
674 * Revision 1.18 1994/06/21 23:47:44 john
675 * MAde Malloc always 4*1024*1024.
677 * Revision 1.17 1994/06/21 22:58:47 john
678 * Added error if out of memory.
680 * Revision 1.16 1994/06/21 22:15:48 john
681 * Added % done to demo recording.
684 * Revision 1.15 1994/06/21 19:45:55 john
685 * Added palette effects to demo recording.
687 * Revision 1.14 1994/06/21 15:08:54 john
688 * Made demo record HUD message and cleaned up the HUD code.
690 * Revision 1.13 1994/06/21 14:20:08 john
691 * Put in hooks to record HUD messages.
693 * Revision 1.12 1994/06/20 11:50:15 john
694 * Made demo record flash effect, and control center triggers.
696 * Revision 1.11 1994/06/17 18:01:33 john
697 * A bunch of new stuff by John
699 * Revision 1.10 1994/06/17 12:13:31 john
700 * More newdemo stuff; made editor->game transition start in slew mode.
702 * Revision 1.9 1994/06/16 13:14:36 matt
705 * Revision 1.8 1994/06/16 13:02:07 john
708 * Revision 1.7 1994/06/15 19:01:33 john
709 * Added the capability to make 3d sounds play just once for the
710 * laser hit wall effects.
712 * Revision 1.6 1994/06/15 14:56:59 john
713 * Added triggers to demo recording.
715 * Revision 1.5 1994/06/14 20:42:15 john
716 * Made robot matztn cntr not work until no robots or player are
719 * Revision 1.4 1994/06/14 14:43:27 john
720 * Made doors work with newdemo system.
722 * Revision 1.3 1994/06/14 11:32:29 john
723 * Made Newdemo record & restore the current mine.
725 * Revision 1.2 1994/06/13 21:02:43 john
726 * Initial version of new demo recording system.
728 * Revision 1.1 1994/06/13 11:09:00 john
742 #include <string.h> // for memset
744 #include <ctype.h> /* for isdigit */
747 #include <sys/stat.h>
748 #include <sys/types.h>
773 #include "fireball.h"
786 #include "lighting.h"
789 #include "gamesave.h"
790 #include "gamemine.h"
803 #include "cntrlcen.h"
804 #include "aistruct.h"
807 #include "controls.h"
811 #include "findfile.h"
814 #include "editor/editor.h"
818 #pragma global_optimizer off // pretty much sucks...need to look into this
821 void DoJasonInterpolate (fix recorded_time);
823 //#include "nocfile.h"
825 //Does demo start automatically?
828 byte WasRecorded [MAX_OBJECTS];
829 byte ViewWasRecorded[MAX_OBJECTS];
830 byte RenderingWasRecorded[32];
832 #define ND_EVENT_EOF 0 // EOF
833 #define ND_EVENT_START_DEMO 1 // Followed by 16 character, NULL terminated filename of .SAV file to use
834 #define ND_EVENT_START_FRAME 2 // Followed by integer frame number, then a fix FrameTime
835 #define ND_EVENT_VIEWER_OBJECT 3 // Followed by an object structure
836 #define ND_EVENT_RENDER_OBJECT 4 // Followed by an object structure
837 #define ND_EVENT_SOUND 5 // Followed by int soundum
838 #define ND_EVENT_SOUND_ONCE 6 // Followed by int soundum
839 #define ND_EVENT_SOUND_3D 7 // Followed by int soundum, int angle, int volume
840 #define ND_EVENT_WALL_HIT_PROCESS 8 // Followed by int segnum, int side, fix damage
841 #define ND_EVENT_TRIGGER 9 // Followed by int segnum, int side, int objnum
842 #define ND_EVENT_HOSTAGE_RESCUED 10 // Followed by int hostage_type
843 #define ND_EVENT_SOUND_3D_ONCE 11 // Followed by int soundum, int angle, int volume
844 #define ND_EVENT_MORPH_FRAME 12 // Followed by ? data
845 #define ND_EVENT_WALL_TOGGLE 13 // Followed by int seg, int side
846 #define ND_EVENT_HUD_MESSAGE 14 // Followed by char size, char * string (+null)
847 #define ND_EVENT_CONTROL_CENTER_DESTROYED 15 // Just a simple flag
848 #define ND_EVENT_PALETTE_EFFECT 16 // Followed by short r,g,b
849 #define ND_EVENT_PLAYER_ENERGY 17 // followed by byte energy
850 #define ND_EVENT_PLAYER_SHIELD 18 // followed by byte shields
851 #define ND_EVENT_PLAYER_FLAGS 19 // followed by player flags
852 #define ND_EVENT_PLAYER_WEAPON 20 // followed by weapon type and weapon number
853 #define ND_EVENT_EFFECT_BLOWUP 21 // followed by segment, side, and pnt
854 #define ND_EVENT_HOMING_DISTANCE 22 // followed by homing distance
855 #define ND_EVENT_LETTERBOX 23 // letterbox mode for death seq.
856 #define ND_EVENT_RESTORE_COCKPIT 24 // restore cockpit after death
857 #define ND_EVENT_REARVIEW 25 // going to rear view mode
858 #define ND_EVENT_WALL_SET_TMAP_NUM1 26 // Wall changed
859 #define ND_EVENT_WALL_SET_TMAP_NUM2 27 // Wall changed
860 #define ND_EVENT_NEW_LEVEL 28 // followed by level number
861 #define ND_EVENT_MULTI_CLOAK 29 // followed by player num
862 #define ND_EVENT_MULTI_DECLOAK 30 // followed by player num
863 #define ND_EVENT_RESTORE_REARVIEW 31 // restore cockpit after rearview mode
864 #define ND_EVENT_MULTI_DEATH 32 // with player number
865 #define ND_EVENT_MULTI_KILL 33 // with player number
866 #define ND_EVENT_MULTI_CONNECT 34 // with player number
867 #define ND_EVENT_MULTI_RECONNECT 35 // with player number
868 #define ND_EVENT_MULTI_DISCONNECT 36 // with player number
869 #define ND_EVENT_MULTI_SCORE 37 // playernum / score
870 #define ND_EVENT_PLAYER_SCORE 38 // followed by score
871 #define ND_EVENT_PRIMARY_AMMO 39 // with old/new ammo count
872 #define ND_EVENT_SECONDARY_AMMO 40 // with old/new ammo count
873 #define ND_EVENT_DOOR_OPENING 41 // with segment/side
874 #define ND_EVENT_LASER_LEVEL 42 // no data
875 #define ND_EVENT_PLAYER_AFTERBURNER 43 // followed by byte old ab, current ab
876 #define ND_EVENT_CLOAKING_WALL 44 // info changing while wall cloaking
877 #define ND_EVENT_CHANGE_COCKPIT 45 // change the cockpit
878 #define ND_EVENT_START_GUIDED 46 // switch to guided view
879 #define ND_EVENT_END_GUIDED 47 // stop guided view/return to ship
880 #define ND_EVENT_SECRET_THINGY 48 // 0/1 = secret exit functional/non-functional
881 #define ND_EVENT_LINK_SOUND_TO_OBJ 49 // record digi_link_sound_to_object3
882 #define ND_EVENT_KILL_SOUND_TO_OBJ 50 // record digi_kill_sound_linked_to_object
885 #define NORMAL_PLAYBACK 0
886 #define SKIP_PLAYBACK 1
887 #define INTERPOLATE_PLAYBACK 2
888 #define INTERPOL_FACTOR (F1_0 + (F1_0/5))
890 #define DEMO_VERSION 15 // last D1 version was 13
891 #define DEMO_GAME_TYPE 3 // 1 was shareware, 2 registered
893 #define DEMO_FILENAME DEMO_DIR "tmpdemo.dem"
895 #define DEMO_MAX_LEVELS 29
898 char nd_save_callsign[CALLSIGN_LEN+1];
899 int Newdemo_state = 0;
900 int Newdemo_vcr_state = 0;
901 int Newdemo_start_frame = -1;
902 unsigned int Newdemo_size;
903 int Newdemo_num_written;
904 int Newdemo_game_mode;
905 int Newdemo_old_cockpit;
906 byte Newdemo_no_space;
908 byte Newdemo_do_interpolate = 0; // 1
909 byte Newdemo_players_cloaked;
910 byte Newdemo_warning_given = 0;
911 byte Newdemo_cntrlcen_destroyed = 0;
912 static byte nd_bad_read;
913 int NewdemoFrameCount;
914 short frame_bytes_written = 0;
915 fix nd_playback_total;
916 fix nd_recorded_total;
917 fix nd_recorded_time;
919 byte First_time_playback=1;
920 fix JasonPlaybackTotal=0;
926 int newdemo_get_percent_done() {
927 if ( Newdemo_state == ND_STATE_PLAYBACK ) {
928 return (ftell(infile)*100)/Newdemo_size;
930 if ( Newdemo_state == ND_STATE_RECORDING ) {
931 return ftell(outfile);
936 #define VEL_PRECISION 12
938 void my_extract_shortpos(object *objp, shortpos *spp)
944 objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
945 objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
946 objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
948 objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
949 objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
950 objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
952 objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
953 objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
954 objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
956 segnum = spp->segment;
957 objp->segnum = segnum;
959 objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x;
960 objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y;
961 objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z;
963 objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION);
964 objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION);
965 objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION);
968 int newdemo_read( void *buffer, int elsize, int nelem )
971 num_read = fread( buffer,elsize,nelem, infile );
972 if (ferror(infile) || feof(infile))
978 int newdemo_find_object( int signature )
983 for (i=0; i<=Highest_object_index; i++, objp++ ) {
984 if ( (objp->type != OBJ_NONE) && (objp->signature == signature))
990 int newdemo_write( void *buffer, int elsize, int nelem )
992 int num_written, total_size;
994 total_size = elsize * nelem;
995 frame_bytes_written += total_size;
996 Newdemo_num_written += total_size;
997 Assert(outfile != NULL);
998 num_written = fwrite( buffer, elsize, nelem, outfile );
999 //if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) {
1000 // Newdemo_no_space=1;
1001 // newdemo_stop_recording();
1004 if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space)
1006 if (num_written == nelem && !Newdemo_no_space)
1010 newdemo_stop_recording();
1015 * The next bunch of files taken from Matt's gamesave.c. We have to modify
1016 * these since the demo must save more information about objects that
1020 static void nd_write_byte(byte b)
1022 newdemo_write(&b, 1, 1);
1025 static void nd_write_short(short s)
1027 newdemo_write(&s, 2, 1);
1030 static void nd_write_int(int i)
1032 newdemo_write(&i, 4, 1);
1035 static void nd_write_string(char *str)
1037 nd_write_byte(strlen(str) + 1);
1038 newdemo_write(str, strlen(str) + 1, 1);
1041 static void nd_write_fix(fix f)
1043 newdemo_write(&f, sizeof(fix), 1);
1046 static void nd_write_fixang(fixang f)
1048 newdemo_write(&f, sizeof(fixang), 1);
1051 static void nd_write_vector(vms_vector *v)
1058 static void nd_write_angvec(vms_angvec *v)
1060 nd_write_fixang(v->p);
1061 nd_write_fixang(v->b);
1062 nd_write_fixang(v->h);
1065 void nd_write_shortpos(object *obj)
1071 create_shortpos(&sp, obj, 0);
1073 render_type = obj->render_type;
1074 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
1075 for (i = 0; i < 9; i++)
1076 nd_write_byte(sp.bytemat[i]);
1077 for (i = 0; i < 9; i++) {
1078 if (sp.bytemat[i] != 0)
1082 Int3(); // contact Allender about this.
1086 nd_write_short(sp.xo);
1087 nd_write_short(sp.yo);
1088 nd_write_short(sp.zo);
1089 nd_write_short(sp.segment);
1090 nd_write_short(sp.velx);
1091 nd_write_short(sp.vely);
1092 nd_write_short(sp.velz);
1095 static void nd_read_byte(byte *b)
1097 newdemo_read(b, 1, 1);
1100 static void nd_read_short(short *s)
1102 newdemo_read(s, 2, 1);
1105 static void nd_read_int(int *i)
1107 newdemo_read(i, 4, 1);
1110 static void nd_read_string(char *str)
1115 newdemo_read(str, len, 1);
1118 static void nd_read_fix(fix *f)
1120 newdemo_read(f, sizeof(fix), 1);
1123 static void nd_read_fixang(fixang *f)
1125 newdemo_read(f, sizeof(fixang), 1);
1128 static void nd_read_vector(vms_vector *v)
1130 nd_read_fix(&(v->x));
1131 nd_read_fix(&(v->y));
1132 nd_read_fix(&(v->z));
1135 static void nd_read_angvec(vms_angvec *v)
1137 nd_read_fixang(&(v->p));
1138 nd_read_fixang(&(v->b));
1139 nd_read_fixang(&(v->h));
1142 static void nd_read_shortpos(object *obj)
1148 render_type = obj->render_type;
1149 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
1150 for (i = 0; i < 9; i++)
1151 nd_read_byte(&(sp.bytemat[i]));
1154 nd_read_short(&(sp.xo));
1155 nd_read_short(&(sp.yo));
1156 nd_read_short(&(sp.zo));
1157 nd_read_short(&(sp.segment));
1158 nd_read_short(&(sp.velx));
1159 nd_read_short(&(sp.vely));
1160 nd_read_short(&(sp.velz));
1162 my_extract_shortpos(obj, &sp);
1163 if ((obj->id == VCLIP_MORPHING_ROBOT) && (render_type == RT_FIREBALL) && (obj->control_type == CT_EXPLOSION))
1164 extract_orient_from_segment(&obj->orient,&Segments[obj->segnum]);
1168 object *prev_obj=NULL; //ptr to last object read in
1170 void nd_read_object(object *obj)
1172 memset(obj, 0, sizeof(object));
1175 * Do render type first, since with render_type == RT_NONE, we
1176 * blow by all other object information
1178 nd_read_byte(&(obj->render_type));
1179 nd_read_byte(&(obj->type));
1180 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
1183 nd_read_byte(&(obj->id));
1184 nd_read_byte(&(obj->flags));
1185 nd_read_short((short *)&(obj->signature));
1186 nd_read_shortpos(obj);
1188 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
1191 obj->attached_obj = -1;
1196 obj->control_type = CT_POWERUP;
1197 obj->movement_type = MT_NONE;
1198 obj->size = HOSTAGE_SIZE;
1202 obj->control_type = CT_AI;
1203 // (MarkA and MikeK said we should not do the crazy last secret stuff with multiple reactors...
1204 // This necessary code is our vindication. --MK, 2/15/96)
1205 if (obj->id != SPECIAL_REACTOR_ROBOT)
1206 obj->movement_type = MT_PHYSICS;
1208 obj->movement_type = MT_NONE;
1209 obj->size = Polygon_models[Robot_info[obj->id].model_num].rad;
1210 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
1211 obj->rtype.pobj_info.subobj_flags = 0;
1212 obj->ctype.ai_info.CLOAKED = (Robot_info[obj->id].cloak_type?1:0);
1216 obj->control_type = CT_POWERUP;
1217 nd_read_byte(&(obj->movement_type)); // might have physics movement
1218 obj->size = Powerup_info[obj->id].size;
1222 obj->control_type = CT_NONE;
1223 obj->movement_type = MT_PHYSICS;
1224 obj->size = Polygon_models[Player_ship->model_num].rad;
1225 obj->rtype.pobj_info.model_num = Player_ship->model_num;
1226 obj->rtype.pobj_info.subobj_flags = 0;
1230 obj->control_type = CT_NONE;
1231 obj->movement_type = MT_NONE;
1232 obj->size = Polygon_models[obj->id].rad;
1233 obj->rtype.pobj_info.model_num = obj->id;
1234 obj->rtype.pobj_info.subobj_flags = 0;
1238 nd_read_byte(&(obj->control_type));
1239 nd_read_byte(&(obj->movement_type));
1240 nd_read_fix(&(obj->size));
1245 nd_read_vector(&(obj->last_pos));
1246 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
1247 nd_read_fix(&(obj->lifeleft));
1252 obj->lifeleft = (fix)b;
1253 // MWA old way -- won't work with big endian machines nd_read_byte((ubyte *)&(obj->lifeleft));
1254 obj->lifeleft = (fix)((int)obj->lifeleft << 12);
1257 if (obj->type == OBJ_ROBOT) {
1258 if (Robot_info[obj->id].boss_flag) {
1261 nd_read_byte(&cloaked);
1262 obj->ctype.ai_info.CLOAKED = cloaked;
1266 switch (obj->movement_type) {
1269 nd_read_vector(&(obj->mtype.phys_info.velocity));
1270 nd_read_vector(&(obj->mtype.phys_info.thrust));
1274 nd_read_vector(&(obj->mtype.spin_rate));
1284 switch (obj->control_type) {
1288 nd_read_fix(&(obj->ctype.expl_info.spawn_time));
1289 nd_read_fix(&(obj->ctype.expl_info.delete_time));
1290 nd_read_short(&(obj->ctype.expl_info.delete_objnum));
1292 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
1294 if (obj->flags & OF_ATTACHED) { //attach to previous object
1295 Assert(prev_obj!=NULL);
1296 if (prev_obj->control_type == CT_EXPLOSION) {
1297 if (prev_obj->flags & OF_ATTACHED && prev_obj->ctype.expl_info.attach_parent!=-1)
1298 obj_attach(&Objects[prev_obj->ctype.expl_info.attach_parent],obj);
1300 obj->flags &= ~OF_ATTACHED;
1303 obj_attach(prev_obj,obj);
1309 nd_read_fix(&(obj->ctype.light_info.intensity));
1331 switch (obj->render_type) {
1340 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
1341 nd_read_int(&(obj->rtype.pobj_info.model_num));
1342 nd_read_int(&(obj->rtype.pobj_info.subobj_flags));
1345 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
1347 for (i=0;i<MAX_SUBMODELS;i++)
1348 nd_read_angvec(&(obj->pobj_info.anim_angles[i]));
1350 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
1351 nd_read_angvec(&obj->rtype.pobj_info.anim_angles[i]);
1356 obj->rtype.pobj_info.tmap_override = tmo;
1359 obj->rtype.pobj_info.tmap_override = -1;
1361 int xlated_tmo = tmap_xlate_table[tmo];
1362 if (xlated_tmo < 0) {
1363 //mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->pobj_info.model_num));
1367 obj->rtype.pobj_info.tmap_override = xlated_tmo;
1375 case RT_WEAPON_VCLIP:
1378 nd_read_int(&(obj->rtype.vclip_info.vclip_num));
1379 nd_read_fix(&(obj->rtype.vclip_info.frametime));
1380 nd_read_byte(&(obj->rtype.vclip_info.framenum));
1394 void nd_write_object(object *obj)
1398 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
1402 * Do render_type first so on read, we can make determination of
1403 * what else to read in
1405 nd_write_byte(obj->render_type);
1406 nd_write_byte(obj->type);
1407 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
1410 nd_write_byte(obj->id);
1411 nd_write_byte(obj->flags);
1412 nd_write_short((short)obj->signature);
1413 nd_write_shortpos(obj);
1415 if ((obj->type != OBJ_HOSTAGE) && (obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_POWERUP) && (obj->type != OBJ_CLUTTER)) {
1416 nd_write_byte(obj->control_type);
1417 nd_write_byte(obj->movement_type);
1418 nd_write_fix(obj->size);
1420 if (obj->type == OBJ_POWERUP)
1421 nd_write_byte(obj->movement_type);
1423 nd_write_vector(&obj->last_pos);
1425 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
1426 nd_write_fix(obj->lifeleft);
1428 life = (int)obj->lifeleft;
1432 nd_write_byte((ubyte)life);
1435 if (obj->type == OBJ_ROBOT) {
1436 if (Robot_info[obj->id].boss_flag) {
1437 if ((GameTime > Boss_cloak_start_time) && (GameTime < Boss_cloak_end_time))
1444 switch (obj->movement_type) {
1447 nd_write_vector(&obj->mtype.phys_info.velocity);
1448 nd_write_vector(&obj->mtype.phys_info.thrust);
1452 nd_write_vector(&obj->mtype.spin_rate);
1462 switch (obj->control_type) {
1468 nd_write_fix(obj->ctype.expl_info.spawn_time);
1469 nd_write_fix(obj->ctype.expl_info.delete_time);
1470 nd_write_short(obj->ctype.expl_info.delete_objnum);
1478 nd_write_fix(obj->ctype.light_info.intensity);
1485 case CT_SLEW: //the player is generally saved as slew
1498 switch (obj->render_type) {
1507 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
1508 nd_write_int(obj->rtype.pobj_info.model_num);
1509 nd_write_int(obj->rtype.pobj_info.subobj_flags);
1512 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
1514 for (i=0;i<MAX_SUBMODELS;i++)
1515 nd_write_angvec(&obj->pobj_info.anim_angles[i]);
1517 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
1518 nd_write_angvec(&obj->rtype.pobj_info.anim_angles[i]);
1520 nd_write_int(obj->rtype.pobj_info.tmap_override);
1526 case RT_WEAPON_VCLIP:
1529 nd_write_int(obj->rtype.vclip_info.vclip_num);
1530 nd_write_fix(obj->rtype.vclip_info.frametime);
1531 nd_write_byte(obj->rtype.vclip_info.framenum);
1544 int JustStartedRecording=0,JustStartedPlayback=0;
1546 void newdemo_record_start_demo()
1551 nd_write_byte(ND_EVENT_START_DEMO);
1552 nd_write_byte(DEMO_VERSION);
1553 nd_write_byte(DEMO_GAME_TYPE);
1554 nd_write_fix(GameTime);
1557 if (Game_mode & GM_MULTI)
1558 nd_write_int(Game_mode | (Player_num << 16));
1561 // NOTE LINK TO ABOVE!!!
1562 nd_write_int(Game_mode);
1565 if (Game_mode & GM_TEAM) {
1566 nd_write_byte(Netgame.team_vector);
1567 nd_write_string(Netgame.team_name[0]);
1568 nd_write_string(Netgame.team_name[1]);
1571 if (Game_mode & GM_MULTI) {
1572 nd_write_byte((byte)N_players);
1573 for (i = 0; i < N_players; i++) {
1574 nd_write_string(Players[i].callsign);
1575 nd_write_byte(Players[i].connected);
1577 if (Game_mode & GM_MULTI_COOP) {
1578 nd_write_int(Players[i].score);
1580 nd_write_short((short)Players[i].net_killed_total);
1581 nd_write_short((short)Players[i].net_kills_total);
1586 // NOTE LINK TO ABOVE!!!
1587 nd_write_int(Players[Player_num].score);
1589 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
1590 nd_write_short((short)Players[Player_num].primary_ammo[i]);
1592 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
1593 nd_write_short((short)Players[Player_num].secondary_ammo[i]);
1595 nd_write_byte((byte)Players[Player_num].laser_level);
1597 // Support for missions added here
1599 nd_write_string(Current_mission_filename);
1601 nd_write_byte((byte)(f2ir(Players[Player_num].energy)));
1602 nd_write_byte((byte)(f2ir(Players[Player_num].shields)));
1603 nd_write_int(Players[Player_num].flags); // be sure players flags are set
1604 nd_write_byte((byte)Primary_weapon);
1605 nd_write_byte((byte)Secondary_weapon);
1606 Newdemo_start_frame = FrameCount;
1607 JustStartedRecording=1;
1609 newdemo_set_new_level(Current_level_num);
1614 void newdemo_record_start_frame(int frame_number, fix frame_time )
1618 if (Newdemo_no_space) {
1619 newdemo_stop_playback();
1625 for (i=0;i<MAX_OBJECTS;i++)
1628 ViewWasRecorded[i]=0;
1631 RenderingWasRecorded[i]=0;
1633 frame_number -= Newdemo_start_frame;
1635 Assert(frame_number >= 0 );
1637 nd_write_byte(ND_EVENT_START_FRAME);
1638 nd_write_short(frame_bytes_written - 1); // from previous frame
1639 frame_bytes_written=3;
1640 nd_write_int(frame_number);
1641 nd_write_int(frame_time);
1646 void newdemo_record_render_object(object * obj)
1648 if (ViewWasRecorded[obj-Objects])
1651 //if (obj==&Objects[Players[Player_num].objnum] && !Player_is_dead)
1655 nd_write_byte(ND_EVENT_RENDER_OBJECT);
1656 nd_write_object(obj);
1660 extern ubyte RenderingType;
1662 void newdemo_record_viewer_object(object * obj)
1665 if (ViewWasRecorded[obj-Objects] && (ViewWasRecorded[obj-Objects]-1)==RenderingType)
1667 //if (WasRecorded[obj-Objects])
1669 if (RenderingWasRecorded[RenderingType])
1672 ViewWasRecorded[obj-Objects]=RenderingType+1;
1673 RenderingWasRecorded[RenderingType]=1;
1675 nd_write_byte(ND_EVENT_VIEWER_OBJECT);
1676 nd_write_byte(RenderingType);
1677 nd_write_object(obj);
1681 void newdemo_record_sound( int soundno )
1684 nd_write_byte(ND_EVENT_SOUND);
1685 nd_write_int( soundno );
1689 //--unused-- void newdemo_record_sound_once( int soundno ) {
1690 //--unused-- stop_time();
1691 //--unused-- nd_write_byte( ND_EVENT_SOUND_ONCE );
1692 //--unused-- nd_write_int( soundno );
1693 //--unused-- start_time();
1697 void newdemo_record_cockpit_change (int mode)
1700 nd_write_byte (ND_EVENT_CHANGE_COCKPIT);
1706 void newdemo_record_sound_3d( int soundno, int angle, int volume )
1709 nd_write_byte( ND_EVENT_SOUND_3D );
1710 nd_write_int( soundno );
1711 nd_write_int( angle );
1712 nd_write_int( volume );
1716 void newdemo_record_sound_3d_once( int soundno, int angle, int volume )
1719 nd_write_byte( ND_EVENT_SOUND_3D_ONCE );
1720 nd_write_int( soundno );
1721 nd_write_int( angle );
1722 nd_write_int( volume );
1727 void newdemo_record_link_sound_to_object3( int soundno, short objnum, fix max_volume, fix max_distance, int loop_start, int loop_end )
1730 nd_write_byte( ND_EVENT_LINK_SOUND_TO_OBJ );
1731 nd_write_int( soundno );
1732 nd_write_int( Objects[objnum].signature );
1733 nd_write_int( max_volume );
1734 nd_write_int( max_distance );
1735 nd_write_int( loop_start );
1736 nd_write_int( loop_end );
1740 void newdemo_record_kill_sound_linked_to_object( int objnum )
1743 nd_write_byte( ND_EVENT_KILL_SOUND_TO_OBJ );
1744 nd_write_int( Objects[objnum].signature );
1749 void newdemo_record_wall_hit_process( int segnum, int side, int damage, int playernum )
1755 //playernum = playernum;
1756 nd_write_byte( ND_EVENT_WALL_HIT_PROCESS );
1757 nd_write_int( segnum );
1758 nd_write_int( side );
1759 nd_write_int( damage );
1760 nd_write_int( playernum );
1764 void newdemo_record_guided_start ()
1766 nd_write_byte (ND_EVENT_START_GUIDED);
1769 void newdemo_record_guided_end ()
1771 nd_write_byte (ND_EVENT_END_GUIDED);
1774 void newdemo_record_secret_exit_blown(int truth)
1777 nd_write_byte( ND_EVENT_SECRET_THINGY );
1778 nd_write_int( truth );
1782 void newdemo_record_trigger( int segnum, int side, int objnum,int shot )
1785 nd_write_byte( ND_EVENT_TRIGGER );
1786 nd_write_int( segnum );
1787 nd_write_int( side );
1788 nd_write_int( objnum );
1793 void newdemo_record_hostage_rescued( int hostage_number ) {
1795 nd_write_byte( ND_EVENT_HOSTAGE_RESCUED );
1796 nd_write_int( hostage_number );
1800 void newdemo_record_morph_frame(morph_data *md)
1804 nd_write_byte( ND_EVENT_MORPH_FRAME );
1806 newdemo_write( md->morph_vecs, sizeof(md->morph_vecs), 1 );
1807 newdemo_write( md->submodel_active, sizeof(md->submodel_active), 1 );
1808 newdemo_write( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 );
1810 nd_write_object( md->obj );
1814 void newdemo_record_wall_toggle( int segnum, int side )
1817 nd_write_byte( ND_EVENT_WALL_TOGGLE );
1818 nd_write_int( segnum );
1819 nd_write_int( side );
1823 void newdemo_record_control_center_destroyed()
1826 nd_write_byte( ND_EVENT_CONTROL_CENTER_DESTROYED );
1827 nd_write_int( Countdown_seconds_left );
1831 void newdemo_record_hud_message( char * message )
1834 nd_write_byte( ND_EVENT_HUD_MESSAGE );
1835 nd_write_string(message);
1839 void newdemo_record_palette_effect(short r, short g, short b )
1842 nd_write_byte( ND_EVENT_PALETTE_EFFECT );
1843 nd_write_short( r );
1844 nd_write_short( g );
1845 nd_write_short( b );
1849 void newdemo_record_player_energy(int old_energy, int energy)
1852 nd_write_byte( ND_EVENT_PLAYER_ENERGY );
1853 nd_write_byte((byte) old_energy);
1854 nd_write_byte((byte) energy);
1858 void newdemo_record_player_afterburner(fix old_afterburner, fix afterburner)
1861 nd_write_byte( ND_EVENT_PLAYER_AFTERBURNER );
1862 nd_write_byte((byte) (old_afterburner>>9));
1863 nd_write_byte((byte) (afterburner>>9));
1867 void newdemo_record_player_shields(int old_shield, int shield)
1870 nd_write_byte( ND_EVENT_PLAYER_SHIELD );
1871 nd_write_byte((byte)old_shield);
1872 nd_write_byte((byte)shield);
1876 void newdemo_record_player_flags(uint oflags, uint flags)
1879 nd_write_byte( ND_EVENT_PLAYER_FLAGS );
1880 nd_write_int(((short)oflags << 16) | (short)flags);
1884 void newdemo_record_player_weapon(int weapon_type, int weapon_num)
1887 nd_write_byte( ND_EVENT_PLAYER_WEAPON );
1888 nd_write_byte((byte)weapon_type);
1889 nd_write_byte((byte)weapon_num);
1891 nd_write_byte((byte)Secondary_weapon);
1893 nd_write_byte((byte)Primary_weapon);
1897 void newdemo_record_effect_blowup(short segment, int side, vms_vector *pnt)
1900 nd_write_byte (ND_EVENT_EFFECT_BLOWUP);
1901 nd_write_short(segment);
1902 nd_write_byte((byte)side);
1903 nd_write_vector(pnt);
1907 void newdemo_record_homing_distance(fix distance)
1910 nd_write_byte(ND_EVENT_HOMING_DISTANCE);
1911 nd_write_short((short)(distance>>16));
1915 void newdemo_record_letterbox(void)
1918 nd_write_byte(ND_EVENT_LETTERBOX);
1922 void newdemo_record_rearview(void)
1925 nd_write_byte(ND_EVENT_REARVIEW);
1929 void newdemo_record_restore_cockpit(void)
1932 nd_write_byte(ND_EVENT_RESTORE_COCKPIT);
1936 void newdemo_record_restore_rearview(void)
1939 nd_write_byte(ND_EVENT_RESTORE_REARVIEW);
1943 void newdemo_record_wall_set_tmap_num1(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1946 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM1);
1947 nd_write_short(seg);
1948 nd_write_byte(side);
1949 nd_write_short(cseg);
1950 nd_write_byte(cside);
1951 nd_write_short(tmap);
1955 void newdemo_record_wall_set_tmap_num2(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1958 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM2);
1959 nd_write_short(seg);
1960 nd_write_byte(side);
1961 nd_write_short(cseg);
1962 nd_write_byte(cside);
1963 nd_write_short(tmap);
1967 void newdemo_record_multi_cloak(int pnum)
1970 nd_write_byte(ND_EVENT_MULTI_CLOAK);
1971 nd_write_byte((byte)pnum);
1975 void newdemo_record_multi_decloak(int pnum)
1978 nd_write_byte(ND_EVENT_MULTI_DECLOAK);
1979 nd_write_byte((byte)pnum);
1983 void newdemo_record_multi_death(int pnum)
1986 nd_write_byte(ND_EVENT_MULTI_DEATH);
1987 nd_write_byte((byte)pnum);
1991 void newdemo_record_multi_kill(int pnum, byte kill)
1994 nd_write_byte(ND_EVENT_MULTI_KILL);
1995 nd_write_byte((byte)pnum);
1996 nd_write_byte(kill);
2000 void newdemo_record_multi_connect(int pnum, int new_player, char *new_callsign)
2003 nd_write_byte(ND_EVENT_MULTI_CONNECT);
2004 nd_write_byte((byte)pnum);
2005 nd_write_byte((byte)new_player);
2007 nd_write_string(Players[pnum].callsign);
2008 nd_write_int(Players[pnum].net_killed_total);
2009 nd_write_int(Players[pnum].net_kills_total);
2011 nd_write_string(new_callsign);
2015 void newdemo_record_multi_reconnect(int pnum)
2018 nd_write_byte(ND_EVENT_MULTI_RECONNECT);
2019 nd_write_byte((byte)pnum);
2023 void newdemo_record_multi_disconnect(int pnum)
2026 nd_write_byte(ND_EVENT_MULTI_DISCONNECT);
2027 nd_write_byte((byte)pnum);
2031 void newdemo_record_player_score(int score)
2034 nd_write_byte(ND_EVENT_PLAYER_SCORE);
2035 nd_write_int(score);
2039 void newdemo_record_multi_score(int pnum, int score)
2042 nd_write_byte(ND_EVENT_MULTI_SCORE);
2043 nd_write_byte((byte)pnum);
2044 nd_write_int(score - Players[pnum].score); // called before score is changed!!!!
2048 void newdemo_record_primary_ammo(int old_ammo, int new_ammo)
2051 nd_write_byte(ND_EVENT_PRIMARY_AMMO);
2053 nd_write_short((short)new_ammo);
2055 nd_write_short((short)old_ammo);
2056 nd_write_short((short)new_ammo);
2060 void newdemo_record_secondary_ammo(int old_ammo, int new_ammo)
2063 nd_write_byte(ND_EVENT_SECONDARY_AMMO);
2065 nd_write_short((short)new_ammo);
2067 nd_write_short((short)old_ammo);
2068 nd_write_short((short)new_ammo);
2072 void newdemo_record_door_opening(int segnum, int side)
2075 nd_write_byte(ND_EVENT_DOOR_OPENING);
2076 nd_write_short((short)segnum);
2077 nd_write_byte((byte)side);
2081 void newdemo_record_laser_level(byte old_level, byte new_level)
2084 nd_write_byte(ND_EVENT_LASER_LEVEL);
2085 nd_write_byte(old_level);
2086 nd_write_byte(new_level);
2090 void newdemo_record_cloaking_wall(int front_wall_num, int back_wall_num, ubyte type, ubyte state, fix cloak_value, fix l0, fix l1, fix l2, fix l3)
2092 Assert(front_wall_num <= 255 && back_wall_num <= 255);
2095 nd_write_byte(ND_EVENT_CLOAKING_WALL);
2096 nd_write_byte(front_wall_num);
2097 nd_write_byte(back_wall_num);
2098 nd_write_byte(type);
2099 nd_write_byte(state);
2100 nd_write_byte(cloak_value);
2101 nd_write_short(l0>>8);
2102 nd_write_short(l1>>8);
2103 nd_write_short(l2>>8);
2104 nd_write_short(l3>>8);
2108 void newdemo_set_new_level(int level_num)
2115 nd_write_byte(ND_EVENT_NEW_LEVEL);
2116 nd_write_byte((byte)level_num);
2117 nd_write_byte((byte)Current_level_num);
2119 if (JustStartedRecording==1)
2121 nd_write_int(Num_walls);
2122 for (i=0;i<Num_walls;i++)
2124 nd_write_byte (Walls[i].type);
2125 nd_write_byte (Walls[i].flags);
2126 nd_write_byte (Walls[i].state);
2128 seg = &Segments[Walls[i].segnum];
2129 side = Walls[i].sidenum;
2130 nd_write_short (seg->sides[side].tmap_num);
2131 nd_write_short (seg->sides[side].tmap_num2);
2132 JustStartedRecording=0;
2139 int newdemo_read_demo_start(int rnd_demo)
2141 byte i, version, game_type, laser_level;
2142 char c, energy, shield;
2143 char text[50], current_mission[9];
2146 if ((c != ND_EVENT_START_DEMO) || nd_bad_read) {
2149 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_CORRUPT);
2150 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2151 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2154 nd_read_byte(&version);
2155 nd_read_byte(&game_type);
2156 if (game_type < DEMO_GAME_TYPE) {
2159 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
2160 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2161 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Descent: First Strike";
2163 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2166 if (game_type != DEMO_GAME_TYPE) {
2169 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
2170 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2171 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Unknown Descent version";
2173 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2176 if (version < DEMO_VERSION) {
2179 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_OLD);
2180 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2181 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2185 nd_read_fix(&GameTime);
2186 Boss_cloak_start_time=Boss_cloak_end_time=GameTime;
2187 JasonPlaybackTotal=0;
2189 nd_read_int(&Newdemo_game_mode);
2192 change_playernum_to((Newdemo_game_mode >> 16) & 0x7);
2193 if (Newdemo_game_mode & GM_TEAM) {
2194 nd_read_byte(&(Netgame.team_vector));
2195 nd_read_string(Netgame.team_name[0]);
2196 nd_read_string(Netgame.team_name[1]);
2198 if (Newdemo_game_mode & GM_MULTI) {
2203 // changed this to above two lines -- breaks on the mac because of
2205 // nd_read_byte((byte *)&N_players);
2206 for (i = 0 ; i < N_players; i++) {
2207 Players[i].cloak_time = 0;
2208 Players[i].invulnerable_time = 0;
2209 nd_read_string(Players[i].callsign);
2210 nd_read_byte(&(Players[i].connected));
2212 if (Newdemo_game_mode & GM_MULTI_COOP) {
2213 nd_read_int(&(Players[i].score));
2215 nd_read_short((short *)&(Players[i].net_killed_total));
2216 nd_read_short((short *)&(Players[i].net_kills_total));
2219 Game_mode = Newdemo_game_mode;
2220 multi_sort_kill_list();
2221 Game_mode = GM_NORMAL;
2224 nd_read_int(&(Players[Player_num].score)); // Note link to above if!
2226 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
2227 nd_read_short((short*)&(Players[Player_num].primary_ammo[i]));
2229 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
2230 nd_read_short((short*)&(Players[Player_num].secondary_ammo[i]));
2232 nd_read_byte(&laser_level);
2233 if (laser_level != Players[Player_num].laser_level) {
2234 Players[Player_num].laser_level = laser_level;
2235 update_laser_weapon_info();
2238 // Support for missions
2240 nd_read_string(current_mission);
2241 if (!load_mission_by_name(current_mission)) {
2245 sprintf(text, TXT_NOMISSION4DEMO, current_mission);
2246 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2247 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2252 nd_recorded_total = 0;
2253 nd_playback_total = 0;
2254 nd_read_byte(&energy);
2255 nd_read_byte(&shield);
2257 nd_read_int((int *)&(Players[Player_num].flags));
2258 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
2259 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2260 Newdemo_players_cloaked |= (1 << Player_num);
2262 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2263 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2265 nd_read_byte((byte *)&Primary_weapon);
2266 nd_read_byte((byte *)&Secondary_weapon);
2268 // Next bit of code to fix problem that I introduced between 1.0 and 1.1
2269 // check the next byte -- it _will_ be a load_new_level event. If it is
2270 // not, then we must shift all bytes up by one.
2272 Players[Player_num].energy = i2f(energy);
2273 Players[Player_num].shields = i2f(shield);
2274 JustStartedPlayback=1;
2278 void newdemo_pop_ctrlcen_triggers()
2282 segment *seg, *csegp;
2284 for (i = 0; i < ControlCenterTriggers.num_links; i++) {
2285 seg = &Segments[ControlCenterTriggers.seg[i]];
2286 side = ControlCenterTriggers.side[i];
2287 csegp = &Segments[seg->children[side]];
2288 cside = find_connect_side(seg, csegp);
2289 anim_num = Walls[seg->sides[side].wall_num].clip_num;
2290 n = WallAnims[anim_num].num_frames;
2291 if (WallAnims[anim_num].flags & WCF_TMAP1) {
2292 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[n-1];
2294 seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[n-1];
2299 #define N_PLAYER_SHIP_TEXTURES 6
2301 void nd_render_extras (ubyte,object *);
2302 extern void multi_apply_goal_textures ();
2303 ubyte Newdemo_flying_guided=0;
2305 int newdemo_read_frame_information()
2307 int done, segnum, side, objnum, soundno, angle, volume, i,shot;
2309 ubyte c,WhichWindow;
2310 static byte saved_letter_cockpit;
2311 static byte saved_rearview_cockpit;
2313 static char LastReadValue=101;
2318 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2319 for (segnum=0; segnum <= Highest_segment_index; segnum++)
2320 Segments[segnum].objects = -1;
2323 Players[Player_num].homing_object_dist = -F1_0;
2329 if (nd_bad_read) { done = -1; break; }
2333 case ND_EVENT_START_FRAME: { // Followed by an integer frame number, then a fix FrameTime
2334 short last_frame_length;
2337 nd_read_short(&last_frame_length);
2338 nd_read_int(&NewdemoFrameCount);
2339 nd_read_int((int *)&nd_recorded_time);
2340 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2341 nd_recorded_total += nd_recorded_time;
2342 NewdemoFrameCount--;
2344 if (nd_bad_read) { done = -1; break; }
2348 case ND_EVENT_VIEWER_OBJECT: // Followed by an object structure
2349 nd_read_byte (&WhichWindow);
2352 //mprintf ((0,"Reading extra!\n"));
2353 nd_read_object (&extraobj);
2354 if (Newdemo_vcr_state!=ND_STATE_PAUSED)
2356 if (nd_bad_read) { done = -1; break; }
2358 nd_render_extras (WhichWindow,&extraobj);
2363 //mprintf ((0,"Reading viewer!\n"));
2364 //Viewer=&Objects[0];
2365 nd_read_object(Viewer);
2367 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2368 if (nd_bad_read) { done = -1; break; }
2369 segnum = Viewer->segnum;
2370 Viewer->next = Viewer->prev = Viewer->segnum = -1;
2372 // HACK HACK HACK -- since we have multiple level recording, it can be the case
2373 // HACK HACK HACK -- that when rewinding the demo, the viewer is in a segment
2374 // HACK HACK HACK -- that is greater than the highest index of segments. Bash
2375 // HACK HACK HACK -- the viewer to segment 0 for bogus view.
2377 if (segnum > Highest_segment_index)
2379 obj_link(Viewer-Objects,segnum);
2384 case ND_EVENT_RENDER_OBJECT: // Followed by an object structure
2385 objnum = obj_allocate();
2388 obj = &Objects[objnum];
2389 nd_read_object(obj);
2390 if (nd_bad_read) { done = -1; break; }
2391 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2392 segnum = obj->segnum;
2393 obj->next = obj->prev = obj->segnum = -1;
2395 // HACK HACK HACK -- don't render objects is segments greater than Highest_segment_index
2396 // HACK HACK HACK -- (see above)
2398 if (segnum > Highest_segment_index)
2401 obj_link(obj-Objects,segnum);
2403 if ((obj->type == OBJ_PLAYER) && (Newdemo_game_mode & GM_MULTI)) {
2406 if (Newdemo_game_mode & GM_TEAM)
2407 player = get_team(obj->id);
2414 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
2415 multi_player_textures[player][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[obj->rtype.pobj_info.model_num].first_texture+i]];
2417 multi_player_textures[player][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2]];
2418 multi_player_textures[player][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2+1]];
2419 obj->rtype.pobj_info.alt_textures = player+1;
2425 case ND_EVENT_SOUND:
2426 nd_read_int(&soundno);
2427 if (nd_bad_read) {done = -1; break; }
2428 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2429 digi_play_sample( soundno, F1_0 );
2432 //--unused case ND_EVENT_SOUND_ONCE:
2433 //--unused nd_read_int(&soundno);
2434 //--unused if (nd_bad_read) { done = -1; break; }
2435 //--unused if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2436 //--unused digi_play_sample_once( soundno, F1_0 );
2439 case ND_EVENT_SOUND_3D:
2440 nd_read_int(&soundno);
2441 nd_read_int(&angle);
2442 nd_read_int(&volume);
2443 if (nd_bad_read) { done = -1; break; }
2444 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2445 digi_play_sample_3d( soundno, angle, volume, 0 );
2448 case ND_EVENT_SOUND_3D_ONCE:
2449 nd_read_int(&soundno);
2450 nd_read_int(&angle);
2451 nd_read_int(&volume);
2452 if (nd_bad_read) { done = -1; break; }
2453 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2454 digi_play_sample_3d( soundno, angle, volume, 1 );
2457 case ND_EVENT_LINK_SOUND_TO_OBJ:
2459 int soundno, objnum, max_volume, max_distance, loop_start, loop_end;
2461 nd_read_int( &soundno );
2462 nd_read_int( &signature );
2463 nd_read_int( &max_volume );
2464 nd_read_int( &max_distance );
2465 nd_read_int( &loop_start );
2466 nd_read_int( &loop_end );
2467 objnum = newdemo_find_object( signature );
2468 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
2469 digi_link_sound_to_object3( soundno, objnum, 1, max_volume, max_distance, loop_start, loop_end );
2474 case ND_EVENT_KILL_SOUND_TO_OBJ:
2476 int objnum, signature;
2477 nd_read_int( &signature );
2478 objnum = newdemo_find_object( signature );
2479 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
2480 digi_kill_sound_linked_to_object(objnum);
2485 case ND_EVENT_WALL_HIT_PROCESS: {
2489 nd_read_int(&segnum);
2491 nd_read_fix(&damage);
2492 nd_read_int(&player);
2493 if (nd_bad_read) { done = -1; break; }
2494 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2495 wall_hit_process(&Segments[segnum], side, damage, player, &(Objects[0]) );
2499 case ND_EVENT_TRIGGER:
2500 nd_read_int(&segnum);
2502 nd_read_int(&objnum);
2504 if (nd_bad_read) { done = -1; break; }
2505 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2507 mprintf ((0,"EVENT TRIGGER! shot=%d\n",shot));
2509 if (Triggers[Walls[Segments[segnum].sides[side].wall_num].trigger].type == TT_SECRET_EXIT) {
2513 Assert(c == ND_EVENT_SECRET_THINGY);
2514 nd_read_int(&truth);
2516 check_trigger(&Segments[segnum], side, objnum,shot);
2518 check_trigger(&Segments[segnum], side, objnum,shot);
2522 case ND_EVENT_HOSTAGE_RESCUED: {
2525 nd_read_int(&hostage_number);
2526 if (nd_bad_read) { done = -1; break; }
2527 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2528 hostage_rescue( hostage_number );
2532 case ND_EVENT_MORPH_FRAME: {
2536 md = &morph_objects[0];
2537 if (newdemo_read( md->morph_vecs, sizeof(md->morph_vecs), 1 )!=1) { done=-1; break; }
2538 if (newdemo_read( md->submodel_active, sizeof(md->submodel_active), 1 )!=1) { done=-1; break; }
2539 if (newdemo_read( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 )!=1) { done=-1; break; }
2541 objnum = obj_allocate();
2544 obj = &Objects[objnum];
2545 nd_read_object(obj);
2546 obj->render_type = RT_POLYOBJ;
2547 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2548 if (nd_bad_read) { done = -1; break; }
2549 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2550 segnum = obj->segnum;
2551 obj->next = obj->prev = obj->segnum = -1;
2552 obj_link(obj-Objects,segnum);
2558 case ND_EVENT_WALL_TOGGLE:
2559 nd_read_int(&segnum);
2561 if (nd_bad_read) {done = -1; break; }
2562 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2563 wall_toggle(&Segments[segnum], side);
2566 case ND_EVENT_CONTROL_CENTER_DESTROYED:
2567 nd_read_int(&Countdown_seconds_left);
2568 Control_center_destroyed = 1;
2569 if (nd_bad_read) { done = -1; break; }
2570 if (!Newdemo_cntrlcen_destroyed) {
2571 newdemo_pop_ctrlcen_triggers();
2572 Newdemo_cntrlcen_destroyed = 1;
2573 //do_controlcen_destroyed_stuff(NULL);
2577 case ND_EVENT_HUD_MESSAGE: {
2580 nd_read_string(&(hud_msg[0]));
2581 if (nd_bad_read) { done = -1; break; }
2582 HUD_init_message( hud_msg );
2585 case ND_EVENT_START_GUIDED:
2586 Newdemo_flying_guided=1;
2587 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2588 Newdemo_flying_guided=0;
2590 case ND_EVENT_END_GUIDED:
2591 Newdemo_flying_guided=0;
2592 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2593 Newdemo_flying_guided=1;
2596 case ND_EVENT_PALETTE_EFFECT: {
2602 if (nd_bad_read) { done = -1; break; }
2603 PALETTE_FLASH_SET(r,g,b);
2607 case ND_EVENT_PLAYER_ENERGY: {
2611 nd_read_byte(&old_energy);
2612 nd_read_byte(&energy);
2613 if (nd_bad_read) {done = -1; break; }
2614 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2615 Players[Player_num].energy = i2f(energy);
2616 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2617 if (old_energy != 255)
2618 Players[Player_num].energy = i2f(old_energy);
2623 case ND_EVENT_PLAYER_AFTERBURNER: {
2625 ubyte old_afterburner;
2627 nd_read_byte(&old_afterburner);
2628 nd_read_byte(&afterburner);
2629 if (nd_bad_read) {done = -1; break; }
2630 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2631 Afterburner_charge = afterburner<<9;
2632 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2633 if (old_afterburner != 255)
2634 Afterburner_charge = old_afterburner<<9;
2639 case ND_EVENT_PLAYER_SHIELD: {
2643 nd_read_byte(&old_shield);
2644 nd_read_byte(&shield);
2645 if (nd_bad_read) {done = -1; break; }
2646 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2647 Players[Player_num].shields = i2f(shield);
2648 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2649 if (old_shield != 255)
2650 Players[Player_num].shields = i2f(old_shield);
2655 case ND_EVENT_PLAYER_FLAGS: {
2658 nd_read_int((int *)&(Players[Player_num].flags));
2659 if (nd_bad_read) {done = -1; break; }
2661 oflags = Players[Player_num].flags >> 16;
2662 Players[Player_num].flags &= 0xffff;
2664 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || ((Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) && (oflags != 0xffff)) ) {
2665 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2666 Players[Player_num].cloak_time = 0;
2667 Newdemo_players_cloaked &= ~(1 << Player_num);
2669 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2670 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2671 Newdemo_players_cloaked |= (1 << Player_num);
2673 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2674 Players[Player_num].invulnerable_time = 0;
2675 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2676 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2677 Players[Player_num].flags = oflags;
2678 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2679 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2680 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2681 Newdemo_players_cloaked |= (1 << Player_num);
2683 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2684 Players[Player_num].cloak_time = 0;
2685 Newdemo_players_cloaked &= ~(1 << Player_num);
2687 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2688 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2689 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2690 Players[Player_num].invulnerable_time = 0;
2692 update_laser_weapon_info(); // in case of quad laser change
2696 case ND_EVENT_PLAYER_WEAPON: {
2697 byte weapon_type, weapon_num;
2700 nd_read_byte(&weapon_type);
2701 nd_read_byte(&weapon_num);
2702 nd_read_byte(&old_weapon);
2703 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2704 if (weapon_type == 0)
2705 Primary_weapon = (int)weapon_num;
2707 Secondary_weapon = (int)weapon_num;
2708 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2709 if (weapon_type == 0)
2710 Primary_weapon = (int)old_weapon;
2712 Secondary_weapon = (int)old_weapon;
2717 case ND_EVENT_EFFECT_BLOWUP: {
2723 //create a dummy object which will be the weapon that hits
2724 //the monitor. the blowup code wants to know who the parent of the
2725 //laser is, so create a laser whose parent is the player
2726 dummy.ctype.laser_info.parent_type = OBJ_PLAYER;
2728 nd_read_short(&segnum);
2729 nd_read_byte(&side);
2730 nd_read_vector(&pnt);
2731 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2732 check_effect_blowup(&(Segments[segnum]), side, &pnt, &dummy, 0);
2736 case ND_EVENT_HOMING_DISTANCE: {
2739 nd_read_short(&distance);
2740 Players[Player_num].homing_object_dist = i2f((int)(distance << 16));
2744 case ND_EVENT_LETTERBOX:
2745 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2746 saved_letter_cockpit = Cockpit_mode;
2747 select_cockpit(CM_LETTERBOX);
2748 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2749 select_cockpit(saved_letter_cockpit);
2752 case ND_EVENT_CHANGE_COCKPIT: {
2755 nd_read_int (&dummy);
2756 select_cockpit (dummy);
2760 case ND_EVENT_REARVIEW:
2761 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2762 saved_rearview_cockpit = Cockpit_mode;
2763 if (Cockpit_mode == CM_FULL_COCKPIT)
2764 select_cockpit(CM_REAR_VIEW);
2766 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2767 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2768 saved_rearview_cockpit = CM_FULL_COCKPIT;
2769 select_cockpit(saved_rearview_cockpit);
2774 case ND_EVENT_RESTORE_COCKPIT:
2775 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2776 saved_letter_cockpit = Cockpit_mode;
2777 select_cockpit(CM_LETTERBOX);
2778 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2779 select_cockpit(saved_letter_cockpit);
2783 case ND_EVENT_RESTORE_REARVIEW:
2784 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2785 saved_rearview_cockpit = Cockpit_mode;
2786 if (Cockpit_mode == CM_FULL_COCKPIT)
2787 select_cockpit(CM_REAR_VIEW);
2789 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2790 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2791 saved_rearview_cockpit = CM_FULL_COCKPIT;
2792 select_cockpit(saved_rearview_cockpit);
2798 case ND_EVENT_WALL_SET_TMAP_NUM1: {
2799 short seg, cseg, tmap;
2802 nd_read_short(&seg);
2803 nd_read_byte(&side);
2804 nd_read_short(&cseg);
2805 nd_read_byte(&cside);
2806 nd_read_short( &tmap );
2807 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD))
2808 Segments[seg].sides[side].tmap_num = Segments[cseg].sides[cside].tmap_num = tmap;
2812 case ND_EVENT_WALL_SET_TMAP_NUM2: {
2813 short seg, cseg, tmap;
2816 nd_read_short(&seg);
2817 nd_read_byte(&side);
2818 nd_read_short(&cseg);
2819 nd_read_byte(&cside);
2820 nd_read_short( &tmap );
2821 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) {
2822 Assert(tmap!=0 && Segments[seg].sides[side].tmap_num2!=0);
2823 Segments[seg].sides[side].tmap_num2 = Segments[cseg].sides[cside].tmap_num2 = tmap;
2828 case ND_EVENT_MULTI_CLOAK: {
2831 nd_read_byte(&pnum);
2832 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2833 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2834 Players[pnum].cloak_time = 0;
2835 Newdemo_players_cloaked &= ~(1 << pnum);
2836 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2837 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2838 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2839 Newdemo_players_cloaked |= (1 << pnum);
2844 case ND_EVENT_MULTI_DECLOAK: {
2847 nd_read_byte(&pnum);
2849 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2850 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2851 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2852 Newdemo_players_cloaked |= (1 << pnum);
2853 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2854 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2855 Players[pnum].cloak_time = 0;
2856 Newdemo_players_cloaked &= ~(1 << pnum);
2861 case ND_EVENT_MULTI_DEATH: {
2864 nd_read_byte(&pnum);
2865 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2866 Players[pnum].net_killed_total--;
2867 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2868 Players[pnum].net_killed_total++;
2873 case ND_EVENT_MULTI_KILL: {
2876 nd_read_byte(&pnum);
2877 nd_read_byte(&kill);
2878 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2879 Players[pnum].net_kills_total -= kill;
2880 if (Newdemo_game_mode & GM_TEAM)
2881 team_kills[get_team(pnum)] -= kill;
2882 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2883 Players[pnum].net_kills_total += kill;
2884 if (Newdemo_game_mode & GM_TEAM)
2885 team_kills[get_team(pnum)] += kill;
2887 Game_mode = Newdemo_game_mode;
2888 multi_sort_kill_list();
2889 Game_mode = GM_NORMAL;
2893 case ND_EVENT_MULTI_CONNECT: {
2894 byte pnum, new_player;
2895 int killed_total, kills_total;
2896 char new_callsign[CALLSIGN_LEN+1], old_callsign[CALLSIGN_LEN+1];
2898 nd_read_byte(&pnum);
2899 nd_read_byte(&new_player);
2901 nd_read_string(old_callsign);
2902 nd_read_int(&killed_total);
2903 nd_read_int(&kills_total);
2905 nd_read_string(new_callsign);
2906 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2907 Players[pnum].connected = 0;
2909 memcpy(Players[pnum].callsign, old_callsign, CALLSIGN_LEN+1);
2910 Players[pnum].net_killed_total = killed_total;
2911 Players[pnum].net_kills_total = kills_total;
2915 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2916 Players[pnum].connected = 1;
2917 Players[pnum].net_kills_total = 0;
2918 Players[pnum].net_killed_total = 0;
2919 memcpy(Players[pnum].callsign, new_callsign, CALLSIGN_LEN+1);
2926 case ND_EVENT_MULTI_RECONNECT: {
2929 nd_read_byte(&pnum);
2930 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2931 Players[pnum].connected = 0;
2932 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2933 Players[pnum].connected = 1;
2937 case ND_EVENT_MULTI_DISCONNECT: {
2940 nd_read_byte(&pnum);
2941 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2942 Players[pnum].connected = 1;
2943 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2944 Players[pnum].connected = 0;
2948 case ND_EVENT_MULTI_SCORE: {
2952 nd_read_byte(&pnum);
2953 nd_read_int(&score);
2954 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2955 Players[pnum].score -= score;
2956 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2957 Players[pnum].score += score;
2958 Game_mode = Newdemo_game_mode;
2959 multi_sort_kill_list();
2960 Game_mode = GM_NORMAL;
2965 case ND_EVENT_PLAYER_SCORE: {
2968 nd_read_int(&score);
2969 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2970 Players[Player_num].score -= score;
2971 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2972 Players[Player_num].score += score;
2977 case ND_EVENT_PRIMARY_AMMO: {
2978 short old_ammo, new_ammo;
2980 nd_read_short(&old_ammo);
2981 nd_read_short(&new_ammo);
2983 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2984 Players[Player_num].primary_ammo[Primary_weapon] = old_ammo;
2985 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2986 Players[Player_num].primary_ammo[Primary_weapon] = new_ammo;
2990 case ND_EVENT_SECONDARY_AMMO: {
2991 short old_ammo, new_ammo;
2993 nd_read_short(&old_ammo);
2994 nd_read_short(&new_ammo);
2996 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2997 Players[Player_num].secondary_ammo[Secondary_weapon] = old_ammo;
2998 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2999 Players[Player_num].secondary_ammo[Secondary_weapon] = new_ammo;
3003 case ND_EVENT_DOOR_OPENING: {
3007 nd_read_short(&segnum);
3008 nd_read_byte(&side);
3009 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
3012 segment *segp, *csegp;
3014 segp = &Segments[segnum];
3015 csegp = &Segments[segp->children[side]];
3016 cside = find_connect_side(segp, csegp);
3017 anim_num = Walls[segp->sides[side].wall_num].clip_num;
3019 if (WallAnims[anim_num].flags & WCF_TMAP1) {
3020 segp->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[0];
3022 segp->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[0];
3028 case ND_EVENT_LASER_LEVEL: {
3029 byte old_level, new_level;
3031 nd_read_byte(&old_level);
3032 nd_read_byte(&new_level);
3033 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
3034 Players[Player_num].laser_level = old_level;
3035 update_laser_weapon_info();
3036 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
3037 Players[Player_num].laser_level = new_level;
3038 update_laser_weapon_info();
3043 case ND_EVENT_CLOAKING_WALL: {
3044 ubyte back_wall_num,front_wall_num,type,state,cloak_value;
3049 nd_read_byte(&front_wall_num);
3050 nd_read_byte(&back_wall_num);
3051 nd_read_byte(&type);
3052 nd_read_byte(&state);
3053 nd_read_byte(&cloak_value);
3059 Walls[front_wall_num].type = type;
3060 Walls[front_wall_num].state = state;
3061 Walls[front_wall_num].cloak_value = cloak_value;
3062 segp = &Segments[Walls[front_wall_num].segnum];
3063 sidenum = Walls[front_wall_num].sidenum;
3064 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
3065 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
3066 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
3067 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
3069 Walls[back_wall_num].type = type;
3070 Walls[back_wall_num].state = state;
3071 Walls[back_wall_num].cloak_value = cloak_value;
3072 segp = &Segments[Walls[back_wall_num].segnum];
3073 sidenum = Walls[back_wall_num].sidenum;
3074 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
3075 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
3076 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
3077 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
3082 case ND_EVENT_NEW_LEVEL: {
3083 byte new_level, old_level, loaded_level;
3085 nd_read_byte (&new_level);
3086 nd_read_byte (&old_level);
3087 if (Newdemo_vcr_state == ND_STATE_PAUSED)
3091 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
3092 loaded_level = old_level;
3094 loaded_level = new_level;
3095 for (i = 0; i < MAX_PLAYERS; i++) {
3096 Players[i].cloak_time = 0;
3097 Players[i].flags &= ~PLAYER_FLAGS_CLOAKED;
3100 if ((loaded_level < Last_secret_level) || (loaded_level > Last_level)) {
3103 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
3104 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
3105 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
3106 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
3110 LoadLevel((int)loaded_level,1);
3111 Newdemo_cntrlcen_destroyed = 0;
3113 if (JustStartedPlayback)
3115 nd_read_int (&Num_walls);
3116 for (i=0;i<Num_walls;i++) // restore the walls
3118 nd_read_byte (&Walls[i].type);
3119 nd_read_byte (&Walls[i].flags);
3120 nd_read_byte (&Walls[i].state);
3122 seg = &Segments[Walls[i].segnum];
3123 side = Walls[i].sidenum;
3124 nd_read_short (&seg->sides[side].tmap_num);
3125 nd_read_short (&seg->sides[side].tmap_num2);
3128 if (Newdemo_game_mode & GM_CAPTURE)
3129 multi_apply_goal_textures ();
3131 JustStartedPlayback=0;
3135 // so says Rob H.!!! if (Newdemo_game_mode & GM_MULTI) {
3136 // so says Rob H.!!! for (i = 0; i < Num_walls; i++) {
3137 // so says Rob H.!!! if (Walls[i].type == WALL_BLASTABLE)
3138 // so says Rob H.!!! {
3139 // so says Rob H.!!! int a, n;
3140 // so says Rob H.!!! int side;
3141 // so says Rob H.!!! segment *seg;
3142 // so says Rob H.!!!
3143 // so says Rob H.!!! seg = &Segments[Walls[i].segnum];
3144 // so says Rob H.!!! side = Walls[i].sidenum;
3145 // so says Rob H.!!! a = Walls[i].clip_num;
3146 // so says Rob H.!!! n = WallAnims[a].num_frames;
3147 // so says Rob H.!!! seg->sides[side].tmap_num = WallAnims[a].frames[n-1];
3148 // so says Rob H.!!! Walls[i].flags |= WALL_BLASTED;
3149 // so says Rob H.!!! }
3150 // so says Rob H.!!! }
3151 // so says Rob H.!!! }
3153 reset_palette_add(); // get palette back to normal
3158 case ND_EVENT_EOF: {
3160 fseek(infile, -1, SEEK_CUR); // get back to the EOF marker
3162 NewdemoFrameCount++;
3176 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_ERR_READING;
3177 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_DEMO_OLD_CORRUPT;
3178 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
3184 void newdemo_goto_beginning()
3186 //if (NewdemoFrameCount == 0)
3188 fseek(infile, 0, SEEK_SET);
3189 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3190 if (newdemo_read_demo_start(0))
3191 newdemo_stop_playback();
3192 if (newdemo_read_frame_information() == -1)
3193 newdemo_stop_playback();
3194 if (newdemo_read_frame_information() == -1)
3195 newdemo_stop_playback();
3196 Newdemo_vcr_state = ND_STATE_PAUSED;
3200 void newdemo_goto_end()
3202 short frame_length, byte_count, bshort;
3203 byte level, bbyte, laser_level;
3204 ubyte energy, shield, c;
3207 fseek(infile, -2, SEEK_END);
3208 nd_read_byte(&level);
3210 if ((level < Last_secret_level) || (level > Last_level)) {
3213 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
3214 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
3215 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
3216 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
3217 newdemo_stop_playback();
3220 if (level != Current_level_num)
3223 fseek(infile, -4, SEEK_END);
3224 nd_read_short(&byte_count);
3225 fseek(infile, -2 - byte_count, SEEK_CUR);
3227 nd_read_short(&frame_length);
3228 loc = ftell(infile);
3229 if (Newdemo_game_mode & GM_MULTI)
3230 nd_read_byte(&Newdemo_players_cloaked);
3232 nd_read_byte(&bbyte);
3233 nd_read_byte(&bbyte);
3234 nd_read_short(&bshort);
3237 nd_read_byte(&energy);
3238 nd_read_byte(&shield);
3239 Players[Player_num].energy = i2f(energy);
3240 Players[Player_num].shields = i2f(shield);
3241 nd_read_int((int *)&(Players[Player_num].flags));
3242 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
3243 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
3244 Newdemo_players_cloaked |= (1 << Player_num);
3246 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
3247 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
3248 nd_read_byte((byte *)&Primary_weapon);
3249 nd_read_byte((byte *)&Secondary_weapon);
3250 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
3251 nd_read_short((short *)&(Players[Player_num].primary_ammo[i]));
3252 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
3253 nd_read_short((short *)&(Players[Player_num].secondary_ammo[i]));
3254 nd_read_byte(&laser_level);
3255 if (laser_level != Players[Player_num].laser_level) {
3256 Players[Player_num].laser_level = laser_level;
3257 update_laser_weapon_info();
3260 if (Newdemo_game_mode & GM_MULTI) {
3263 // see newdemo_read_start_demo for explanation of
3264 // why this is commented out
3265 // nd_read_byte((byte *)&N_players);
3266 for (i = 0; i < N_players; i++) {
3267 nd_read_string(Players[i].callsign);
3268 nd_read_byte(&(Players[i].connected));
3269 if (Newdemo_game_mode & GM_MULTI_COOP) {
3270 nd_read_int(&(Players[i].score));
3272 nd_read_short((short *)&(Players[i].net_killed_total));
3273 nd_read_short((short *)&(Players[i].net_kills_total));
3277 nd_read_int(&(Players[Player_num].score));
3280 fseek(infile, loc, SEEK_SET);
3281 fseek(infile, -frame_length, SEEK_CUR);
3282 nd_read_int(&NewdemoFrameCount); // get the frame count
3283 NewdemoFrameCount--;
3284 fseek(infile, 4, SEEK_CUR);
3285 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3286 newdemo_read_frame_information(); // then the frame information
3287 Newdemo_vcr_state = ND_STATE_PAUSED;
3291 void newdemo_back_frames(int frames)
3293 short last_frame_length;
3296 for (i = 0; i < frames; i++)
3298 fseek(infile, -10, SEEK_CUR);
3299 nd_read_short(&last_frame_length);
3300 fseek(infile, 8 - last_frame_length, SEEK_CUR);
3302 if (!Newdemo_at_eof && newdemo_read_frame_information() == -1) {
3303 newdemo_stop_playback();
3309 fseek(infile, -10, SEEK_CUR);
3310 nd_read_short(&last_frame_length);
3311 fseek(infile, 8 - last_frame_length, SEEK_CUR);
3317 * routine to interpolate the viewer position. the current position is
3318 * stored in the Viewer object. Save this position, and read the next
3319 * frame to get all objects read in. Calculate the delta playback and
3320 * the delta recording frame times between the two frames, then intepolate
3321 * the viewers position accordingly. nd_recorded_time is the time that it
3322 * took the recording to render the frame that we are currently looking
3326 void interpolate_frame(fix d_play, fix d_recorded)
3328 int i, j, num_cur_objs;
3332 factor = fixdiv(d_play, d_recorded);
3336 num_cur_objs = Highest_object_index;
3337 cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1));
3338 if (cur_objs == NULL) {
3339 mprintf((0,"Couldn't get %d bytes for cur_objs in interpolate_frame\n", sizeof(object) * num_cur_objs));
3343 for (i = 0; i <= num_cur_objs; i++)
3344 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
3346 Newdemo_vcr_state = ND_STATE_PAUSED;
3347 if (newdemo_read_frame_information() == -1) {
3349 newdemo_stop_playback();
3353 for (i = 0; i <= num_cur_objs; i++) {
3354 for (j = 0; j <= Highest_object_index; j++) {
3355 if (cur_objs[i].signature == Objects[j].signature) {
3356 ubyte render_type = cur_objs[i].render_type;
3357 //fix delta_p, delta_h, delta_b;
3358 fix delta_x, delta_y, delta_z;
3359 //vms_angvec cur_angles, dest_angles;
3361 // Extract the angles from the object orientation matrix.
3362 // Some of this code taken from ai_turn_towards_vector
3363 // Don't do the interpolation on certain render types which don't use an orientation matrix
3365 if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) {
3367 vms_vector fvec1, fvec2, rvec1, rvec2;
3370 fvec1 = cur_objs[i].orient.fvec;
3371 vm_vec_scale(&fvec1, F1_0-factor);
3372 fvec2 = Objects[j].orient.fvec;
3373 vm_vec_scale(&fvec2, factor);
3374 vm_vec_add2(&fvec1, &fvec2);
3375 mag1 = vm_vec_normalize_quick(&fvec1);
3376 if (mag1 > F1_0/256) {
3377 rvec1 = cur_objs[i].orient.rvec;
3378 vm_vec_scale(&rvec1, F1_0-factor);
3379 rvec2 = Objects[j].orient.rvec;
3380 vm_vec_scale(&rvec2, factor);
3381 vm_vec_add2(&rvec1, &rvec2);
3382 vm_vec_normalize_quick(&rvec1); // Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1
3383 vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
3386 //--old new way -- vms_vector fvec1, fvec2, rvec1, rvec2;
3388 //--old new way -- fvec1 = cur_objs[i].orient.fvec;
3389 //--old new way -- vm_vec_scale(&fvec1, F1_0-factor);
3390 //--old new way -- fvec2 = Objects[j].orient.fvec;
3391 //--old new way -- vm_vec_scale(&fvec2, factor);
3392 //--old new way -- vm_vec_add2(&fvec1, &fvec2);
3393 //--old new way -- vm_vec_normalize_quick(&fvec1);
3395 //--old new way -- rvec1 = cur_objs[i].orient.rvec;
3396 //--old new way -- vm_vec_scale(&rvec1, F1_0-factor);
3397 //--old new way -- rvec2 = Objects[j].orient.rvec;
3398 //--old new way -- vm_vec_scale(&rvec2, factor);
3399 //--old new way -- vm_vec_add2(&rvec1, &rvec2);
3400 //--old new way -- vm_vec_normalize_quick(&rvec1);
3402 //--old new way -- vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
3404 // -- old fashioned way -- vm_extract_angles_matrix(&cur_angles, &(cur_objs[i].orient));
3405 // -- old fashioned way -- vm_extract_angles_matrix(&dest_angles, &(Objects[j].orient));
3406 // -- old fashioned way --
3407 // -- old fashioned way -- delta_p = (dest_angles.p - cur_angles.p);
3408 // -- old fashioned way -- delta_h = (dest_angles.h - cur_angles.h);
3409 // -- old fashioned way -- delta_b = (dest_angles.b - cur_angles.b);
3410 // -- old fashioned way --
3411 // -- old fashioned way -- if (delta_p != 0) {
3412 // -- old fashioned way -- if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
3413 // -- old fashioned way -- if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
3414 // -- old fashioned way -- delta_p = fixmul(delta_p, factor);
3415 // -- old fashioned way -- cur_angles.p += delta_p;
3416 // -- old fashioned way -- }
3417 // -- old fashioned way -- if (delta_h != 0) {
3418 // -- old fashioned way -- if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
3419 // -- old fashioned way -- if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
3420 // -- old fashioned way -- delta_h = fixmul(delta_h, factor);
3421 // -- old fashioned way -- cur_angles.h += delta_h;
3422 // -- old fashioned way -- }
3423 // -- old fashioned way -- if (delta_b != 0) {
3424 // -- old fashioned way -- if (delta_b > F1_0/2) delta_b = dest_angles.b - cur_angles.b - F1_0;
3425 // -- old fashioned way -- if (delta_b < -F1_0/2) delta_b = dest_angles.b - cur_angles.b + F1_0;
3426 // -- old fashioned way -- delta_b = fixmul(delta_b, factor);
3427 // -- old fashioned way -- cur_angles.b += delta_b;
3428 // -- old fashioned way -- }
3431 // Interpolate the object position. This is just straight linear
3434 delta_x = Objects[j].pos.x - cur_objs[i].pos.x;
3435 delta_y = Objects[j].pos.y - cur_objs[i].pos.y;
3436 delta_z = Objects[j].pos.z - cur_objs[i].pos.z;
3438 delta_x = fixmul(delta_x, factor);
3439 delta_y = fixmul(delta_y, factor);
3440 delta_z = fixmul(delta_z, factor);
3442 cur_objs[i].pos.x += delta_x;
3443 cur_objs[i].pos.y += delta_y;
3444 cur_objs[i].pos.z += delta_z;
3446 // -- old fashioned way --// stuff the new angles back into the object structure
3447 // -- old fashioned way -- vm_angles_2_matrix(&(cur_objs[i].orient), &cur_angles);
3452 // get back to original position in the demo file. Reread the current
3453 // frame information again to reset all of the object stuff not covered
3454 // with Highest_object_index and the object array (previously rendered
3455 // objects, etc....)
3457 newdemo_back_frames(1);
3458 newdemo_back_frames(1);
3459 if (newdemo_read_frame_information() == -1)
3460 newdemo_stop_playback();
3461 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3463 for (i = 0; i <= num_cur_objs; i++)
3464 memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object));
3465 Highest_object_index = num_cur_objs;
3469 void newdemo_playback_one_frame()
3471 int frames_back, i, level;
3472 static fix base_interpol_time = 0;
3473 static fix d_recorded = 0;
3475 for (i = 0; i < MAX_PLAYERS; i++)
3476 if (Newdemo_players_cloaked & (1 << i))
3477 Players[i].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
3479 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
3480 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
3482 if (Newdemo_vcr_state == ND_STATE_PAUSED) // render a frame or not
3485 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
3486 DoJasonInterpolate(nd_recorded_time);
3488 Control_center_destroyed = 0;
3489 Countdown_seconds_left = -1;
3490 PALETTE_FLASH_SET(0,0,0); //clear flash
3492 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
3494 level = Current_level_num;
3495 if (NewdemoFrameCount == 0)
3497 else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (NewdemoFrameCount < 10)) {
3498 newdemo_goto_beginning();
3501 if (Newdemo_vcr_state == ND_STATE_REWINDING)
3505 if (Newdemo_at_eof) {
3506 fseek(infile, 11, SEEK_CUR);
3508 newdemo_back_frames(frames_back);
3510 if (level != Current_level_num)
3511 newdemo_pop_ctrlcen_triggers();
3513 if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) {
3514 if (level != Current_level_num)
3515 newdemo_back_frames(1);
3516 Newdemo_vcr_state = ND_STATE_PAUSED;
3519 else if (Newdemo_vcr_state == ND_STATE_FASTFORWARD) {
3520 if (!Newdemo_at_eof)
3522 for (i = 0; i < 10; i++)
3524 if (newdemo_read_frame_information() == -1)
3527 Newdemo_vcr_state = ND_STATE_PAUSED;
3529 newdemo_stop_playback();
3535 Newdemo_vcr_state = ND_STATE_PAUSED;
3537 else if (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD) {
3538 if (!Newdemo_at_eof) {
3539 level = Current_level_num;
3540 if (newdemo_read_frame_information() == -1) {
3541 if (!Newdemo_at_eof)
3542 newdemo_stop_playback();
3544 if (level != Current_level_num) {
3545 if (newdemo_read_frame_information() == -1) {
3546 if (!Newdemo_at_eof)
3547 newdemo_stop_playback();
3550 Newdemo_vcr_state = ND_STATE_PAUSED;
3552 Newdemo_vcr_state = ND_STATE_PAUSED;
3556 // First, uptate the total playback time to date. Then we check to see
3557 // if we need to change the playback style to interpolate frames or
3558 // skip frames based on where the playback time is relative to the
3561 if (NewdemoFrameCount <= 0)
3562 nd_playback_total = nd_recorded_total; // baseline total playback time
3564 nd_playback_total += FrameTime;
3565 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
3566 if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) {
3567 playback_style = INTERPOLATE_PLAYBACK;
3568 nd_playback_total = nd_recorded_total + FrameTime; // baseline playback time
3569 base_interpol_time = nd_recorded_total;
3570 d_recorded = nd_recorded_time; // baseline delta recorded
3572 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
3573 if (nd_playback_total > nd_recorded_total)
3574 playback_style = SKIP_PLAYBACK;
3577 if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) {
3580 if (nd_recorded_total - nd_playback_total < FrameTime) {
3581 d_recorded = nd_recorded_total - nd_playback_total;
3583 while (nd_recorded_total - nd_playback_total < FrameTime) {
3585 int i, j, num_objs, level;
3587 num_objs = Highest_object_index;
3588 cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1));
3589 if (cur_objs == NULL) {
3590 Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs);
3593 for (i = 0; i <= num_objs; i++)
3594 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
3596 level = Current_level_num;
3597 if (newdemo_read_frame_information() == -1) {
3599 newdemo_stop_playback();
3602 if (level != Current_level_num) {
3604 if (newdemo_read_frame_information() == -1)
3605 newdemo_stop_playback();
3609 // for each new object in the frame just read in, determine if there is
3610 // a corresponding object that we have been interpolating. If so, then
3611 // copy that interpolated object to the new Objects array so that the
3612 // interpolated position and orientation can be preserved.
3614 for (i = 0; i <= num_objs; i++) {
3615 for (j = 0; j <= Highest_object_index; j++) {
3616 if (cur_objs[i].signature == Objects[j].signature) {
3617 memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix));
3618 memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector));
3624 d_recorded += nd_recorded_time;
3625 base_interpol_time = nd_playback_total - FrameTime;
3629 d_play = nd_playback_total - base_interpol_time;
3630 interpolate_frame(d_play, d_recorded);
3634 //mprintf ((0, "*"));
3635 if (newdemo_read_frame_information() == -1) {
3636 newdemo_stop_playback();
3639 if (playback_style == SKIP_PLAYBACK) {
3640 //mprintf ((0, "."));
3641 while (nd_playback_total > nd_recorded_total) {
3642 if (newdemo_read_frame_information() == -1) {
3643 newdemo_stop_playback();
3652 void newdemo_start_recording()
3655 Newdemo_size=GetFreeDiskSpace();
3656 mprintf((0, "Free space = %d\n", Newdemo_size));
3658 Newdemo_size = GetDiskFree();
3661 Newdemo_size -= 100000;
3663 if ((Newdemo_size+100000) < 2000000000) {
3664 if (((int)(Newdemo_size)) < 500000) {
3666 nm_messagebox(NULL, 1, TXT_OK, TXT_DEMO_NO_SPACE);
3668 nm_messagebox(NULL, 1, TXT_OK, "Not enough space on current\ndrive to start demo recording.");
3674 Newdemo_num_written = 0;
3676 Newdemo_state = ND_STATE_RECORDING;
3677 outfile = fopen( DEMO_FILENAME, "wb" );
3680 if (outfile == NULL && errno == ENOENT) { //dir doesn't exist?
3682 if (outfile == NULL) { //dir doesn't exist and no errno on mac!
3684 d_mkdir(DEMO_DIR); //try making directory
3685 outfile = fopen( DEMO_FILENAME, "wb" );
3688 if (outfile == NULL)
3690 nm_messagebox(NULL, 1, TXT_OK, "Cannot open demo temp file");
3691 Newdemo_state = ND_STATE_NORMAL;
3694 newdemo_record_start_demo();
3698 char demoname_allowed_chars[] = "azAZ09__--";
3699 void newdemo_stop_recording()
3703 static char filename[15] = "", *s;
3704 static ubyte tmpcnt = 0;
3706 char fullname[15+FILENAME_LEN] = DEMO_DIR;
3707 unsigned short byte_count = 0;
3711 nd_write_byte(ND_EVENT_EOF);
3712 nd_write_short(frame_bytes_written - 1);
3713 if (Game_mode & GM_MULTI) {
3714 for (l = 0; l < N_players; l++) {
3715 if (Players[l].flags & PLAYER_FLAGS_CLOAKED)
3716 cloaked |= (1 << l);
3718 nd_write_byte(cloaked);
3719 nd_write_byte(ND_EVENT_EOF);
3721 nd_write_short(ND_EVENT_EOF);
3723 nd_write_short(ND_EVENT_EOF);
3724 nd_write_int(ND_EVENT_EOF);
3726 byte_count += 10; // from frame_bytes_written
3728 nd_write_byte((byte)(f2ir(Players[Player_num].energy)));
3729 nd_write_byte((byte)(f2ir(Players[Player_num].shields)));
3730 nd_write_int(Players[Player_num].flags); // be sure players flags are set
3731 nd_write_byte((byte)Primary_weapon);
3732 nd_write_byte((byte)Secondary_weapon);
3735 for (l = 0; l < MAX_PRIMARY_WEAPONS; l++)
3736 nd_write_short((short)Players[Player_num].primary_ammo[l]);
3738 for (l = 0; l < MAX_SECONDARY_WEAPONS; l++)
3739 nd_write_short((short)Players[Player_num].secondary_ammo[l]);
3740 byte_count += (sizeof(short) * (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS));
3742 nd_write_byte(Players[Player_num].laser_level);
3745 if (Game_mode & GM_MULTI) {
3746 nd_write_byte((byte)N_players);
3748 for (l = 0; l < N_players; l++) {
3749 nd_write_string(Players[l].callsign);
3750 byte_count += (strlen(Players[l].callsign) + 2);
3751 nd_write_byte(Players[l].connected);
3752 if (Game_mode & GM_MULTI_COOP) {
3753 nd_write_int(Players[l].score);
3756 nd_write_short((short)Players[l].net_killed_total);
3757 nd_write_short((short)Players[l].net_kills_total);
3762 nd_write_int(Players[Player_num].score);
3765 nd_write_short(byte_count);
3767 nd_write_byte(Current_level_num);
3768 nd_write_byte(ND_EVENT_EOF);
3773 Newdemo_state = ND_STATE_NORMAL;
3774 gr_palette_load( gr_palette );
3776 if (filename[0] != '\0') {
3777 int num, i = strlen(filename) - 1;
3780 while (isdigit(filename[i])) {
3786 num = atoi(&(filename[i]));
3789 sprintf (newfile, "%s%d", filename, num);
3790 strncpy(filename, newfile, 8);
3797 Newmenu_allowed_chars = demoname_allowed_chars;
3798 if (!Newdemo_no_space) {
3799 m[0].type=NM_TYPE_INPUT; m[0].text_len = 8; m[0].text = filename;
3800 exit = newmenu_do( NULL, TXT_SAVE_DEMO_AS, 1, &(m[0]), NULL );
3801 } else if (Newdemo_no_space == 1) {
3802 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_BAD;
3803 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3804 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3805 } else if (Newdemo_no_space == 2) {
3806 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_NOSPACE;
3807 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3808 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3810 Newmenu_allowed_chars = NULL;
3812 if (exit == -2) { // got bumped out from network menu
3813 char save_file[7+FILENAME_LEN];
3815 if (filename[0] != '\0') {
3816 strcpy(save_file, DEMO_DIR);
3817 strcat(save_file, filename);
3818 strcat(save_file, ".dem");
3820 sprintf (save_file, "%stmp%d.dem", DEMO_DIR, tmpcnt++);
3822 rename(DEMO_FILENAME, save_file);
3825 if (exit == -1) { // pressed ESC
3826 remove(DEMO_FILENAME); // might as well remove the file
3827 return; // return without doing anything
3830 if (filename[0]==0) //null string
3833 //check to make sure name is ok
3834 for (s=filename;*s;s++)
3835 if (!isalnum(*s) && *s!='_') {
3836 nm_messagebox1(NULL, NULL,1,TXT_CONTINUE, TXT_DEMO_USE_LETTERS);
3840 if (Newdemo_no_space)
3841 strcat(fullname, m[1].text);
3843 strcat(fullname, m[0].text);
3844 strcat(fullname, ".dem");
3846 rename(DEMO_FILENAME, fullname);
3849 //returns the number of demo files on the disk
3850 int newdemo_count_demos()
3852 FILEFINDSTRUCT find;
3855 if( !FileFindFirst( DEMO_DIR "*.dem", &find ) ) {
3858 } while( !FileFindNext( &find ) );
3862 if ( AltHogdir_initialized ) {
3863 char search_name[PATH_MAX + 5];
3864 strcpy(search_name, AltHogDir);
3865 strcat(search_name, "/" DEMO_DIR "*.dem");
3866 if( !FileFindFirst( search_name, &find ) ) {
3869 } while( !FileFindNext( &find ) );
3877 void newdemo_start_playback(char * filename)
3879 FILEFINDSTRUCT find;
3881 char filename2[PATH_MAX+FILENAME_LEN] = DEMO_DIR;
3884 change_playernum_to(0);
3886 First_time_playback=1;
3887 JasonPlaybackTotal=0;
3889 if (filename==NULL) {
3890 // Randomly pick a filename
3891 int NumFiles = 0, RandFileNum;
3894 NumFiles = newdemo_count_demos();
3896 if ( NumFiles == 0 ) {
3897 return; // No files found!
3899 RandFileNum = d_rand() % NumFiles;
3901 if( !FileFindFirst( DEMO_DIR "*.dem", &find ) ) {
3903 if ( NumFiles==RandFileNum ) {
3904 filename = (char *)&find.name;
3908 } while( !FileFindNext( &find ) );
3912 if ( filename == NULL && AltHogdir_initialized ) {
3913 char search_name[PATH_MAX + 5];
3914 strcpy(search_name, AltHogDir);
3915 strcat(search_name, "/" DEMO_DIR "*.dem");
3916 if( !FileFindFirst( search_name, &find ) ) {
3918 if ( NumFiles==RandFileNum ) {
3919 filename = (char *)&find.name;
3923 } while( !FileFindNext( &find ) );
3928 if ( filename==NULL) return;
3934 strcat(filename2,filename);
3936 infile = fopen( filename2, "rb" );
3938 if (infile==NULL && AltHogdir_initialized) {
3939 strcpy(filename2, AltHogDir);
3940 strcat(filename2, "/" DEMO_DIR);
3941 strcat(filename2, filename);
3942 infile = fopen( filename2, "rb" );
3946 mprintf( (0, "Error reading '%s'\n", filename ));
3952 change_playernum_to(0); // force playernum to 0
3954 strncpy(nd_save_callsign, Players[Player_num].callsign, CALLSIGN_LEN);
3955 Viewer = ConsoleObject = &Objects[0]; // play properly as if console player
3956 if (newdemo_read_demo_start(rnd_demo)) {
3961 Game_mode = GM_NORMAL;
3962 Newdemo_state = ND_STATE_PLAYBACK;
3963 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3964 Newdemo_old_cockpit = Cockpit_mode;
3965 Newdemo_size = filelength(fileno(infile));
3968 NewdemoFrameCount = 0;
3969 Newdemo_players_cloaked = 0;
3970 playback_style = NORMAL_PLAYBACK;
3971 Function_mode = FMODE_GAME;
3972 Cockpit_3d_view[0] = CV_NONE; //turn off 3d views on cockpit
3973 Cockpit_3d_view[1] = CV_NONE; //turn off 3d views on cockpit
3974 newdemo_playback_one_frame(); // this one loads new level
3975 newdemo_playback_one_frame(); // get all of the objects to renderb game
3978 void newdemo_stop_playback()
3981 Newdemo_state = ND_STATE_NORMAL;
3983 change_playernum_to(0); //this is reality
3985 strncpy(Players[Player_num].callsign, nd_save_callsign, CALLSIGN_LEN);
3986 Cockpit_mode = Newdemo_old_cockpit;
3987 Game_mode = GM_GAME_OVER;
3988 Function_mode = FMODE_MENU;
3989 longjmp(LeaveGame,0); // Exit game loop
3995 #define BUF_SIZE 16384
3997 void newdemo_strip_frames(char *outname, int bytes_to_strip)
4001 int total_size, bytes_done, read_elems, bytes_back;
4002 int trailer_start, loc1, loc2, stop_loc, bytes_to_read;
4003 short last_frame_length;
4006 total_size = filelength(fileno(infile));
4007 outfile = fopen(outname, "wb");
4008 if (outfile == NULL) {
4011 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't open output file";
4012 newmenu_do( NULL, NULL, 1, m, NULL );
4013 newdemo_stop_playback();
4016 buf = d_malloc(BUF_SIZE);
4020 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't malloc output buffer";
4021 newmenu_do( NULL, NULL, 1, m, NULL );
4023 newdemo_stop_playback();
4027 trailer_start = ftell(infile);
4028 fseek(infile, 11, SEEK_CUR);
4030 while (bytes_back < bytes_to_strip) {
4031 loc1 = ftell(infile);
4032 //fseek(infile, -10, SEEK_CUR);
4033 //nd_read_short(&last_frame_length);
4034 //fseek(infile, 8 - last_frame_length, SEEK_CUR);
4035 newdemo_back_frames(1);
4036 loc2 = ftell(infile);
4037 bytes_back += (loc1 - loc2);
4039 fseek(infile, -10, SEEK_CUR);
4040 nd_read_short(&last_frame_length);
4041 fseek(infile, -3, SEEK_CUR);
4042 stop_loc = ftell(infile);
4043 fseek(infile, 0, SEEK_SET);
4044 while (stop_loc > 0) {
4045 if (stop_loc < BUF_SIZE)
4046 bytes_to_read = stop_loc;
4048 bytes_to_read = BUF_SIZE;
4049 read_elems = fread(buf, 1, bytes_to_read, infile);
4050 fwrite(buf, 1, read_elems, outfile);
4051 stop_loc -= read_elems;
4053 stop_loc = ftell(outfile);
4054 fseek(infile, trailer_start, SEEK_SET);
4055 while ((read_elems = fread(buf, 1, BUF_SIZE, infile)) != 0)
4056 fwrite(buf, 1, read_elems, outfile);
4057 fseek(outfile, stop_loc, SEEK_SET);
4058 fseek(outfile, 1, SEEK_CUR);
4059 fwrite(&last_frame_length, 2, 1, outfile);
4061 newdemo_stop_playback();
4067 object DemoRightExtra,DemoLeftExtra;
4068 ubyte DemoDoRight=0,DemoDoLeft=0;
4070 void nd_render_extras (ubyte which,object *obj)
4073 ubyte type=which&15;
4077 Int3(); // how'd we get here?
4078 do_cockpit_window_view(w,NULL,0,WBU_WEAPON,NULL);
4084 memcpy (&DemoRightExtra,obj,sizeof(object)); DemoDoRight=type;
4088 memcpy (&DemoLeftExtra,obj,sizeof(object)); DemoDoLeft=type;
4093 void DoJasonInterpolate (fix recorded_time)
4097 JasonPlaybackTotal+=FrameTime;
4099 if (!First_time_playback)
4101 // get the difference between the recorded time and the playback time
4102 the_delay=(recorded_time - FrameTime);
4103 //mprintf ((0,"The delay=%d\n", f2i(the_delay)));
4104 if (the_delay >= f0_0)
4107 timer_delay(the_delay);
4112 while (JasonPlaybackTotal > nd_recorded_total)
4113 if (newdemo_read_frame_information() == -1)
4115 newdemo_stop_playback();
4119 //the_delay = nd_recorded_total - JasonPlaybackTotal;
4120 //if (the_delay > f0_0)
4121 // timer_delay(the_delay);
4126 First_time_playback=0;
4130 #pragma global_optimizer reset