1 /* $Id: newdemo.c,v 1.15 2003-11-26 12:26:31 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.
20 * Revision 1.12 1995/10/31 10:19:43 allender
23 * Revision 1.11 1995/10/17 13:19:16 allender
24 * close boxes for demo save
26 * Revision 1.10 1995/10/05 10:36:07 allender
27 * fixed calls to digi_play_sample to account for differing volume and angle calculations
29 * Revision 1.9 1995/09/12 15:49:13 allender
30 * define __useAppleExts__ if not already defined
32 * Revision 1.8 1995/09/05 14:28:32 allender
33 * fixed previous N_players bug in newdemo_goto_end
35 * Revision 1.7 1995/09/05 14:16:51 allender
36 * added space to allowable demo filename characters
37 * and fixed bug with netgame demos N_players got getting
40 * Revision 1.6 1995/09/01 16:10:47 allender
41 * fixed bug with reading in N_players variable on
42 * netgame demo playback
44 * Revision 1.5 1995/08/24 16:04:11 allender
45 * fix signed byte problem
47 * Revision 1.4 1995/08/12 12:21:59 allender
48 * made call to create_shortpos not swap the shortpos
51 * Revision 1.3 1995/08/01 16:04:19 allender
52 * made random picking of demo work
54 * Revision 1.2 1995/08/01 13:56:56 allender
55 * demo system working on the mac
57 * Revision 1.1 1995/05/16 15:28:59 allender
60 * Revision 2.7 1995/05/26 16:16:06 john
61 * Split SATURN into define's for requiring cd, using cd, etc.
62 * Also started adding all the Rockwell stuff.
64 * Revision 2.6 1995/03/21 14:39:38 john
65 * Ifdef'd out the NETWORK code.
67 * Revision 2.5 1995/03/14 18:24:31 john
68 * Force Destination Saturn to use CD-ROM drive.
70 * Revision 2.4 1995/03/14 16:22:29 john
71 * Added cdrom alternate directory stuff.
73 * Revision 2.3 1995/03/10 12:58:33 allender
74 * only display rear view cockpit when cockpit mode was CM_FULL_COCKPIT.
76 * Revision 2.2 1995/03/08 16:12:15 allender
77 * changes for Destination Saturn
79 * Revision 2.1 1995/03/08 12:11:26 allender
80 * fix shortpos reading
82 * Revision 2.0 1995/02/27 11:29:40 john
83 * New version 2.0, which has no anonymous unions, builds with
84 * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
86 * Revision 1.189 1995/02/22 14:53:42 allender
87 * missed some anonymous stuff
89 * Revision 1.188 1995/02/22 13:24:53 john
90 * Removed the vecmat anonymous unions.
92 * Revision 1.187 1995/02/22 13:13:54 allender
93 * remove anonymous unions from object structure
95 * Revision 1.186 1995/02/14 15:36:41 allender
96 * fix fix for morph effect
98 * Revision 1.185 1995/02/14 11:25:48 allender
99 * save cockpit mode and restore after playback. get orientation for morph
100 * effect when object is morph vclip
102 * Revision 1.184 1995/02/13 12:18:14 allender
103 * change to decide about interpolating or not
105 * Revision 1.183 1995/02/12 00:46:23 adam
106 * don't decide to skip frames until after at least 10 frames have
109 * Revision 1.182 1995/02/11 22:34:01 john
110 * Made textures page in for newdemos before playback time.
112 * Revision 1.181 1995/02/11 17:28:32 allender
113 * strip frames from end of demo
115 * Revision 1.180 1995/02/11 16:40:35 allender
116 * start of frame stripping debug code
118 * Revision 1.179 1995/02/10 17:40:06 allender
119 * put back in wall_hit_process code to fix door problem
121 * Revision 1.178 1995/02/10 17:17:24 adam
124 * Revision 1.177 1995/02/10 17:16:24 allender
125 * fix possible tmap problems
127 * Revision 1.176 1995/02/10 15:54:37 allender
128 * changes for out of space on device.
130 * Revision 1.175 1995/02/09 19:55:00 allender
131 * fix bug with morph recording -- force rendertype to RT_POLYOBJ when
132 * playing back since it won't render until fully morphed otherwise
134 * Revision 1.174 1995/02/07 17:15:35 allender
137 * Revision 1.173 1995/02/07 17:14:21 allender
138 * immediately return when loading bogus level stuff when reading a frame
140 * Revision 1.172 1995/02/02 11:15:03 allender
141 * after loading new level, read next frame (forward or back) always because
142 * of co-op ships showing up when level is loaded
144 * Revision 1.171 1995/02/02 10:24:16 allender
145 * removed cfile stuff. Use standard FILE functions for demo playback
147 * Revision 1.170 1995/01/30 13:54:32 allender
148 * support for missions
150 * Revision 1.169 1995/01/27 16:27:35 allender
151 * put game mode to demo_game_mode when sorting multiplayer kill (and score)
154 * Revision 1.168 1995/01/27 09:52:25 allender
155 * minor changes because of object/segment linking problems
157 * Revision 1.167 1995/01/27 09:22:28 allender
158 * changed way multi-player score is recorded. Record difference, not
161 * Revision 1.166 1995/01/25 14:32:44 allender
162 * changed with recorded player flags. More checks for paused state
163 * during interpolation reading of objects
165 * Revision 1.165 1995/01/25 11:23:32 allender
166 * found bug with out of disk space problem
168 * Revision 1.164 1995/01/25 11:11:33 allender
169 * coupla' things. Fix problem with objects apparently being linked in
170 * the wrong segment. Put an Int3 in to check why demos will write to
171 * end of space on drive. close demo file if demo doens't start playing
172 * back. Put obj->type == OBJ_ROBOT around checking for boss cloaking
174 * Revision 1.163 1995/01/24 19:44:30 allender
175 * fix obscure problem with rewinding and having the wrong object linked
176 * to the wrong segments. will investigate further.
178 * Revision 1.162 1995/01/23 09:31:28 allender
179 * add team score in team mode playback
181 * Revision 1.161 1995/01/20 22:47:39 matt
182 * Mission system implemented, though imcompletely
184 * Revision 1.160 1995/01/20 09:30:37 allender
185 * don't call LoadLevel with bogus data
187 * Revision 1.159 1995/01/20 09:13:23 allender
190 * Revision 1.158 1995/01/20 09:12:04 allender
191 * record team names during demo recoring in GM_TEAM
193 * Revision 1.157 1995/01/19 16:31:09 allender
194 * forgot to bump demo version for new weapon change stuff
196 * Revision 1.156 1995/01/19 16:29:33 allender
197 * added new byte for weapon change (old weapon) so rewinding works
198 * correctly for weapon changes in registered
200 * Revision 1.155 1995/01/19 15:00:05 allender
201 * remove code to take away blastable walls in multiplayer demo playbacks
203 * Revision 1.154 1995/01/19 11:07:05 allender
204 * put in psuedo cloaking for boss robots. Problem is that cloaking is
205 * time based, and that don't get done in demos, so bosses just disappear.
208 * Revision 1.153 1995/01/19 09:42:29 allender
209 * record laser levels in demos
211 * Revision 1.152 1995/01/18 20:43:12 allender
212 * fix laser level stuff on goto-beginning and goto-end
214 * Revision 1.151 1995/01/18 20:28:18 allender
215 * cloak robots now cloak (except maybe for boss........) Put in function
216 * to deal with control center triggers
218 * Revision 1.150 1995/01/18 18:55:07 allender
221 * Revision 1.149 1995/01/18 18:49:03 allender
222 * lots 'o stuff....record laser level. Record beginning of door opening
223 * sequence. Fix some problems with control center stuff. Control center
224 * triggers now work in reverse
226 * Revision 1.148 1995/01/18 08:51:40 allender
227 * forgot to record ammo counts at beginning of demo
229 * Revision 1.147 1995/01/17 17:42:07 allender
230 * added primary and secondary ammo counts. Changed goto_end routine
231 * to be more efficient
233 * Revision 1.146 1995/01/17 13:46:35 allender
234 * fix problem with destroyed control center and rewinding a demo.
235 * Save callsign and restore after demo playback
237 * Revision 1.145 1995/01/12 10:21:53 allender
238 * fixes for 1.0 to 1.1 demo incompatibility
240 * Revision 1.144 1995/01/05 13:51:43 allender
241 * fixed type of player num variable
243 * Revision 1.143 1995/01/04 16:58:28 allender
244 * bumped up demo version number
246 * Revision 1.142 1995/01/04 14:59:02 allender
247 * added more information to end of demo for registered. Forced game mode
248 * to be GM_NORMAL on demo playback
250 * Revision 1.141 1995/01/03 17:30:47 allender
251 * fixed logic problem with cloak stuf
253 * Revision 1.140 1995/01/03 17:12:23 allender
254 * fix for getting cloak stuff at end of demo for shareware
256 * Revision 1.139 1995/01/03 15:20:24 allender
257 * fix goto_end for shareware -- changes to goto_end for registered
259 * Revision 1.138 1995/01/03 13:13:26 allender
262 * Revision 1.137 1995/01/03 13:10:29 allender
263 * make score work forwards and backwards
265 * Revision 1.136 1995/01/03 11:45:20 allender
266 * added code to record players scores
268 * Revision 1.135 1994/12/30 10:03:57 allender
269 * put cloak stuff at end of demo for fast forward to the end
271 * Revision 1.134 1994/12/29 17:02:55 allender
272 * spelling fix on SHAREWARE
274 * Revision 1.133 1994/12/29 16:43:41 allender
275 * lots of new multiplayer stuff. wrapped much code with SHAREWARE defines
277 * Revision 1.132 1994/12/28 14:15:01 allender
278 * added routines to deal with connecting and reconnecting players when
279 * recording multiplayer demos
281 * Revision 1.131 1994/12/21 12:57:59 allender
284 * Revision 1.130 1994/12/21 12:46:53 allender
285 * record multi player deaths and kills
287 * Revision 1.129 1994/12/19 16:37:27 allender
288 * pick good filename when trying to save in network play and player
289 * gets bumped out of menu by multi-player code
291 * Revision 1.128 1994/12/14 10:49:01 allender
292 * reset bad_read variable when starting demo playback
294 * Revision 1.127 1994/12/14 08:53:06 allender
295 * lowered watermark for out of space
297 * Revision 1.126 1994/12/14 08:49:52 allender
298 * put up warning when starting demo recording if not enough space and
299 * not let them record
301 * Revision 1.125 1994/12/13 00:01:37 allender
302 * CLOAK FIX -- (I'm tempted to take cloak out of game because I can't
303 * seem to get it right in demo playback)
305 * Revision 1.124 1994/12/12 14:51:21 allender
306 * more fixed to multiplayer cloak stuff
308 * Revision 1.123 1994/12/12 11:33:11 allender
309 * fixed rearview mode to work again
311 * Revision 1.122 1994/12/12 11:00:16 matt
312 * Added code to handle confusion with attached objects
314 * Revision 1.121 1994/12/12 00:31:29 allender
315 * give better warning when out of space when recording. Don't record
316 * until no space left. We have 500K watermark when we now stop recording
318 * Revision 1.120 1994/12/10 16:44:54 matt
319 * Added debugging code to track down door that turns into rock
321 * Revision 1.119 1994/12/09 18:46:15 matt
322 * Added code to handle odd error condition
324 * Revision 1.118 1994/12/09 17:27:37 allender
325 * force playernum to 0 when demo is done playing
327 * Revision 1.117 1994/12/09 16:40:39 allender
328 * yet more cloak stuff. Assign cloak/invuln time when starting demo
329 * if flags are set. Check cloak and invuln time when demo
332 * Revision 1.116 1994/12/09 14:59:22 matt
333 * Added system to attach a fireball to another object for rendering purposes,
334 * so the fireball always renders on top of (after) the object.
336 * Revision 1.115 1994/12/09 12:21:45 allender
337 * only allow valid chars when typing in demo filename
339 * Revision 1.114 1994/12/08 23:19:02 allender
340 * final(?) fix for getting cloak gauge to work on demo playback
341 * with forward and reverse
343 * Revision 1.113 1994/12/08 21:34:38 allender
344 * record old and new player flags to accuratedly record cloaking and
348 * Revision 1.112 1994/12/08 18:04:47 allender
349 * bashed playernum right after reading it in demo header so shields
350 * and energy are put in right place
352 * Revision 1.111 1994/12/08 17:10:07 allender
353 * encode playernum in demo header. Bash viewer segment to 0 if in
354 * bogus segnum. Don't link render objs for same reason
356 * Revision 1.110 1994/12/08 15:36:12 allender
357 * cloak stuff works forwards and backwards
359 * Revision 1.109 1994/12/08 13:46:03 allender
360 * don't record rearview anymore, but leave in case statement for playback
361 * purposes. change the way letterbox <--> cockpit transitions happen
363 * Revision 1.108 1994/12/08 12:36:06 matt
364 * Added new object allocation & deallocation functions so other code
365 * could stop messing around with internal object data structures.
367 * Revision 1.107 1994/12/08 11:19:04 allender
368 * handle out of space (more) gracefully then before
370 * Revision 1.106 1994/12/08 00:29:49 allender
371 * fixed bug that didn't load level on goto_beginning
373 * Revision 1.105 1994/12/08 00:11:51 mike
374 * change matrix interpolation.
376 * Revision 1.104 1994/12/07 23:46:37 allender
377 * changed invulnerability and cloak to work (almost) correctly both
378 * in single and multi player
380 * Revision 1.103 1994/12/07 11:48:49 adam
381 * BY ALLENDER -- added dampening of interpolation factor to 1 if greater
382 * than 1 (although I have not seen this happen). this is attempt to
383 * get wobbling problem solved
385 * Revision 1.102 1994/12/07 11:23:56 allender
386 * attempt at getting rid of wobbling on demo playback
388 * Revision 1.101 1994/12/06 19:31:17 allender
389 * moved blastable wall stuff code to where we load level during demo
392 * Revision 1.100 1994/12/06 19:21:51 allender
393 * multi games, destroy blastable walls. Do wall toggle when control center
396 * Revision 1.99 1994/12/06 16:54:48 allender
397 * fixed code so if demo automatically started from menu, don't bring up
398 * message if demo is too old
400 * Revision 1.98 1994/12/06 13:55:15 matt
401 * Use new rounding func, f2ir()
403 * Revision 1.97 1994/12/06 13:44:45 allender
404 * suppressed compiler warnings
406 * Revision 1.96 1994/12/06 13:38:03 allender
407 * removed recording of wall hit process. I think that all bases are covered
410 * Revision 1.95 1994/12/06 12:57:35 allender
411 * added recording of multi_decloaking. Fixed some other cloaking code so
412 * that cloak should last as long as player was cloaked. We will lose the
413 * guage effect, but the time is probably more important on playback
415 * Revision 1.94 1994/12/05 23:37:17 matt
416 * Took out calls to warning() function
418 * Revision 1.93 1994/12/03 17:52:04 yuan
419 * Localization 380ish
421 * Revision 1.92 1994/12/02 12:53:39 allender
422 * fixed goto_beginning and goto_end on demo playback
424 * Revision 1.91 1994/12/01 12:01:49 allender
425 * added multi player cloak stuff
427 * Revision 1.90 1994/11/30 09:33:58 allender
428 * added field in header to tell what version (shareware or registered)
429 * demo was recorded with. Don't allow demo recorded on one to playback
432 * Revision 1.89 1994/11/29 00:31:01 allender
433 * major changes -- added level recording feature which records level
434 * advancement. Changes to internal code to handle this.
436 * Revision 1.88 1994/11/27 23:13:54 matt
437 * Made changes for new mprintf calling convention
439 * Revision 1.87 1994/11/27 23:07:35 allender
440 * starting on code to get all level transitions recorded. not done yet
442 * Revision 1.86 1994/11/27 17:39:47 matt
443 * Don't xlate tmap numbers when editor compiled out
445 * Revision 1.85 1994/11/23 09:27:21 allender
446 * put up info box with message if demo version is too old or level
449 * Revision 1.84 1994/11/22 19:37:39 allender
452 * Revision 1.83 1994/11/22 19:35:09 allender
453 * record player ship colors in multiplayer demo recordings
455 * Revision 1.82 1994/11/19 15:36:42 mike
458 * Revision 1.81 1994/11/19 15:23:21 mike
459 * rip out unused code
461 * Revision 1.80 1994/11/16 14:51:49 rob
462 * Fixed network/demo incompatibility.
464 * Revision 1.79 1994/11/15 10:55:48 allender
465 * made start of demo playback read initial demo information so
466 * level will get loaded. Made demo record to single file which
467 * will get renamed. Added numerics after old filename so
468 * sequential filenames would be defaulted to
470 * Revision 1.78 1994/11/15 09:46:06 allender
471 * added versioning. Fixed problems with trying to interpolating a completely
472 * 0 orientation matrix
474 * Revision 1.77 1994/11/14 14:34:31 matt
475 * Fixed up handling when textures can't be found during remap
477 * Revision 1.76 1994/11/14 09:15:29 allender
478 * make ESC from file save menu exit w/o saving. Fix letterbox, rear view,
479 * to normal cockpit mode transition to work correctly when skipping and
480 * interpolating frames
482 * Revision 1.75 1994/11/11 16:22:07 allender
483 * made morphing objects record only the object being morphed.
485 * Revision 1.74 1994/11/08 14:59:19 john
486 * Added code to respond to network while in menus.
488 * Revision 1.73 1994/11/08 14:52:20 adam
489 * *** empty log message ***
491 * Revision 1.72 1994/11/07 15:47:04 allender
492 * prompt for filename when done recording demo
494 * Revision 1.71 1994/11/07 11:47:19 allender
495 * when interpolating frames, delete weapon, fireball, and debris objects
496 * from an inpolated frame if they don't appear in the next recorded
499 * Revision 1.70 1994/11/07 11:02:41 allender
500 * more with interpolation. I believe that I have it right now
502 * Revision 1.69 1994/11/07 08:47:40 john
503 * Made wall state record.
505 * Revision 1.68 1994/11/05 17:22:51 john
506 * Fixed lots of sequencing problems with newdemo stuff.
508 * Revision 1.67 1994/11/04 20:11:52 john
509 * Neatening up palette stuff with demos.
511 * Revision 1.66 1994/11/04 16:49:44 allender
512 * changed newdemo_do_interpolate to default to on
514 * Revision 1.65 1994/11/04 16:44:51 allender
515 * added filename support for demo recording. more auto demo stuff
517 * Revision 1.64 1994/11/04 13:05:31 allender
518 * fixing the lifeleft variable again. (I think I got it right this time)
520 * Revision 1.63 1994/11/04 11:37:37 allender
521 * commented out fprintfs and fixed compiler warning
523 * Revision 1.62 1994/11/04 11:33:50 allender
524 * added OBJ_FLARE and OBJ_LIGHT to obj->lifeleft recording
526 * Revision 1.61 1994/11/04 11:29:21 allender
527 * more interpolation stuff -- not done yet. Fixed so hostage vclips
528 * render correctly. Changed lifeleft to full precision, but only
529 * write it when object is fireball or weapon type of object
531 * Revision 1.60 1994/11/03 10:00:11 allender
532 * fixed divide by zero in calculating render time. more interpolation
533 * stuff which isn't quite done
535 * Revision 1.59 1994/11/02 17:10:59 allender
536 * never play recorded frames when interpolation is occuring
538 * Revision 1.58 1994/11/02 14:28:58 allender
539 * profile total playback time and average frame render time
541 * Revision 1.57 1994/11/02 14:09:03 allender
542 * record rear view. start of playback interpolation code -- this
545 * Revision 1.56 1994/11/01 13:25:30 allender
546 * drop frames if playing back demo on slower machine
548 * Revision 1.55 1994/10/31 16:10:40 allender
549 * record letterbox mode on death seq, and then restore
551 * Revision 1.54 1994/10/29 16:01:38 allender
552 * added ND_STATE_NODEMOS to indicate that there are no demos currently
553 * available for playback
555 * Revision 1.53 1994/10/29 15:38:42 allender
556 * in newdemo_start_playback, make Newdemo_at_eof = 0
558 * Revision 1.52 1994/10/28 14:45:28 john
559 * fixed typo from last checkin.
561 * Revision 1.51 1994/10/28 14:42:55 john
562 * Added sound volumes to all sound calls.
564 * Revision 1.50 1994/10/28 14:31:57 allender
565 * homing missle and autodemo stuff
567 * Revision 1.49 1994/10/28 12:42:14 allender
568 * record homing distance
570 * Revision 1.48 1994/10/27 16:57:54 allender
571 * changed demo vcr to be able to play any number of frames by storing
572 * frame length (in bytes) in the demo file. Added blowing up monitors
574 * Revision 1.47 1994/10/26 16:50:50 allender
575 * put two functions inside of VCR_MODE ifdef
577 * Revision 1.46 1994/10/26 15:20:32 allender
578 * added CT_REMOTE as valid control type for recording
580 * Revision 1.45 1994/10/26 14:45:35 allender
581 * completed hacked in vcr demo playback stuff
583 * Revision 1.44 1994/10/26 13:40:52 allender
584 * vcr playback of demo stuff
586 * Revision 1.43 1994/10/26 08:51:57 allender
587 * record player weapon change
589 * Revision 1.42 1994/10/25 15:48:01 allender
590 * add shields, energy, and player flags to demo recording.
593 * Revision 1.41 1994/10/24 08:19:35 allender
594 * fixed compilation errors
596 * Revision 1.40 1994/10/23 19:17:08 matt
597 * Fixed bug with "no key" messages
599 * Revision 1.39 1994/10/22 14:15:08 mike
600 * Suppress compiler warnings.
602 * Revision 1.38 1994/10/21 15:24:55 allender
603 * compressed writing of object structures with specialized code
604 * to write out only pertinent object structures.
606 * Revision 1.37 1994/10/20 13:03:17 matt
607 * Replaced old save files (MIN/SAV/HOT) with new LVL files
609 * Revision 1.36 1994/09/28 23:13:10 matt
610 * Macroized palette flash system
612 * Revision 1.35 1994/09/26 17:28:32 matt
613 * Made new multiple-object morph code work with the demo system
615 * Revision 1.34 1994/09/10 13:31:54 matt
616 * Made exploding walls a type of blastable walls.
617 * Cleaned up blastable walls, making them tmap2 bitmaps.
619 * Revision 1.33 1994/08/15 18:05:28 john
620 * *** empty log message ***
622 * Revision 1.32 1994/08/15 17:56:38 john
625 * Revision 1.31 1994/08/10 09:44:54 john
626 * *** empty log message ***
628 * Revision 1.30 1994/07/22 12:35:48 matt
629 * Cleaned up editor/game interactions some more.
631 * Revision 1.29 1994/07/21 13:06:45 matt
632 * Ripped out remants of old demo system, and added demo only system that
633 * disables object movement and game options from menu.
635 * Revision 1.28 1994/07/18 16:22:44 john
636 * Made all file read/writes call the same routine.
638 * Revision 1.27 1994/07/14 22:38:27 matt
639 * Added exploding doors
641 * Revision 1.26 1994/07/05 12:49:04 john
642 * Put functionality of New Hostage spec into code.
644 * Revision 1.25 1994/06/29 11:05:38 john
645 * Made demos read in compressed.
647 * Revision 1.24 1994/06/29 09:14:06 john
648 * Made files write out uncompressed and read in compressed.
650 * Revision 1.23 1994/06/28 11:55:28 john
651 * Made newdemo system record/play directly to/from disk, so
652 * we don't need the 4 MB buffer anymore.
654 * Revision 1.22 1994/06/27 15:52:38 john
655 * #define'd out the newdemo stuff
658 * Revision 1.21 1994/06/22 00:29:04 john
659 * Fixed bug with playing demo then playing game without
662 * Revision 1.20 1994/06/22 00:14:23 john
663 * Attempted to fix sign.
665 * Revision 1.19 1994/06/21 23:57:54 john
666 * Hopefully fixed bug with negative countdowns.
668 * Revision 1.18 1994/06/21 23:47:44 john
669 * MAde Malloc always 4*1024*1024.
671 * Revision 1.17 1994/06/21 22:58:47 john
672 * Added error if out of memory.
674 * Revision 1.16 1994/06/21 22:15:48 john
675 * Added % done to demo recording.
678 * Revision 1.15 1994/06/21 19:45:55 john
679 * Added palette effects to demo recording.
681 * Revision 1.14 1994/06/21 15:08:54 john
682 * Made demo record HUD message and cleaned up the HUD code.
684 * Revision 1.13 1994/06/21 14:20:08 john
685 * Put in hooks to record HUD messages.
687 * Revision 1.12 1994/06/20 11:50:15 john
688 * Made demo record flash effect, and control center triggers.
690 * Revision 1.11 1994/06/17 18:01:33 john
691 * A bunch of new stuff by John
693 * Revision 1.10 1994/06/17 12:13:31 john
694 * More newdemo stuff; made editor->game transition start in slew mode.
696 * Revision 1.9 1994/06/16 13:14:36 matt
699 * Revision 1.8 1994/06/16 13:02:07 john
702 * Revision 1.7 1994/06/15 19:01:33 john
703 * Added the capability to make 3d sounds play just once for the
704 * laser hit wall effects.
706 * Revision 1.6 1994/06/15 14:56:59 john
707 * Added triggers to demo recording.
709 * Revision 1.5 1994/06/14 20:42:15 john
710 * Made robot matztn cntr not work until no robots or player are
713 * Revision 1.4 1994/06/14 14:43:27 john
714 * Made doors work with newdemo system.
716 * Revision 1.3 1994/06/14 11:32:29 john
717 * Made Newdemo record & restore the current mine.
719 * Revision 1.2 1994/06/13 21:02:43 john
720 * Initial version of new demo recording system.
722 * Revision 1.1 1994/06/13 11:09:00 john
736 #include <string.h> // for memset
740 #include <ctype.h> /* for isdigit */
743 #include <sys/stat.h>
744 #include <sys/types.h>
769 #include "fireball.h"
782 #include "lighting.h"
785 #include "gamesave.h"
786 #include "gamemine.h"
799 #include "cntrlcen.h"
800 #include "aistruct.h"
803 #include "controls.h"
807 #include "findfile.h"
810 #include "editor/editor.h"
814 #pragma global_optimizer off // pretty much sucks...need to look into this
817 void DoJasonInterpolate (fix recorded_time);
819 //#include "nocfile.h"
821 //Does demo start automatically?
824 sbyte WasRecorded [MAX_OBJECTS];
825 sbyte ViewWasRecorded[MAX_OBJECTS];
826 sbyte RenderingWasRecorded[32];
828 #define ND_EVENT_EOF 0 // EOF
829 #define ND_EVENT_START_DEMO 1 // Followed by 16 character, NULL terminated filename of .SAV file to use
830 #define ND_EVENT_START_FRAME 2 // Followed by integer frame number, then a fix FrameTime
831 #define ND_EVENT_VIEWER_OBJECT 3 // Followed by an object structure
832 #define ND_EVENT_RENDER_OBJECT 4 // Followed by an object structure
833 #define ND_EVENT_SOUND 5 // Followed by int soundum
834 #define ND_EVENT_SOUND_ONCE 6 // Followed by int soundum
835 #define ND_EVENT_SOUND_3D 7 // Followed by int soundum, int angle, int volume
836 #define ND_EVENT_WALL_HIT_PROCESS 8 // Followed by int segnum, int side, fix damage
837 #define ND_EVENT_TRIGGER 9 // Followed by int segnum, int side, int objnum
838 #define ND_EVENT_HOSTAGE_RESCUED 10 // Followed by int hostage_type
839 #define ND_EVENT_SOUND_3D_ONCE 11 // Followed by int soundum, int angle, int volume
840 #define ND_EVENT_MORPH_FRAME 12 // Followed by ? data
841 #define ND_EVENT_WALL_TOGGLE 13 // Followed by int seg, int side
842 #define ND_EVENT_HUD_MESSAGE 14 // Followed by char size, char * string (+null)
843 #define ND_EVENT_CONTROL_CENTER_DESTROYED 15 // Just a simple flag
844 #define ND_EVENT_PALETTE_EFFECT 16 // Followed by short r,g,b
845 #define ND_EVENT_PLAYER_ENERGY 17 // followed by byte energy
846 #define ND_EVENT_PLAYER_SHIELD 18 // followed by byte shields
847 #define ND_EVENT_PLAYER_FLAGS 19 // followed by player flags
848 #define ND_EVENT_PLAYER_WEAPON 20 // followed by weapon type and weapon number
849 #define ND_EVENT_EFFECT_BLOWUP 21 // followed by segment, side, and pnt
850 #define ND_EVENT_HOMING_DISTANCE 22 // followed by homing distance
851 #define ND_EVENT_LETTERBOX 23 // letterbox mode for death seq.
852 #define ND_EVENT_RESTORE_COCKPIT 24 // restore cockpit after death
853 #define ND_EVENT_REARVIEW 25 // going to rear view mode
854 #define ND_EVENT_WALL_SET_TMAP_NUM1 26 // Wall changed
855 #define ND_EVENT_WALL_SET_TMAP_NUM2 27 // Wall changed
856 #define ND_EVENT_NEW_LEVEL 28 // followed by level number
857 #define ND_EVENT_MULTI_CLOAK 29 // followed by player num
858 #define ND_EVENT_MULTI_DECLOAK 30 // followed by player num
859 #define ND_EVENT_RESTORE_REARVIEW 31 // restore cockpit after rearview mode
860 #define ND_EVENT_MULTI_DEATH 32 // with player number
861 #define ND_EVENT_MULTI_KILL 33 // with player number
862 #define ND_EVENT_MULTI_CONNECT 34 // with player number
863 #define ND_EVENT_MULTI_RECONNECT 35 // with player number
864 #define ND_EVENT_MULTI_DISCONNECT 36 // with player number
865 #define ND_EVENT_MULTI_SCORE 37 // playernum / score
866 #define ND_EVENT_PLAYER_SCORE 38 // followed by score
867 #define ND_EVENT_PRIMARY_AMMO 39 // with old/new ammo count
868 #define ND_EVENT_SECONDARY_AMMO 40 // with old/new ammo count
869 #define ND_EVENT_DOOR_OPENING 41 // with segment/side
870 #define ND_EVENT_LASER_LEVEL 42 // no data
871 #define ND_EVENT_PLAYER_AFTERBURNER 43 // followed by byte old ab, current ab
872 #define ND_EVENT_CLOAKING_WALL 44 // info changing while wall cloaking
873 #define ND_EVENT_CHANGE_COCKPIT 45 // change the cockpit
874 #define ND_EVENT_START_GUIDED 46 // switch to guided view
875 #define ND_EVENT_END_GUIDED 47 // stop guided view/return to ship
876 #define ND_EVENT_SECRET_THINGY 48 // 0/1 = secret exit functional/non-functional
877 #define ND_EVENT_LINK_SOUND_TO_OBJ 49 // record digi_link_sound_to_object3
878 #define ND_EVENT_KILL_SOUND_TO_OBJ 50 // record digi_kill_sound_linked_to_object
881 #define NORMAL_PLAYBACK 0
882 #define SKIP_PLAYBACK 1
883 #define INTERPOLATE_PLAYBACK 2
884 #define INTERPOL_FACTOR (F1_0 + (F1_0/5))
886 #define DEMO_VERSION 15 // last D1 version was 13
887 #define DEMO_GAME_TYPE 3 // 1 was shareware, 2 registered
889 #define DEMO_FILENAME DEMO_DIR "tmpdemo.dem"
891 #define DEMO_MAX_LEVELS 29
894 char nd_save_callsign[CALLSIGN_LEN+1];
895 int Newdemo_state = 0;
896 int Newdemo_vcr_state = 0;
897 int Newdemo_start_frame = -1;
898 unsigned int Newdemo_size;
899 int Newdemo_num_written;
900 int Newdemo_game_mode;
901 int Newdemo_old_cockpit;
902 sbyte Newdemo_no_space;
903 sbyte Newdemo_at_eof;
904 sbyte Newdemo_do_interpolate = 0; // 1
905 sbyte Newdemo_players_cloaked;
906 sbyte Newdemo_warning_given = 0;
907 sbyte Newdemo_cntrlcen_destroyed = 0;
908 static sbyte nd_bad_read;
909 int NewdemoFrameCount;
910 short frame_bytes_written = 0;
911 fix nd_playback_total;
912 fix nd_recorded_total;
913 fix nd_recorded_time;
914 sbyte playback_style;
915 sbyte First_time_playback=1;
916 fix JasonPlaybackTotal=0;
920 CFILE *outfile = NULL;
922 int newdemo_get_percent_done() {
923 if ( Newdemo_state == ND_STATE_PLAYBACK ) {
924 return (cftell(infile) * 100) / Newdemo_size;
926 if ( Newdemo_state == ND_STATE_RECORDING ) {
927 return cftell(outfile);
932 #define VEL_PRECISION 12
934 void my_extract_shortpos(object *objp, shortpos *spp)
940 objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
941 objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
942 objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
944 objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
945 objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
946 objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
948 objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
949 objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
950 objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
952 segnum = spp->segment;
953 objp->segnum = segnum;
955 objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x;
956 objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y;
957 objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z;
959 objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION);
960 objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION);
961 objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION);
964 int newdemo_read( void *buffer, int elsize, int nelem )
967 num_read = cfread(buffer, elsize, nelem, infile);
968 if (cferror(infile) || cfeof(infile))
974 int newdemo_find_object( int signature )
979 for (i=0; i<=Highest_object_index; i++, objp++ ) {
980 if ( (objp->type != OBJ_NONE) && (objp->signature == signature))
986 int newdemo_write( void *buffer, int elsize, int nelem )
988 int num_written, total_size;
990 total_size = elsize * nelem;
991 frame_bytes_written += total_size;
992 Newdemo_num_written += total_size;
993 Assert(outfile != NULL);
994 num_written = cfwrite(buffer, elsize, nelem, outfile);
995 //if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) {
996 // Newdemo_no_space=1;
997 // newdemo_stop_recording();
1000 if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space)
1002 if (num_written == nelem && !Newdemo_no_space)
1006 newdemo_stop_recording();
1011 * The next bunch of files taken from Matt's gamesave.c. We have to modify
1012 * these since the demo must save more information about objects that
1016 static void nd_write_byte(sbyte b)
1018 newdemo_write(&b, 1, 1);
1021 static void nd_write_short(short s)
1023 newdemo_write(&s, 2, 1);
1026 static void nd_write_int(int i)
1028 newdemo_write(&i, 4, 1);
1031 static void nd_write_string(char *str)
1033 nd_write_byte(strlen(str) + 1);
1034 newdemo_write(str, strlen(str) + 1, 1);
1037 static void nd_write_fix(fix f)
1039 newdemo_write(&f, sizeof(fix), 1);
1042 static void nd_write_fixang(fixang f)
1044 newdemo_write(&f, sizeof(fixang), 1);
1047 static void nd_write_vector(vms_vector *v)
1054 static void nd_write_angvec(vms_angvec *v)
1056 nd_write_fixang(v->p);
1057 nd_write_fixang(v->b);
1058 nd_write_fixang(v->h);
1061 void nd_write_shortpos(object *obj)
1067 create_shortpos(&sp, obj, 0);
1069 render_type = obj->render_type;
1070 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
1071 for (i = 0; i < 9; i++)
1072 nd_write_byte(sp.bytemat[i]);
1073 for (i = 0; i < 9; i++) {
1074 if (sp.bytemat[i] != 0)
1078 Int3(); // contact Allender about this.
1082 nd_write_short(sp.xo);
1083 nd_write_short(sp.yo);
1084 nd_write_short(sp.zo);
1085 nd_write_short(sp.segment);
1086 nd_write_short(sp.velx);
1087 nd_write_short(sp.vely);
1088 nd_write_short(sp.velz);
1091 static void nd_read_byte(sbyte *b)
1093 newdemo_read(b, 1, 1);
1096 static void nd_read_short(short *s)
1098 newdemo_read(s, 2, 1);
1101 static void nd_read_int(int *i)
1103 newdemo_read(i, 4, 1);
1106 static void nd_read_string(char *str)
1111 newdemo_read(str, len, 1);
1114 static void nd_read_fix(fix *f)
1116 newdemo_read(f, sizeof(fix), 1);
1119 static void nd_read_fixang(fixang *f)
1121 newdemo_read(f, sizeof(fixang), 1);
1124 static void nd_read_vector(vms_vector *v)
1126 nd_read_fix(&(v->x));
1127 nd_read_fix(&(v->y));
1128 nd_read_fix(&(v->z));
1131 static void nd_read_angvec(vms_angvec *v)
1133 nd_read_fixang(&(v->p));
1134 nd_read_fixang(&(v->b));
1135 nd_read_fixang(&(v->h));
1138 static void nd_read_shortpos(object *obj)
1144 render_type = obj->render_type;
1145 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
1146 for (i = 0; i < 9; i++)
1147 nd_read_byte(&(sp.bytemat[i]));
1150 nd_read_short(&(sp.xo));
1151 nd_read_short(&(sp.yo));
1152 nd_read_short(&(sp.zo));
1153 nd_read_short(&(sp.segment));
1154 nd_read_short(&(sp.velx));
1155 nd_read_short(&(sp.vely));
1156 nd_read_short(&(sp.velz));
1158 my_extract_shortpos(obj, &sp);
1159 if ((obj->id == VCLIP_MORPHING_ROBOT) && (render_type == RT_FIREBALL) && (obj->control_type == CT_EXPLOSION))
1160 extract_orient_from_segment(&obj->orient,&Segments[obj->segnum]);
1164 object *prev_obj=NULL; //ptr to last object read in
1166 void nd_read_object(object *obj)
1168 memset(obj, 0, sizeof(object));
1171 * Do render type first, since with render_type == RT_NONE, we
1172 * blow by all other object information
1174 nd_read_byte(&(obj->render_type));
1175 nd_read_byte(&(obj->type));
1176 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
1179 nd_read_byte(&(obj->id));
1180 nd_read_byte(&(obj->flags));
1181 nd_read_short((short *)&(obj->signature));
1182 nd_read_shortpos(obj);
1184 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
1187 obj->attached_obj = -1;
1192 obj->control_type = CT_POWERUP;
1193 obj->movement_type = MT_NONE;
1194 obj->size = HOSTAGE_SIZE;
1198 obj->control_type = CT_AI;
1199 // (MarkA and MikeK said we should not do the crazy last secret stuff with multiple reactors...
1200 // This necessary code is our vindication. --MK, 2/15/96)
1201 if (obj->id != SPECIAL_REACTOR_ROBOT)
1202 obj->movement_type = MT_PHYSICS;
1204 obj->movement_type = MT_NONE;
1205 obj->size = Polygon_models[Robot_info[obj->id].model_num].rad;
1206 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
1207 obj->rtype.pobj_info.subobj_flags = 0;
1208 obj->ctype.ai_info.CLOAKED = (Robot_info[obj->id].cloak_type?1:0);
1212 obj->control_type = CT_POWERUP;
1213 nd_read_byte(&(obj->movement_type)); // might have physics movement
1214 obj->size = Powerup_info[obj->id].size;
1218 obj->control_type = CT_NONE;
1219 obj->movement_type = MT_PHYSICS;
1220 obj->size = Polygon_models[Player_ship->model_num].rad;
1221 obj->rtype.pobj_info.model_num = Player_ship->model_num;
1222 obj->rtype.pobj_info.subobj_flags = 0;
1226 obj->control_type = CT_NONE;
1227 obj->movement_type = MT_NONE;
1228 obj->size = Polygon_models[obj->id].rad;
1229 obj->rtype.pobj_info.model_num = obj->id;
1230 obj->rtype.pobj_info.subobj_flags = 0;
1234 nd_read_byte(&(obj->control_type));
1235 nd_read_byte(&(obj->movement_type));
1236 nd_read_fix(&(obj->size));
1241 nd_read_vector(&(obj->last_pos));
1242 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
1243 nd_read_fix(&(obj->lifeleft));
1248 obj->lifeleft = (fix)b;
1249 // MWA old way -- won't work with big endian machines nd_read_byte((ubyte *)&(obj->lifeleft));
1250 obj->lifeleft = (fix)((int)obj->lifeleft << 12);
1253 if (obj->type == OBJ_ROBOT) {
1254 if (Robot_info[obj->id].boss_flag) {
1257 nd_read_byte(&cloaked);
1258 obj->ctype.ai_info.CLOAKED = cloaked;
1262 switch (obj->movement_type) {
1265 nd_read_vector(&(obj->mtype.phys_info.velocity));
1266 nd_read_vector(&(obj->mtype.phys_info.thrust));
1270 nd_read_vector(&(obj->mtype.spin_rate));
1280 switch (obj->control_type) {
1284 nd_read_fix(&(obj->ctype.expl_info.spawn_time));
1285 nd_read_fix(&(obj->ctype.expl_info.delete_time));
1286 nd_read_short(&(obj->ctype.expl_info.delete_objnum));
1288 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
1290 if (obj->flags & OF_ATTACHED) { //attach to previous object
1291 Assert(prev_obj!=NULL);
1292 if (prev_obj->control_type == CT_EXPLOSION) {
1293 if (prev_obj->flags & OF_ATTACHED && prev_obj->ctype.expl_info.attach_parent!=-1)
1294 obj_attach(&Objects[prev_obj->ctype.expl_info.attach_parent],obj);
1296 obj->flags &= ~OF_ATTACHED;
1299 obj_attach(prev_obj,obj);
1305 nd_read_fix(&(obj->ctype.light_info.intensity));
1327 switch (obj->render_type) {
1336 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
1337 nd_read_int(&(obj->rtype.pobj_info.model_num));
1338 nd_read_int(&(obj->rtype.pobj_info.subobj_flags));
1341 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
1343 for (i=0;i<MAX_SUBMODELS;i++)
1344 nd_read_angvec(&(obj->pobj_info.anim_angles[i]));
1346 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
1347 nd_read_angvec(&obj->rtype.pobj_info.anim_angles[i]);
1352 obj->rtype.pobj_info.tmap_override = tmo;
1355 obj->rtype.pobj_info.tmap_override = -1;
1357 int xlated_tmo = tmap_xlate_table[tmo];
1358 if (xlated_tmo < 0) {
1359 //mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->pobj_info.model_num));
1363 obj->rtype.pobj_info.tmap_override = xlated_tmo;
1371 case RT_WEAPON_VCLIP:
1374 nd_read_int(&(obj->rtype.vclip_info.vclip_num));
1375 nd_read_fix(&(obj->rtype.vclip_info.frametime));
1376 nd_read_byte(&(obj->rtype.vclip_info.framenum));
1390 void nd_write_object(object *obj)
1394 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
1398 * Do render_type first so on read, we can make determination of
1399 * what else to read in
1401 nd_write_byte(obj->render_type);
1402 nd_write_byte(obj->type);
1403 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
1406 nd_write_byte(obj->id);
1407 nd_write_byte(obj->flags);
1408 nd_write_short((short)obj->signature);
1409 nd_write_shortpos(obj);
1411 if ((obj->type != OBJ_HOSTAGE) && (obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_POWERUP) && (obj->type != OBJ_CLUTTER)) {
1412 nd_write_byte(obj->control_type);
1413 nd_write_byte(obj->movement_type);
1414 nd_write_fix(obj->size);
1416 if (obj->type == OBJ_POWERUP)
1417 nd_write_byte(obj->movement_type);
1419 nd_write_vector(&obj->last_pos);
1421 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
1422 nd_write_fix(obj->lifeleft);
1424 life = (int)obj->lifeleft;
1428 nd_write_byte((ubyte)life);
1431 if (obj->type == OBJ_ROBOT) {
1432 if (Robot_info[obj->id].boss_flag) {
1433 if ((GameTime > Boss_cloak_start_time) && (GameTime < Boss_cloak_end_time))
1440 switch (obj->movement_type) {
1443 nd_write_vector(&obj->mtype.phys_info.velocity);
1444 nd_write_vector(&obj->mtype.phys_info.thrust);
1448 nd_write_vector(&obj->mtype.spin_rate);
1458 switch (obj->control_type) {
1464 nd_write_fix(obj->ctype.expl_info.spawn_time);
1465 nd_write_fix(obj->ctype.expl_info.delete_time);
1466 nd_write_short(obj->ctype.expl_info.delete_objnum);
1474 nd_write_fix(obj->ctype.light_info.intensity);
1481 case CT_SLEW: //the player is generally saved as slew
1494 switch (obj->render_type) {
1503 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
1504 nd_write_int(obj->rtype.pobj_info.model_num);
1505 nd_write_int(obj->rtype.pobj_info.subobj_flags);
1508 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
1510 for (i=0;i<MAX_SUBMODELS;i++)
1511 nd_write_angvec(&obj->pobj_info.anim_angles[i]);
1513 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
1514 nd_write_angvec(&obj->rtype.pobj_info.anim_angles[i]);
1516 nd_write_int(obj->rtype.pobj_info.tmap_override);
1522 case RT_WEAPON_VCLIP:
1525 nd_write_int(obj->rtype.vclip_info.vclip_num);
1526 nd_write_fix(obj->rtype.vclip_info.frametime);
1527 nd_write_byte(obj->rtype.vclip_info.framenum);
1540 int JustStartedRecording=0,JustStartedPlayback=0;
1542 void newdemo_record_start_demo()
1547 nd_write_byte(ND_EVENT_START_DEMO);
1548 nd_write_byte(DEMO_VERSION);
1549 nd_write_byte(DEMO_GAME_TYPE);
1550 nd_write_fix(GameTime);
1553 if (Game_mode & GM_MULTI)
1554 nd_write_int(Game_mode | (Player_num << 16));
1557 // NOTE LINK TO ABOVE!!!
1558 nd_write_int(Game_mode);
1561 if (Game_mode & GM_TEAM) {
1562 nd_write_byte(Netgame.team_vector);
1563 nd_write_string(Netgame.team_name[0]);
1564 nd_write_string(Netgame.team_name[1]);
1567 if (Game_mode & GM_MULTI) {
1568 nd_write_byte((sbyte)N_players);
1569 for (i = 0; i < N_players; i++) {
1570 nd_write_string(Players[i].callsign);
1571 nd_write_byte(Players[i].connected);
1573 if (Game_mode & GM_MULTI_COOP) {
1574 nd_write_int(Players[i].score);
1576 nd_write_short((short)Players[i].net_killed_total);
1577 nd_write_short((short)Players[i].net_kills_total);
1582 // NOTE LINK TO ABOVE!!!
1583 nd_write_int(Players[Player_num].score);
1585 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
1586 nd_write_short((short)Players[Player_num].primary_ammo[i]);
1588 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
1589 nd_write_short((short)Players[Player_num].secondary_ammo[i]);
1591 nd_write_byte((sbyte)Players[Player_num].laser_level);
1593 // Support for missions added here
1595 nd_write_string(Current_mission_filename);
1597 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
1598 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
1599 nd_write_int(Players[Player_num].flags); // be sure players flags are set
1600 nd_write_byte((sbyte)Primary_weapon);
1601 nd_write_byte((sbyte)Secondary_weapon);
1602 Newdemo_start_frame = FrameCount;
1603 JustStartedRecording=1;
1605 newdemo_set_new_level(Current_level_num);
1610 void newdemo_record_start_frame(int frame_number, fix frame_time )
1614 if (Newdemo_no_space) {
1615 newdemo_stop_playback();
1621 for (i=0;i<MAX_OBJECTS;i++)
1624 ViewWasRecorded[i]=0;
1627 RenderingWasRecorded[i]=0;
1629 frame_number -= Newdemo_start_frame;
1631 Assert(frame_number >= 0 );
1633 nd_write_byte(ND_EVENT_START_FRAME);
1634 nd_write_short(frame_bytes_written - 1); // from previous frame
1635 frame_bytes_written=3;
1636 nd_write_int(frame_number);
1637 nd_write_int(frame_time);
1642 void newdemo_record_render_object(object * obj)
1644 if (ViewWasRecorded[obj-Objects])
1647 //if (obj==&Objects[Players[Player_num].objnum] && !Player_is_dead)
1651 nd_write_byte(ND_EVENT_RENDER_OBJECT);
1652 nd_write_object(obj);
1656 extern ubyte RenderingType;
1658 void newdemo_record_viewer_object(object * obj)
1661 if (ViewWasRecorded[obj-Objects] && (ViewWasRecorded[obj-Objects]-1)==RenderingType)
1663 //if (WasRecorded[obj-Objects])
1665 if (RenderingWasRecorded[RenderingType])
1668 ViewWasRecorded[obj-Objects]=RenderingType+1;
1669 RenderingWasRecorded[RenderingType]=1;
1671 nd_write_byte(ND_EVENT_VIEWER_OBJECT);
1672 nd_write_byte(RenderingType);
1673 nd_write_object(obj);
1677 void newdemo_record_sound( int soundno )
1680 nd_write_byte(ND_EVENT_SOUND);
1681 nd_write_int( soundno );
1685 //--unused-- void newdemo_record_sound_once( int soundno ) {
1686 //--unused-- stop_time();
1687 //--unused-- nd_write_byte( ND_EVENT_SOUND_ONCE );
1688 //--unused-- nd_write_int( soundno );
1689 //--unused-- start_time();
1693 void newdemo_record_cockpit_change (int mode)
1696 nd_write_byte (ND_EVENT_CHANGE_COCKPIT);
1702 void newdemo_record_sound_3d( int soundno, int angle, int volume )
1705 nd_write_byte( ND_EVENT_SOUND_3D );
1706 nd_write_int( soundno );
1707 nd_write_int( angle );
1708 nd_write_int( volume );
1712 void newdemo_record_sound_3d_once( int soundno, int angle, int volume )
1715 nd_write_byte( ND_EVENT_SOUND_3D_ONCE );
1716 nd_write_int( soundno );
1717 nd_write_int( angle );
1718 nd_write_int( volume );
1723 void newdemo_record_link_sound_to_object3( int soundno, short objnum, fix max_volume, fix max_distance, int loop_start, int loop_end )
1726 nd_write_byte( ND_EVENT_LINK_SOUND_TO_OBJ );
1727 nd_write_int( soundno );
1728 nd_write_int( Objects[objnum].signature );
1729 nd_write_int( max_volume );
1730 nd_write_int( max_distance );
1731 nd_write_int( loop_start );
1732 nd_write_int( loop_end );
1736 void newdemo_record_kill_sound_linked_to_object( int objnum )
1739 nd_write_byte( ND_EVENT_KILL_SOUND_TO_OBJ );
1740 nd_write_int( Objects[objnum].signature );
1745 void newdemo_record_wall_hit_process( int segnum, int side, int damage, int playernum )
1751 //playernum = playernum;
1752 nd_write_byte( ND_EVENT_WALL_HIT_PROCESS );
1753 nd_write_int( segnum );
1754 nd_write_int( side );
1755 nd_write_int( damage );
1756 nd_write_int( playernum );
1760 void newdemo_record_guided_start ()
1762 nd_write_byte (ND_EVENT_START_GUIDED);
1765 void newdemo_record_guided_end ()
1767 nd_write_byte (ND_EVENT_END_GUIDED);
1770 void newdemo_record_secret_exit_blown(int truth)
1773 nd_write_byte( ND_EVENT_SECRET_THINGY );
1774 nd_write_int( truth );
1778 void newdemo_record_trigger( int segnum, int side, int objnum,int shot )
1781 nd_write_byte( ND_EVENT_TRIGGER );
1782 nd_write_int( segnum );
1783 nd_write_int( side );
1784 nd_write_int( objnum );
1789 void newdemo_record_hostage_rescued( int hostage_number ) {
1791 nd_write_byte( ND_EVENT_HOSTAGE_RESCUED );
1792 nd_write_int( hostage_number );
1796 void newdemo_record_morph_frame(morph_data *md)
1800 nd_write_byte( ND_EVENT_MORPH_FRAME );
1802 newdemo_write( md->morph_vecs, sizeof(md->morph_vecs), 1 );
1803 newdemo_write( md->submodel_active, sizeof(md->submodel_active), 1 );
1804 newdemo_write( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 );
1806 nd_write_object( md->obj );
1810 void newdemo_record_wall_toggle( int segnum, int side )
1813 nd_write_byte( ND_EVENT_WALL_TOGGLE );
1814 nd_write_int( segnum );
1815 nd_write_int( side );
1819 void newdemo_record_control_center_destroyed()
1822 nd_write_byte( ND_EVENT_CONTROL_CENTER_DESTROYED );
1823 nd_write_int( Countdown_seconds_left );
1827 void newdemo_record_hud_message( char * message )
1830 nd_write_byte( ND_EVENT_HUD_MESSAGE );
1831 nd_write_string(message);
1835 void newdemo_record_palette_effect(short r, short g, short b )
1838 nd_write_byte( ND_EVENT_PALETTE_EFFECT );
1839 nd_write_short( r );
1840 nd_write_short( g );
1841 nd_write_short( b );
1845 void newdemo_record_player_energy(int old_energy, int energy)
1848 nd_write_byte( ND_EVENT_PLAYER_ENERGY );
1849 nd_write_byte((sbyte) old_energy);
1850 nd_write_byte((sbyte) energy);
1854 void newdemo_record_player_afterburner(fix old_afterburner, fix afterburner)
1857 nd_write_byte( ND_EVENT_PLAYER_AFTERBURNER );
1858 nd_write_byte((sbyte) (old_afterburner>>9));
1859 nd_write_byte((sbyte) (afterburner>>9));
1863 void newdemo_record_player_shields(int old_shield, int shield)
1866 nd_write_byte( ND_EVENT_PLAYER_SHIELD );
1867 nd_write_byte((sbyte)old_shield);
1868 nd_write_byte((sbyte)shield);
1872 void newdemo_record_player_flags(uint oflags, uint flags)
1875 nd_write_byte( ND_EVENT_PLAYER_FLAGS );
1876 nd_write_int(((short)oflags << 16) | (short)flags);
1880 void newdemo_record_player_weapon(int weapon_type, int weapon_num)
1883 nd_write_byte( ND_EVENT_PLAYER_WEAPON );
1884 nd_write_byte((sbyte)weapon_type);
1885 nd_write_byte((sbyte)weapon_num);
1887 nd_write_byte((sbyte)Secondary_weapon);
1889 nd_write_byte((sbyte)Primary_weapon);
1893 void newdemo_record_effect_blowup(short segment, int side, vms_vector *pnt)
1896 nd_write_byte (ND_EVENT_EFFECT_BLOWUP);
1897 nd_write_short(segment);
1898 nd_write_byte((sbyte)side);
1899 nd_write_vector(pnt);
1903 void newdemo_record_homing_distance(fix distance)
1906 nd_write_byte(ND_EVENT_HOMING_DISTANCE);
1907 nd_write_short((short)(distance>>16));
1911 void newdemo_record_letterbox(void)
1914 nd_write_byte(ND_EVENT_LETTERBOX);
1918 void newdemo_record_rearview(void)
1921 nd_write_byte(ND_EVENT_REARVIEW);
1925 void newdemo_record_restore_cockpit(void)
1928 nd_write_byte(ND_EVENT_RESTORE_COCKPIT);
1932 void newdemo_record_restore_rearview(void)
1935 nd_write_byte(ND_EVENT_RESTORE_REARVIEW);
1939 void newdemo_record_wall_set_tmap_num1(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1942 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM1);
1943 nd_write_short(seg);
1944 nd_write_byte(side);
1945 nd_write_short(cseg);
1946 nd_write_byte(cside);
1947 nd_write_short(tmap);
1951 void newdemo_record_wall_set_tmap_num2(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1954 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM2);
1955 nd_write_short(seg);
1956 nd_write_byte(side);
1957 nd_write_short(cseg);
1958 nd_write_byte(cside);
1959 nd_write_short(tmap);
1963 void newdemo_record_multi_cloak(int pnum)
1966 nd_write_byte(ND_EVENT_MULTI_CLOAK);
1967 nd_write_byte((sbyte)pnum);
1971 void newdemo_record_multi_decloak(int pnum)
1974 nd_write_byte(ND_EVENT_MULTI_DECLOAK);
1975 nd_write_byte((sbyte)pnum);
1979 void newdemo_record_multi_death(int pnum)
1982 nd_write_byte(ND_EVENT_MULTI_DEATH);
1983 nd_write_byte((sbyte)pnum);
1987 void newdemo_record_multi_kill(int pnum, sbyte kill)
1990 nd_write_byte(ND_EVENT_MULTI_KILL);
1991 nd_write_byte((sbyte)pnum);
1992 nd_write_byte(kill);
1996 void newdemo_record_multi_connect(int pnum, int new_player, char *new_callsign)
1999 nd_write_byte(ND_EVENT_MULTI_CONNECT);
2000 nd_write_byte((sbyte)pnum);
2001 nd_write_byte((sbyte)new_player);
2003 nd_write_string(Players[pnum].callsign);
2004 nd_write_int(Players[pnum].net_killed_total);
2005 nd_write_int(Players[pnum].net_kills_total);
2007 nd_write_string(new_callsign);
2011 void newdemo_record_multi_reconnect(int pnum)
2014 nd_write_byte(ND_EVENT_MULTI_RECONNECT);
2015 nd_write_byte((sbyte)pnum);
2019 void newdemo_record_multi_disconnect(int pnum)
2022 nd_write_byte(ND_EVENT_MULTI_DISCONNECT);
2023 nd_write_byte((sbyte)pnum);
2027 void newdemo_record_player_score(int score)
2030 nd_write_byte(ND_EVENT_PLAYER_SCORE);
2031 nd_write_int(score);
2035 void newdemo_record_multi_score(int pnum, int score)
2038 nd_write_byte(ND_EVENT_MULTI_SCORE);
2039 nd_write_byte((sbyte)pnum);
2040 nd_write_int(score - Players[pnum].score); // called before score is changed!!!!
2044 void newdemo_record_primary_ammo(int old_ammo, int new_ammo)
2047 nd_write_byte(ND_EVENT_PRIMARY_AMMO);
2049 nd_write_short((short)new_ammo);
2051 nd_write_short((short)old_ammo);
2052 nd_write_short((short)new_ammo);
2056 void newdemo_record_secondary_ammo(int old_ammo, int new_ammo)
2059 nd_write_byte(ND_EVENT_SECONDARY_AMMO);
2061 nd_write_short((short)new_ammo);
2063 nd_write_short((short)old_ammo);
2064 nd_write_short((short)new_ammo);
2068 void newdemo_record_door_opening(int segnum, int side)
2071 nd_write_byte(ND_EVENT_DOOR_OPENING);
2072 nd_write_short((short)segnum);
2073 nd_write_byte((sbyte)side);
2077 void newdemo_record_laser_level(sbyte old_level, sbyte new_level)
2080 nd_write_byte(ND_EVENT_LASER_LEVEL);
2081 nd_write_byte(old_level);
2082 nd_write_byte(new_level);
2086 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)
2088 Assert(front_wall_num <= 255 && back_wall_num <= 255);
2091 nd_write_byte(ND_EVENT_CLOAKING_WALL);
2092 nd_write_byte(front_wall_num);
2093 nd_write_byte(back_wall_num);
2094 nd_write_byte(type);
2095 nd_write_byte(state);
2096 nd_write_byte(cloak_value);
2097 nd_write_short(l0>>8);
2098 nd_write_short(l1>>8);
2099 nd_write_short(l2>>8);
2100 nd_write_short(l3>>8);
2104 void newdemo_set_new_level(int level_num)
2111 nd_write_byte(ND_EVENT_NEW_LEVEL);
2112 nd_write_byte((sbyte)level_num);
2113 nd_write_byte((sbyte)Current_level_num);
2115 if (JustStartedRecording==1)
2117 nd_write_int(Num_walls);
2118 for (i=0;i<Num_walls;i++)
2120 nd_write_byte (Walls[i].type);
2121 nd_write_byte (Walls[i].flags);
2122 nd_write_byte (Walls[i].state);
2124 seg = &Segments[Walls[i].segnum];
2125 side = Walls[i].sidenum;
2126 nd_write_short (seg->sides[side].tmap_num);
2127 nd_write_short (seg->sides[side].tmap_num2);
2128 JustStartedRecording=0;
2135 int newdemo_read_demo_start(int rnd_demo)
2137 sbyte i, version, game_type, laser_level;
2138 char c, energy, shield;
2139 char text[128], current_mission[9];
2142 if ((c != ND_EVENT_START_DEMO) || nd_bad_read) {
2145 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_CORRUPT);
2146 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2147 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2150 nd_read_byte(&version);
2151 nd_read_byte(&game_type);
2152 if (game_type < DEMO_GAME_TYPE) {
2155 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
2156 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2157 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Descent: First Strike";
2159 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2162 if (game_type != DEMO_GAME_TYPE) {
2165 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
2166 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2167 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Unknown Descent version";
2169 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2172 if (version < DEMO_VERSION) {
2175 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_OLD);
2176 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2177 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2181 nd_read_fix(&GameTime);
2182 Boss_cloak_start_time=Boss_cloak_end_time=GameTime;
2183 JasonPlaybackTotal=0;
2185 nd_read_int(&Newdemo_game_mode);
2188 change_playernum_to((Newdemo_game_mode >> 16) & 0x7);
2189 if (Newdemo_game_mode & GM_TEAM) {
2190 nd_read_byte(&(Netgame.team_vector));
2191 nd_read_string(Netgame.team_name[0]);
2192 nd_read_string(Netgame.team_name[1]);
2194 if (Newdemo_game_mode & GM_MULTI) {
2199 // changed this to above two lines -- breaks on the mac because of
2201 // nd_read_byte((sbyte *)&N_players);
2202 for (i = 0 ; i < N_players; i++) {
2203 Players[i].cloak_time = 0;
2204 Players[i].invulnerable_time = 0;
2205 nd_read_string(Players[i].callsign);
2206 nd_read_byte(&(Players[i].connected));
2208 if (Newdemo_game_mode & GM_MULTI_COOP) {
2209 nd_read_int(&(Players[i].score));
2211 nd_read_short((short *)&(Players[i].net_killed_total));
2212 nd_read_short((short *)&(Players[i].net_kills_total));
2215 Game_mode = Newdemo_game_mode;
2216 multi_sort_kill_list();
2217 Game_mode = GM_NORMAL;
2220 nd_read_int(&(Players[Player_num].score)); // Note link to above if!
2222 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
2223 nd_read_short((short*)&(Players[Player_num].primary_ammo[i]));
2225 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
2226 nd_read_short((short*)&(Players[Player_num].secondary_ammo[i]));
2228 nd_read_byte(&laser_level);
2229 if (laser_level != Players[Player_num].laser_level) {
2230 Players[Player_num].laser_level = laser_level;
2231 update_laser_weapon_info();
2234 // Support for missions
2236 nd_read_string(current_mission);
2237 if (!load_mission_by_name(current_mission)) {
2241 sprintf(text, TXT_NOMISSION4DEMO, current_mission);
2242 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
2243 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2248 nd_recorded_total = 0;
2249 nd_playback_total = 0;
2250 nd_read_byte(&energy);
2251 nd_read_byte(&shield);
2253 nd_read_int((int *)&(Players[Player_num].flags));
2254 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
2255 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2256 Newdemo_players_cloaked |= (1 << Player_num);
2258 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2259 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2261 nd_read_byte((sbyte *)&Primary_weapon);
2262 nd_read_byte((sbyte *)&Secondary_weapon);
2264 // Next bit of code to fix problem that I introduced between 1.0 and 1.1
2265 // check the next byte -- it _will_ be a load_new_level event. If it is
2266 // not, then we must shift all bytes up by one.
2268 Players[Player_num].energy = i2f(energy);
2269 Players[Player_num].shields = i2f(shield);
2270 JustStartedPlayback=1;
2274 void newdemo_pop_ctrlcen_triggers()
2278 segment *seg, *csegp;
2280 for (i = 0; i < ControlCenterTriggers.num_links; i++) {
2281 seg = &Segments[ControlCenterTriggers.seg[i]];
2282 side = ControlCenterTriggers.side[i];
2283 csegp = &Segments[seg->children[side]];
2284 cside = find_connect_side(seg, csegp);
2285 anim_num = Walls[seg->sides[side].wall_num].clip_num;
2286 n = WallAnims[anim_num].num_frames;
2287 if (WallAnims[anim_num].flags & WCF_TMAP1) {
2288 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[n-1];
2290 seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[n-1];
2295 #define N_PLAYER_SHIP_TEXTURES 6
2297 void nd_render_extras (ubyte,object *);
2298 extern void multi_apply_goal_textures ();
2299 ubyte Newdemo_flying_guided=0;
2301 int newdemo_read_frame_information()
2303 int done, segnum, side, objnum, soundno, angle, volume, i,shot;
2305 ubyte c,WhichWindow;
2306 static sbyte saved_letter_cockpit;
2307 static sbyte saved_rearview_cockpit;
2309 static char LastReadValue=101;
2314 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2315 for (segnum=0; segnum <= Highest_segment_index; segnum++)
2316 Segments[segnum].objects = -1;
2319 Players[Player_num].homing_object_dist = -F1_0;
2325 if (nd_bad_read) { done = -1; break; }
2329 case ND_EVENT_START_FRAME: { // Followed by an integer frame number, then a fix FrameTime
2330 short last_frame_length;
2333 nd_read_short(&last_frame_length);
2334 nd_read_int(&NewdemoFrameCount);
2335 nd_read_int((int *)&nd_recorded_time);
2336 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2337 nd_recorded_total += nd_recorded_time;
2338 NewdemoFrameCount--;
2340 if (nd_bad_read) { done = -1; break; }
2344 case ND_EVENT_VIEWER_OBJECT: // Followed by an object structure
2345 nd_read_byte (&WhichWindow);
2348 //mprintf ((0,"Reading extra!\n"));
2349 nd_read_object (&extraobj);
2350 if (Newdemo_vcr_state!=ND_STATE_PAUSED)
2352 if (nd_bad_read) { done = -1; break; }
2354 nd_render_extras (WhichWindow,&extraobj);
2359 //mprintf ((0,"Reading viewer!\n"));
2360 //Viewer=&Objects[0];
2361 nd_read_object(Viewer);
2363 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2364 if (nd_bad_read) { done = -1; break; }
2365 segnum = Viewer->segnum;
2366 Viewer->next = Viewer->prev = Viewer->segnum = -1;
2368 // HACK HACK HACK -- since we have multiple level recording, it can be the case
2369 // HACK HACK HACK -- that when rewinding the demo, the viewer is in a segment
2370 // HACK HACK HACK -- that is greater than the highest index of segments. Bash
2371 // HACK HACK HACK -- the viewer to segment 0 for bogus view.
2373 if (segnum > Highest_segment_index)
2375 obj_link(Viewer-Objects,segnum);
2380 case ND_EVENT_RENDER_OBJECT: // Followed by an object structure
2381 objnum = obj_allocate();
2384 obj = &Objects[objnum];
2385 nd_read_object(obj);
2386 if (nd_bad_read) { done = -1; break; }
2387 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2388 segnum = obj->segnum;
2389 obj->next = obj->prev = obj->segnum = -1;
2391 // HACK HACK HACK -- don't render objects is segments greater than Highest_segment_index
2392 // HACK HACK HACK -- (see above)
2394 if (segnum > Highest_segment_index)
2397 obj_link(obj-Objects,segnum);
2399 if ((obj->type == OBJ_PLAYER) && (Newdemo_game_mode & GM_MULTI)) {
2402 if (Newdemo_game_mode & GM_TEAM)
2403 player = get_team(obj->id);
2410 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
2411 multi_player_textures[player][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[obj->rtype.pobj_info.model_num].first_texture+i]];
2413 multi_player_textures[player][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2]];
2414 multi_player_textures[player][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2+1]];
2415 obj->rtype.pobj_info.alt_textures = player+1;
2421 case ND_EVENT_SOUND:
2422 nd_read_int(&soundno);
2423 if (nd_bad_read) {done = -1; break; }
2424 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2425 digi_play_sample( soundno, F1_0 );
2428 //--unused case ND_EVENT_SOUND_ONCE:
2429 //--unused nd_read_int(&soundno);
2430 //--unused if (nd_bad_read) { done = -1; break; }
2431 //--unused if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2432 //--unused digi_play_sample_once( soundno, F1_0 );
2435 case ND_EVENT_SOUND_3D:
2436 nd_read_int(&soundno);
2437 nd_read_int(&angle);
2438 nd_read_int(&volume);
2439 if (nd_bad_read) { done = -1; break; }
2440 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2441 digi_play_sample_3d( soundno, angle, volume, 0 );
2444 case ND_EVENT_SOUND_3D_ONCE:
2445 nd_read_int(&soundno);
2446 nd_read_int(&angle);
2447 nd_read_int(&volume);
2448 if (nd_bad_read) { done = -1; break; }
2449 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2450 digi_play_sample_3d( soundno, angle, volume, 1 );
2453 case ND_EVENT_LINK_SOUND_TO_OBJ:
2455 int soundno, objnum, max_volume, max_distance, loop_start, loop_end;
2457 nd_read_int( &soundno );
2458 nd_read_int( &signature );
2459 nd_read_int( &max_volume );
2460 nd_read_int( &max_distance );
2461 nd_read_int( &loop_start );
2462 nd_read_int( &loop_end );
2463 objnum = newdemo_find_object( signature );
2464 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
2465 digi_link_sound_to_object3( soundno, objnum, 1, max_volume, max_distance, loop_start, loop_end );
2470 case ND_EVENT_KILL_SOUND_TO_OBJ:
2472 int objnum, signature;
2473 nd_read_int( &signature );
2474 objnum = newdemo_find_object( signature );
2475 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
2476 digi_kill_sound_linked_to_object(objnum);
2481 case ND_EVENT_WALL_HIT_PROCESS: {
2485 nd_read_int(&segnum);
2487 nd_read_fix(&damage);
2488 nd_read_int(&player);
2489 if (nd_bad_read) { done = -1; break; }
2490 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2491 wall_hit_process(&Segments[segnum], side, damage, player, &(Objects[0]) );
2495 case ND_EVENT_TRIGGER:
2496 nd_read_int(&segnum);
2498 nd_read_int(&objnum);
2500 if (nd_bad_read) { done = -1; break; }
2501 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2503 mprintf ((0,"EVENT TRIGGER! shot=%d\n",shot));
2505 if (Triggers[Walls[Segments[segnum].sides[side].wall_num].trigger].type == TT_SECRET_EXIT) {
2509 Assert(c == ND_EVENT_SECRET_THINGY);
2510 nd_read_int(&truth);
2512 check_trigger(&Segments[segnum], side, objnum,shot);
2514 check_trigger(&Segments[segnum], side, objnum,shot);
2518 case ND_EVENT_HOSTAGE_RESCUED: {
2521 nd_read_int(&hostage_number);
2522 if (nd_bad_read) { done = -1; break; }
2523 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2524 hostage_rescue( hostage_number );
2528 case ND_EVENT_MORPH_FRAME: {
2532 md = &morph_objects[0];
2533 if (newdemo_read( md->morph_vecs, sizeof(md->morph_vecs), 1 )!=1) { done=-1; break; }
2534 if (newdemo_read( md->submodel_active, sizeof(md->submodel_active), 1 )!=1) { done=-1; break; }
2535 if (newdemo_read( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 )!=1) { done=-1; break; }
2537 objnum = obj_allocate();
2540 obj = &Objects[objnum];
2541 nd_read_object(obj);
2542 obj->render_type = RT_POLYOBJ;
2543 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2544 if (nd_bad_read) { done = -1; break; }
2545 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
2546 segnum = obj->segnum;
2547 obj->next = obj->prev = obj->segnum = -1;
2548 obj_link(obj-Objects,segnum);
2554 case ND_EVENT_WALL_TOGGLE:
2555 nd_read_int(&segnum);
2557 if (nd_bad_read) {done = -1; break; }
2558 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2559 wall_toggle(&Segments[segnum], side);
2562 case ND_EVENT_CONTROL_CENTER_DESTROYED:
2563 nd_read_int(&Countdown_seconds_left);
2564 Control_center_destroyed = 1;
2565 if (nd_bad_read) { done = -1; break; }
2566 if (!Newdemo_cntrlcen_destroyed) {
2567 newdemo_pop_ctrlcen_triggers();
2568 Newdemo_cntrlcen_destroyed = 1;
2569 //do_controlcen_destroyed_stuff(NULL);
2573 case ND_EVENT_HUD_MESSAGE: {
2576 nd_read_string(&(hud_msg[0]));
2577 if (nd_bad_read) { done = -1; break; }
2578 HUD_init_message( hud_msg );
2581 case ND_EVENT_START_GUIDED:
2582 Newdemo_flying_guided=1;
2583 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2584 Newdemo_flying_guided=0;
2586 case ND_EVENT_END_GUIDED:
2587 Newdemo_flying_guided=0;
2588 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2589 Newdemo_flying_guided=1;
2592 case ND_EVENT_PALETTE_EFFECT: {
2598 if (nd_bad_read) { done = -1; break; }
2599 PALETTE_FLASH_SET(r,g,b);
2603 case ND_EVENT_PLAYER_ENERGY: {
2607 nd_read_byte(&old_energy);
2608 nd_read_byte(&energy);
2609 if (nd_bad_read) {done = -1; break; }
2610 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2611 Players[Player_num].energy = i2f(energy);
2612 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2613 if (old_energy != 255)
2614 Players[Player_num].energy = i2f(old_energy);
2619 case ND_EVENT_PLAYER_AFTERBURNER: {
2621 ubyte old_afterburner;
2623 nd_read_byte(&old_afterburner);
2624 nd_read_byte(&afterburner);
2625 if (nd_bad_read) {done = -1; break; }
2626 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2627 Afterburner_charge = afterburner<<9;
2628 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2629 if (old_afterburner != 255)
2630 Afterburner_charge = old_afterburner<<9;
2635 case ND_EVENT_PLAYER_SHIELD: {
2639 nd_read_byte(&old_shield);
2640 nd_read_byte(&shield);
2641 if (nd_bad_read) {done = -1; break; }
2642 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2643 Players[Player_num].shields = i2f(shield);
2644 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2645 if (old_shield != 255)
2646 Players[Player_num].shields = i2f(old_shield);
2651 case ND_EVENT_PLAYER_FLAGS: {
2654 nd_read_int((int *)&(Players[Player_num].flags));
2655 if (nd_bad_read) {done = -1; break; }
2657 oflags = Players[Player_num].flags >> 16;
2658 Players[Player_num].flags &= 0xffff;
2660 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || ((Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) && (oflags != 0xffff)) ) {
2661 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2662 Players[Player_num].cloak_time = 0;
2663 Newdemo_players_cloaked &= ~(1 << Player_num);
2665 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2666 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2667 Newdemo_players_cloaked |= (1 << Player_num);
2669 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2670 Players[Player_num].invulnerable_time = 0;
2671 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2672 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2673 Players[Player_num].flags = oflags;
2674 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2675 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2676 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2677 Newdemo_players_cloaked |= (1 << Player_num);
2679 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
2680 Players[Player_num].cloak_time = 0;
2681 Newdemo_players_cloaked &= ~(1 << Player_num);
2683 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2684 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2685 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2686 Players[Player_num].invulnerable_time = 0;
2688 update_laser_weapon_info(); // in case of quad laser change
2692 case ND_EVENT_PLAYER_WEAPON: {
2693 sbyte weapon_type, weapon_num;
2696 nd_read_byte(&weapon_type);
2697 nd_read_byte(&weapon_num);
2698 nd_read_byte(&old_weapon);
2699 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2700 if (weapon_type == 0)
2701 Primary_weapon = (int)weapon_num;
2703 Secondary_weapon = (int)weapon_num;
2704 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2705 if (weapon_type == 0)
2706 Primary_weapon = (int)old_weapon;
2708 Secondary_weapon = (int)old_weapon;
2713 case ND_EVENT_EFFECT_BLOWUP: {
2719 //create a dummy object which will be the weapon that hits
2720 //the monitor. the blowup code wants to know who the parent of the
2721 //laser is, so create a laser whose parent is the player
2722 dummy.ctype.laser_info.parent_type = OBJ_PLAYER;
2724 nd_read_short(&segnum);
2725 nd_read_byte(&side);
2726 nd_read_vector(&pnt);
2727 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2728 check_effect_blowup(&(Segments[segnum]), side, &pnt, &dummy, 0);
2732 case ND_EVENT_HOMING_DISTANCE: {
2735 nd_read_short(&distance);
2736 Players[Player_num].homing_object_dist = i2f((int)(distance << 16));
2740 case ND_EVENT_LETTERBOX:
2741 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2742 saved_letter_cockpit = Cockpit_mode;
2743 select_cockpit(CM_LETTERBOX);
2744 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2745 select_cockpit(saved_letter_cockpit);
2748 case ND_EVENT_CHANGE_COCKPIT: {
2751 nd_read_int (&dummy);
2752 select_cockpit (dummy);
2756 case ND_EVENT_REARVIEW:
2757 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2758 saved_rearview_cockpit = Cockpit_mode;
2759 if (Cockpit_mode == CM_FULL_COCKPIT)
2760 select_cockpit(CM_REAR_VIEW);
2762 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2763 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2764 saved_rearview_cockpit = CM_FULL_COCKPIT;
2765 select_cockpit(saved_rearview_cockpit);
2770 case ND_EVENT_RESTORE_COCKPIT:
2771 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2772 saved_letter_cockpit = Cockpit_mode;
2773 select_cockpit(CM_LETTERBOX);
2774 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2775 select_cockpit(saved_letter_cockpit);
2779 case ND_EVENT_RESTORE_REARVIEW:
2780 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2781 saved_rearview_cockpit = Cockpit_mode;
2782 if (Cockpit_mode == CM_FULL_COCKPIT)
2783 select_cockpit(CM_REAR_VIEW);
2785 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2786 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2787 saved_rearview_cockpit = CM_FULL_COCKPIT;
2788 select_cockpit(saved_rearview_cockpit);
2794 case ND_EVENT_WALL_SET_TMAP_NUM1: {
2795 short seg, cseg, tmap;
2798 nd_read_short(&seg);
2799 nd_read_byte(&side);
2800 nd_read_short(&cseg);
2801 nd_read_byte(&cside);
2802 nd_read_short( &tmap );
2803 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD))
2804 Segments[seg].sides[side].tmap_num = Segments[cseg].sides[cside].tmap_num = tmap;
2808 case ND_EVENT_WALL_SET_TMAP_NUM2: {
2809 short seg, cseg, tmap;
2812 nd_read_short(&seg);
2813 nd_read_byte(&side);
2814 nd_read_short(&cseg);
2815 nd_read_byte(&cside);
2816 nd_read_short( &tmap );
2817 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) {
2818 Assert(tmap!=0 && Segments[seg].sides[side].tmap_num2!=0);
2819 Segments[seg].sides[side].tmap_num2 = Segments[cseg].sides[cside].tmap_num2 = tmap;
2824 case ND_EVENT_MULTI_CLOAK: {
2827 nd_read_byte(&pnum);
2828 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2829 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2830 Players[pnum].cloak_time = 0;
2831 Newdemo_players_cloaked &= ~(1 << pnum);
2832 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2833 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2834 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2835 Newdemo_players_cloaked |= (1 << pnum);
2840 case ND_EVENT_MULTI_DECLOAK: {
2843 nd_read_byte(&pnum);
2845 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2846 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2847 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2848 Newdemo_players_cloaked |= (1 << pnum);
2849 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2850 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2851 Players[pnum].cloak_time = 0;
2852 Newdemo_players_cloaked &= ~(1 << pnum);
2857 case ND_EVENT_MULTI_DEATH: {
2860 nd_read_byte(&pnum);
2861 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2862 Players[pnum].net_killed_total--;
2863 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2864 Players[pnum].net_killed_total++;
2869 case ND_EVENT_MULTI_KILL: {
2872 nd_read_byte(&pnum);
2873 nd_read_byte(&kill);
2874 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2875 Players[pnum].net_kills_total -= kill;
2876 if (Newdemo_game_mode & GM_TEAM)
2877 team_kills[get_team(pnum)] -= kill;
2878 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2879 Players[pnum].net_kills_total += kill;
2880 if (Newdemo_game_mode & GM_TEAM)
2881 team_kills[get_team(pnum)] += kill;
2883 Game_mode = Newdemo_game_mode;
2884 multi_sort_kill_list();
2885 Game_mode = GM_NORMAL;
2889 case ND_EVENT_MULTI_CONNECT: {
2890 sbyte pnum, new_player;
2891 int killed_total, kills_total;
2892 char new_callsign[CALLSIGN_LEN+1], old_callsign[CALLSIGN_LEN+1];
2894 nd_read_byte(&pnum);
2895 nd_read_byte(&new_player);
2897 nd_read_string(old_callsign);
2898 nd_read_int(&killed_total);
2899 nd_read_int(&kills_total);
2901 nd_read_string(new_callsign);
2902 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2903 Players[pnum].connected = 0;
2905 memcpy(Players[pnum].callsign, old_callsign, CALLSIGN_LEN+1);
2906 Players[pnum].net_killed_total = killed_total;
2907 Players[pnum].net_kills_total = kills_total;
2911 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2912 Players[pnum].connected = 1;
2913 Players[pnum].net_kills_total = 0;
2914 Players[pnum].net_killed_total = 0;
2915 memcpy(Players[pnum].callsign, new_callsign, CALLSIGN_LEN+1);
2922 case ND_EVENT_MULTI_RECONNECT: {
2925 nd_read_byte(&pnum);
2926 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2927 Players[pnum].connected = 0;
2928 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2929 Players[pnum].connected = 1;
2933 case ND_EVENT_MULTI_DISCONNECT: {
2936 nd_read_byte(&pnum);
2937 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2938 Players[pnum].connected = 1;
2939 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2940 Players[pnum].connected = 0;
2944 case ND_EVENT_MULTI_SCORE: {
2948 nd_read_byte(&pnum);
2949 nd_read_int(&score);
2950 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2951 Players[pnum].score -= score;
2952 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2953 Players[pnum].score += score;
2954 Game_mode = Newdemo_game_mode;
2955 multi_sort_kill_list();
2956 Game_mode = GM_NORMAL;
2961 case ND_EVENT_PLAYER_SCORE: {
2964 nd_read_int(&score);
2965 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2966 Players[Player_num].score -= score;
2967 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2968 Players[Player_num].score += score;
2973 case ND_EVENT_PRIMARY_AMMO: {
2974 short old_ammo, new_ammo;
2976 nd_read_short(&old_ammo);
2977 nd_read_short(&new_ammo);
2979 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2980 Players[Player_num].primary_ammo[Primary_weapon] = old_ammo;
2981 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2982 Players[Player_num].primary_ammo[Primary_weapon] = new_ammo;
2986 case ND_EVENT_SECONDARY_AMMO: {
2987 short old_ammo, new_ammo;
2989 nd_read_short(&old_ammo);
2990 nd_read_short(&new_ammo);
2992 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2993 Players[Player_num].secondary_ammo[Secondary_weapon] = old_ammo;
2994 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2995 Players[Player_num].secondary_ammo[Secondary_weapon] = new_ammo;
2999 case ND_EVENT_DOOR_OPENING: {
3003 nd_read_short(&segnum);
3004 nd_read_byte(&side);
3005 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
3008 segment *segp, *csegp;
3010 segp = &Segments[segnum];
3011 csegp = &Segments[segp->children[side]];
3012 cside = find_connect_side(segp, csegp);
3013 anim_num = Walls[segp->sides[side].wall_num].clip_num;
3015 if (WallAnims[anim_num].flags & WCF_TMAP1) {
3016 segp->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[0];
3018 segp->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[0];
3024 case ND_EVENT_LASER_LEVEL: {
3025 sbyte old_level, new_level;
3027 nd_read_byte(&old_level);
3028 nd_read_byte(&new_level);
3029 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
3030 Players[Player_num].laser_level = old_level;
3031 update_laser_weapon_info();
3032 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
3033 Players[Player_num].laser_level = new_level;
3034 update_laser_weapon_info();
3039 case ND_EVENT_CLOAKING_WALL: {
3040 ubyte back_wall_num,front_wall_num,type,state,cloak_value;
3045 nd_read_byte(&front_wall_num);
3046 nd_read_byte(&back_wall_num);
3047 nd_read_byte(&type);
3048 nd_read_byte(&state);
3049 nd_read_byte(&cloak_value);
3055 Walls[front_wall_num].type = type;
3056 Walls[front_wall_num].state = state;
3057 Walls[front_wall_num].cloak_value = cloak_value;
3058 segp = &Segments[Walls[front_wall_num].segnum];
3059 sidenum = Walls[front_wall_num].sidenum;
3060 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
3061 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
3062 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
3063 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
3065 Walls[back_wall_num].type = type;
3066 Walls[back_wall_num].state = state;
3067 Walls[back_wall_num].cloak_value = cloak_value;
3068 segp = &Segments[Walls[back_wall_num].segnum];
3069 sidenum = Walls[back_wall_num].sidenum;
3070 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
3071 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
3072 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
3073 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
3078 case ND_EVENT_NEW_LEVEL: {
3079 sbyte new_level, old_level, loaded_level;
3081 nd_read_byte (&new_level);
3082 nd_read_byte (&old_level);
3083 if (Newdemo_vcr_state == ND_STATE_PAUSED)
3087 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
3088 loaded_level = old_level;
3090 loaded_level = new_level;
3091 for (i = 0; i < MAX_PLAYERS; i++) {
3092 Players[i].cloak_time = 0;
3093 Players[i].flags &= ~PLAYER_FLAGS_CLOAKED;
3096 if ((loaded_level < Last_secret_level) || (loaded_level > Last_level)) {
3099 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
3100 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
3101 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
3102 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
3106 LoadLevel((int)loaded_level,1);
3107 Newdemo_cntrlcen_destroyed = 0;
3109 if (JustStartedPlayback)
3111 nd_read_int (&Num_walls);
3112 for (i=0;i<Num_walls;i++) // restore the walls
3114 nd_read_byte (&Walls[i].type);
3115 nd_read_byte (&Walls[i].flags);
3116 nd_read_byte (&Walls[i].state);
3118 seg = &Segments[Walls[i].segnum];
3119 side = Walls[i].sidenum;
3120 nd_read_short (&seg->sides[side].tmap_num);
3121 nd_read_short (&seg->sides[side].tmap_num2);
3124 if (Newdemo_game_mode & GM_CAPTURE)
3125 multi_apply_goal_textures ();
3127 JustStartedPlayback=0;
3131 // so says Rob H.!!! if (Newdemo_game_mode & GM_MULTI) {
3132 // so says Rob H.!!! for (i = 0; i < Num_walls; i++) {
3133 // so says Rob H.!!! if (Walls[i].type == WALL_BLASTABLE)
3134 // so says Rob H.!!! {
3135 // so says Rob H.!!! int a, n;
3136 // so says Rob H.!!! int side;
3137 // so says Rob H.!!! segment *seg;
3138 // so says Rob H.!!!
3139 // so says Rob H.!!! seg = &Segments[Walls[i].segnum];
3140 // so says Rob H.!!! side = Walls[i].sidenum;
3141 // so says Rob H.!!! a = Walls[i].clip_num;
3142 // so says Rob H.!!! n = WallAnims[a].num_frames;
3143 // so says Rob H.!!! seg->sides[side].tmap_num = WallAnims[a].frames[n-1];
3144 // so says Rob H.!!! Walls[i].flags |= WALL_BLASTED;
3145 // so says Rob H.!!! }
3146 // so says Rob H.!!! }
3147 // so says Rob H.!!! }
3149 reset_palette_add(); // get palette back to normal
3154 case ND_EVENT_EOF: {
3156 cfseek(infile, -1, SEEK_CUR); // get back to the EOF marker
3158 NewdemoFrameCount++;
3172 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_ERR_READING;
3173 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_DEMO_OLD_CORRUPT;
3174 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
3180 void newdemo_goto_beginning()
3182 //if (NewdemoFrameCount == 0)
3184 cfseek(infile, 0, SEEK_SET);
3185 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3186 if (newdemo_read_demo_start(0))
3187 newdemo_stop_playback();
3188 if (newdemo_read_frame_information() == -1)
3189 newdemo_stop_playback();
3190 if (newdemo_read_frame_information() == -1)
3191 newdemo_stop_playback();
3192 Newdemo_vcr_state = ND_STATE_PAUSED;
3196 void newdemo_goto_end()
3198 short frame_length, byte_count, bshort;
3199 sbyte level, bbyte, laser_level;
3200 ubyte energy, shield, c;
3203 cfseek(infile, -2, SEEK_END);
3204 nd_read_byte(&level);
3206 if ((level < Last_secret_level) || (level > Last_level)) {
3209 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
3210 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
3211 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
3212 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
3213 newdemo_stop_playback();
3216 if (level != Current_level_num)
3219 cfseek(infile, -4, SEEK_END);
3220 nd_read_short(&byte_count);
3221 cfseek(infile, -2 - byte_count, SEEK_CUR);
3223 nd_read_short(&frame_length);
3224 loc = cftell(infile);
3225 if (Newdemo_game_mode & GM_MULTI)
3226 nd_read_byte(&Newdemo_players_cloaked);
3228 nd_read_byte(&bbyte);
3229 nd_read_byte(&bbyte);
3230 nd_read_short(&bshort);
3233 nd_read_byte(&energy);
3234 nd_read_byte(&shield);
3235 Players[Player_num].energy = i2f(energy);
3236 Players[Player_num].shields = i2f(shield);
3237 nd_read_int((int *)&(Players[Player_num].flags));
3238 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
3239 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
3240 Newdemo_players_cloaked |= (1 << Player_num);
3242 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
3243 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
3244 nd_read_byte((sbyte *)&Primary_weapon);
3245 nd_read_byte((sbyte *)&Secondary_weapon);
3246 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
3247 nd_read_short((short *)&(Players[Player_num].primary_ammo[i]));
3248 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
3249 nd_read_short((short *)&(Players[Player_num].secondary_ammo[i]));
3250 nd_read_byte(&laser_level);
3251 if (laser_level != Players[Player_num].laser_level) {
3252 Players[Player_num].laser_level = laser_level;
3253 update_laser_weapon_info();
3256 if (Newdemo_game_mode & GM_MULTI) {
3259 // see newdemo_read_start_demo for explanation of
3260 // why this is commented out
3261 // nd_read_byte((sbyte *)&N_players);
3262 for (i = 0; i < N_players; i++) {
3263 nd_read_string(Players[i].callsign);
3264 nd_read_byte(&(Players[i].connected));
3265 if (Newdemo_game_mode & GM_MULTI_COOP) {
3266 nd_read_int(&(Players[i].score));
3268 nd_read_short((short *)&(Players[i].net_killed_total));
3269 nd_read_short((short *)&(Players[i].net_kills_total));
3273 nd_read_int(&(Players[Player_num].score));
3276 cfseek(infile, loc, SEEK_SET);
3277 cfseek(infile, -frame_length, SEEK_CUR);
3278 nd_read_int(&NewdemoFrameCount); // get the frame count
3279 NewdemoFrameCount--;
3280 cfseek(infile, 4, SEEK_CUR);
3281 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3282 newdemo_read_frame_information(); // then the frame information
3283 Newdemo_vcr_state = ND_STATE_PAUSED;
3287 void newdemo_back_frames(int frames)
3289 short last_frame_length;
3292 for (i = 0; i < frames; i++)
3294 cfseek(infile, -10, SEEK_CUR);
3295 nd_read_short(&last_frame_length);
3296 cfseek(infile, 8 - last_frame_length, SEEK_CUR);
3298 if (!Newdemo_at_eof && newdemo_read_frame_information() == -1) {
3299 newdemo_stop_playback();
3305 cfseek(infile, -10, SEEK_CUR);
3306 nd_read_short(&last_frame_length);
3307 cfseek(infile, 8 - last_frame_length, SEEK_CUR);
3313 * routine to interpolate the viewer position. the current position is
3314 * stored in the Viewer object. Save this position, and read the next
3315 * frame to get all objects read in. Calculate the delta playback and
3316 * the delta recording frame times between the two frames, then intepolate
3317 * the viewers position accordingly. nd_recorded_time is the time that it
3318 * took the recording to render the frame that we are currently looking
3322 void interpolate_frame(fix d_play, fix d_recorded)
3324 int i, j, num_cur_objs;
3328 factor = fixdiv(d_play, d_recorded);
3332 num_cur_objs = Highest_object_index;
3333 cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1));
3334 if (cur_objs == NULL) {
3335 mprintf((0,"Couldn't get %d bytes for cur_objs in interpolate_frame\n", sizeof(object) * num_cur_objs));
3339 for (i = 0; i <= num_cur_objs; i++)
3340 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
3342 Newdemo_vcr_state = ND_STATE_PAUSED;
3343 if (newdemo_read_frame_information() == -1) {
3345 newdemo_stop_playback();
3349 for (i = 0; i <= num_cur_objs; i++) {
3350 for (j = 0; j <= Highest_object_index; j++) {
3351 if (cur_objs[i].signature == Objects[j].signature) {
3352 ubyte render_type = cur_objs[i].render_type;
3353 //fix delta_p, delta_h, delta_b;
3354 fix delta_x, delta_y, delta_z;
3355 //vms_angvec cur_angles, dest_angles;
3357 // Extract the angles from the object orientation matrix.
3358 // Some of this code taken from ai_turn_towards_vector
3359 // Don't do the interpolation on certain render types which don't use an orientation matrix
3361 if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) {
3363 vms_vector fvec1, fvec2, rvec1, rvec2;
3366 fvec1 = cur_objs[i].orient.fvec;
3367 vm_vec_scale(&fvec1, F1_0-factor);
3368 fvec2 = Objects[j].orient.fvec;
3369 vm_vec_scale(&fvec2, factor);
3370 vm_vec_add2(&fvec1, &fvec2);
3371 mag1 = vm_vec_normalize_quick(&fvec1);
3372 if (mag1 > F1_0/256) {
3373 rvec1 = cur_objs[i].orient.rvec;
3374 vm_vec_scale(&rvec1, F1_0-factor);
3375 rvec2 = Objects[j].orient.rvec;
3376 vm_vec_scale(&rvec2, factor);
3377 vm_vec_add2(&rvec1, &rvec2);
3378 vm_vec_normalize_quick(&rvec1); // Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1
3379 vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
3382 //--old new way -- vms_vector fvec1, fvec2, rvec1, rvec2;
3384 //--old new way -- fvec1 = cur_objs[i].orient.fvec;
3385 //--old new way -- vm_vec_scale(&fvec1, F1_0-factor);
3386 //--old new way -- fvec2 = Objects[j].orient.fvec;
3387 //--old new way -- vm_vec_scale(&fvec2, factor);
3388 //--old new way -- vm_vec_add2(&fvec1, &fvec2);
3389 //--old new way -- vm_vec_normalize_quick(&fvec1);
3391 //--old new way -- rvec1 = cur_objs[i].orient.rvec;
3392 //--old new way -- vm_vec_scale(&rvec1, F1_0-factor);
3393 //--old new way -- rvec2 = Objects[j].orient.rvec;
3394 //--old new way -- vm_vec_scale(&rvec2, factor);
3395 //--old new way -- vm_vec_add2(&rvec1, &rvec2);
3396 //--old new way -- vm_vec_normalize_quick(&rvec1);
3398 //--old new way -- vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
3400 // -- old fashioned way -- vm_extract_angles_matrix(&cur_angles, &(cur_objs[i].orient));
3401 // -- old fashioned way -- vm_extract_angles_matrix(&dest_angles, &(Objects[j].orient));
3402 // -- old fashioned way --
3403 // -- old fashioned way -- delta_p = (dest_angles.p - cur_angles.p);
3404 // -- old fashioned way -- delta_h = (dest_angles.h - cur_angles.h);
3405 // -- old fashioned way -- delta_b = (dest_angles.b - cur_angles.b);
3406 // -- old fashioned way --
3407 // -- old fashioned way -- if (delta_p != 0) {
3408 // -- old fashioned way -- if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
3409 // -- old fashioned way -- if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
3410 // -- old fashioned way -- delta_p = fixmul(delta_p, factor);
3411 // -- old fashioned way -- cur_angles.p += delta_p;
3412 // -- old fashioned way -- }
3413 // -- old fashioned way -- if (delta_h != 0) {
3414 // -- old fashioned way -- if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
3415 // -- old fashioned way -- if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
3416 // -- old fashioned way -- delta_h = fixmul(delta_h, factor);
3417 // -- old fashioned way -- cur_angles.h += delta_h;
3418 // -- old fashioned way -- }
3419 // -- old fashioned way -- if (delta_b != 0) {
3420 // -- old fashioned way -- if (delta_b > F1_0/2) delta_b = dest_angles.b - cur_angles.b - F1_0;
3421 // -- old fashioned way -- if (delta_b < -F1_0/2) delta_b = dest_angles.b - cur_angles.b + F1_0;
3422 // -- old fashioned way -- delta_b = fixmul(delta_b, factor);
3423 // -- old fashioned way -- cur_angles.b += delta_b;
3424 // -- old fashioned way -- }
3427 // Interpolate the object position. This is just straight linear
3430 delta_x = Objects[j].pos.x - cur_objs[i].pos.x;
3431 delta_y = Objects[j].pos.y - cur_objs[i].pos.y;
3432 delta_z = Objects[j].pos.z - cur_objs[i].pos.z;
3434 delta_x = fixmul(delta_x, factor);
3435 delta_y = fixmul(delta_y, factor);
3436 delta_z = fixmul(delta_z, factor);
3438 cur_objs[i].pos.x += delta_x;
3439 cur_objs[i].pos.y += delta_y;
3440 cur_objs[i].pos.z += delta_z;
3442 // -- old fashioned way --// stuff the new angles back into the object structure
3443 // -- old fashioned way -- vm_angles_2_matrix(&(cur_objs[i].orient), &cur_angles);
3448 // get back to original position in the demo file. Reread the current
3449 // frame information again to reset all of the object stuff not covered
3450 // with Highest_object_index and the object array (previously rendered
3451 // objects, etc....)
3453 newdemo_back_frames(1);
3454 newdemo_back_frames(1);
3455 if (newdemo_read_frame_information() == -1)
3456 newdemo_stop_playback();
3457 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3459 for (i = 0; i <= num_cur_objs; i++)
3460 memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object));
3461 Highest_object_index = num_cur_objs;
3465 void newdemo_playback_one_frame()
3467 int frames_back, i, level;
3468 static fix base_interpol_time = 0;
3469 static fix d_recorded = 0;
3471 for (i = 0; i < MAX_PLAYERS; i++)
3472 if (Newdemo_players_cloaked & (1 << i))
3473 Players[i].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
3475 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
3476 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
3478 if (Newdemo_vcr_state == ND_STATE_PAUSED) // render a frame or not
3481 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
3482 DoJasonInterpolate(nd_recorded_time);
3484 Control_center_destroyed = 0;
3485 Countdown_seconds_left = -1;
3486 PALETTE_FLASH_SET(0,0,0); //clear flash
3488 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
3490 level = Current_level_num;
3491 if (NewdemoFrameCount == 0)
3493 else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (NewdemoFrameCount < 10)) {
3494 newdemo_goto_beginning();
3497 if (Newdemo_vcr_state == ND_STATE_REWINDING)
3501 if (Newdemo_at_eof) {
3502 cfseek(infile, 11, SEEK_CUR);
3504 newdemo_back_frames(frames_back);
3506 if (level != Current_level_num)
3507 newdemo_pop_ctrlcen_triggers();
3509 if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) {
3510 if (level != Current_level_num)
3511 newdemo_back_frames(1);
3512 Newdemo_vcr_state = ND_STATE_PAUSED;
3515 else if (Newdemo_vcr_state == ND_STATE_FASTFORWARD) {
3516 if (!Newdemo_at_eof)
3518 for (i = 0; i < 10; i++)
3520 if (newdemo_read_frame_information() == -1)
3523 Newdemo_vcr_state = ND_STATE_PAUSED;
3525 newdemo_stop_playback();
3531 Newdemo_vcr_state = ND_STATE_PAUSED;
3533 else if (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD) {
3534 if (!Newdemo_at_eof) {
3535 level = Current_level_num;
3536 if (newdemo_read_frame_information() == -1) {
3537 if (!Newdemo_at_eof)
3538 newdemo_stop_playback();
3540 if (level != Current_level_num) {
3541 if (newdemo_read_frame_information() == -1) {
3542 if (!Newdemo_at_eof)
3543 newdemo_stop_playback();
3546 Newdemo_vcr_state = ND_STATE_PAUSED;
3548 Newdemo_vcr_state = ND_STATE_PAUSED;
3552 // First, uptate the total playback time to date. Then we check to see
3553 // if we need to change the playback style to interpolate frames or
3554 // skip frames based on where the playback time is relative to the
3557 if (NewdemoFrameCount <= 0)
3558 nd_playback_total = nd_recorded_total; // baseline total playback time
3560 nd_playback_total += FrameTime;
3561 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
3562 if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) {
3563 playback_style = INTERPOLATE_PLAYBACK;
3564 nd_playback_total = nd_recorded_total + FrameTime; // baseline playback time
3565 base_interpol_time = nd_recorded_total;
3566 d_recorded = nd_recorded_time; // baseline delta recorded
3568 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
3569 if (nd_playback_total > nd_recorded_total)
3570 playback_style = SKIP_PLAYBACK;
3573 if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) {
3576 if (nd_recorded_total - nd_playback_total < FrameTime) {
3577 d_recorded = nd_recorded_total - nd_playback_total;
3579 while (nd_recorded_total - nd_playback_total < FrameTime) {
3581 int i, j, num_objs, level;
3583 num_objs = Highest_object_index;
3584 cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1));
3585 if (cur_objs == NULL) {
3586 Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs);
3589 for (i = 0; i <= num_objs; i++)
3590 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
3592 level = Current_level_num;
3593 if (newdemo_read_frame_information() == -1) {
3595 newdemo_stop_playback();
3598 if (level != Current_level_num) {
3600 if (newdemo_read_frame_information() == -1)
3601 newdemo_stop_playback();
3605 // for each new object in the frame just read in, determine if there is
3606 // a corresponding object that we have been interpolating. If so, then
3607 // copy that interpolated object to the new Objects array so that the
3608 // interpolated position and orientation can be preserved.
3610 for (i = 0; i <= num_objs; i++) {
3611 for (j = 0; j <= Highest_object_index; j++) {
3612 if (cur_objs[i].signature == Objects[j].signature) {
3613 memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix));
3614 memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector));
3620 d_recorded += nd_recorded_time;
3621 base_interpol_time = nd_playback_total - FrameTime;
3625 d_play = nd_playback_total - base_interpol_time;
3626 interpolate_frame(d_play, d_recorded);
3630 //mprintf ((0, "*"));
3631 if (newdemo_read_frame_information() == -1) {
3632 newdemo_stop_playback();
3635 if (playback_style == SKIP_PLAYBACK) {
3636 //mprintf ((0, "."));
3637 while (nd_playback_total > nd_recorded_total) {
3638 if (newdemo_read_frame_information() == -1) {
3639 newdemo_stop_playback();
3648 void newdemo_start_recording()
3651 Newdemo_size=GetFreeDiskSpace();
3652 mprintf((0, "Free space = %d\n", Newdemo_size));
3654 Newdemo_size = GetDiskFree();
3657 Newdemo_size -= 100000;
3659 if ((Newdemo_size+100000) < 2000000000) {
3660 if (((int)(Newdemo_size)) < 500000) {
3662 nm_messagebox(NULL, 1, TXT_OK, TXT_DEMO_NO_SPACE);
3664 nm_messagebox(NULL, 1, TXT_OK, "Not enough space on current\ndrive to start demo recording.");
3670 Newdemo_num_written = 0;
3672 Newdemo_state = ND_STATE_RECORDING;
3673 outfile = cfopen(DEMO_FILENAME, "wb");
3675 #if !defined(MACINTOSH) && !defined(_WIN32_WCE)
3676 if (outfile == NULL && errno == ENOENT) { //dir doesn't exist?
3678 if (outfile == NULL) { //dir doesn't exist and no errno on mac!
3680 cfile_mkdir(DEMO_DIR); //try making directory
3681 outfile = cfopen(DEMO_FILENAME, "wb");
3684 if (outfile == NULL)
3686 nm_messagebox(NULL, 1, TXT_OK, "Cannot open demo temp file");
3687 Newdemo_state = ND_STATE_NORMAL;
3690 newdemo_record_start_demo();
3694 char demoname_allowed_chars[] = "azAZ09__--";
3695 void newdemo_stop_recording()
3699 static char filename[15] = "", *s;
3700 static ubyte tmpcnt = 0;
3702 char fullname[15+FILENAME_LEN] = DEMO_DIR;
3703 unsigned short byte_count = 0;
3707 nd_write_byte(ND_EVENT_EOF);
3708 nd_write_short(frame_bytes_written - 1);
3709 if (Game_mode & GM_MULTI) {
3710 for (l = 0; l < N_players; l++) {
3711 if (Players[l].flags & PLAYER_FLAGS_CLOAKED)
3712 cloaked |= (1 << l);
3714 nd_write_byte(cloaked);
3715 nd_write_byte(ND_EVENT_EOF);
3717 nd_write_short(ND_EVENT_EOF);
3719 nd_write_short(ND_EVENT_EOF);
3720 nd_write_int(ND_EVENT_EOF);
3722 byte_count += 10; // from frame_bytes_written
3724 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
3725 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
3726 nd_write_int(Players[Player_num].flags); // be sure players flags are set
3727 nd_write_byte((sbyte)Primary_weapon);
3728 nd_write_byte((sbyte)Secondary_weapon);
3731 for (l = 0; l < MAX_PRIMARY_WEAPONS; l++)
3732 nd_write_short((short)Players[Player_num].primary_ammo[l]);
3734 for (l = 0; l < MAX_SECONDARY_WEAPONS; l++)
3735 nd_write_short((short)Players[Player_num].secondary_ammo[l]);
3736 byte_count += (sizeof(short) * (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS));
3738 nd_write_byte(Players[Player_num].laser_level);
3741 if (Game_mode & GM_MULTI) {
3742 nd_write_byte((sbyte)N_players);
3744 for (l = 0; l < N_players; l++) {
3745 nd_write_string(Players[l].callsign);
3746 byte_count += (strlen(Players[l].callsign) + 2);
3747 nd_write_byte(Players[l].connected);
3748 if (Game_mode & GM_MULTI_COOP) {
3749 nd_write_int(Players[l].score);
3752 nd_write_short((short)Players[l].net_killed_total);
3753 nd_write_short((short)Players[l].net_kills_total);
3758 nd_write_int(Players[Player_num].score);
3761 nd_write_short(byte_count);
3763 nd_write_byte(Current_level_num);
3764 nd_write_byte(ND_EVENT_EOF);
3766 l = cftell(outfile);
3769 Newdemo_state = ND_STATE_NORMAL;
3770 gr_palette_load( gr_palette );
3772 if (filename[0] != '\0') {
3773 int num, i = strlen(filename) - 1;
3776 while (isdigit(filename[i])) {
3782 num = atoi(&(filename[i]));
3785 sprintf (newfile, "%s%d", filename, num);
3786 strncpy(filename, newfile, 8);
3793 Newmenu_allowed_chars = demoname_allowed_chars;
3794 if (!Newdemo_no_space) {
3795 m[0].type=NM_TYPE_INPUT; m[0].text_len = 8; m[0].text = filename;
3796 exit = newmenu_do( NULL, TXT_SAVE_DEMO_AS, 1, &(m[0]), NULL );
3797 } else if (Newdemo_no_space == 1) {
3798 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_BAD;
3799 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3800 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3801 } else if (Newdemo_no_space == 2) {
3802 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_NOSPACE;
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 );
3806 Newmenu_allowed_chars = NULL;
3808 if (exit == -2) { // got bumped out from network menu
3809 char save_file[7+FILENAME_LEN];
3811 if (filename[0] != '\0') {
3812 strcpy(save_file, DEMO_DIR);
3813 strcat(save_file, filename);
3814 strcat(save_file, ".dem");
3816 sprintf (save_file, "%stmp%d.dem", DEMO_DIR, tmpcnt++);
3817 cfile_delete(save_file);
3818 cfile_rename(DEMO_FILENAME, save_file);
3821 if (exit == -1) { // pressed ESC
3822 cfile_delete(DEMO_FILENAME); // might as well remove the file
3823 return; // return without doing anything
3826 if (filename[0]==0) //null string
3829 //check to make sure name is ok
3830 for (s=filename;*s;s++)
3831 if (!isalnum(*s) && *s!='_') {
3832 nm_messagebox1(NULL, NULL,1,TXT_CONTINUE, TXT_DEMO_USE_LETTERS);
3836 if (Newdemo_no_space)
3837 strcat(fullname, m[1].text);
3839 strcat(fullname, m[0].text);
3840 strcat(fullname, ".dem");
3841 cfile_delete(fullname);
3842 cfile_rename(DEMO_FILENAME, fullname);
3846 extern char AltHogDir[64];
3847 extern char AltHogdir_initialized;
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 = cfopen(filename2, "rb");
3939 mprintf( (0, "Error reading '%s'\n", filename ));
3945 change_playernum_to(0); // force playernum to 0
3947 strncpy(nd_save_callsign, Players[Player_num].callsign, CALLSIGN_LEN);
3948 Viewer = ConsoleObject = &Objects[0]; // play properly as if console player
3949 if (newdemo_read_demo_start(rnd_demo)) {
3954 Game_mode = GM_NORMAL;
3955 Newdemo_state = ND_STATE_PLAYBACK;
3956 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3957 Newdemo_old_cockpit = Cockpit_mode;
3958 Newdemo_size = cfilelength(infile);
3961 NewdemoFrameCount = 0;
3962 Newdemo_players_cloaked = 0;
3963 playback_style = NORMAL_PLAYBACK;
3964 Function_mode = FMODE_GAME;
3965 Cockpit_3d_view[0] = CV_NONE; //turn off 3d views on cockpit
3966 Cockpit_3d_view[1] = CV_NONE; //turn off 3d views on cockpit
3967 newdemo_playback_one_frame(); // this one loads new level
3968 newdemo_playback_one_frame(); // get all of the objects to renderb game
3971 void newdemo_stop_playback()
3974 Newdemo_state = ND_STATE_NORMAL;
3976 change_playernum_to(0); //this is reality
3978 strncpy(Players[Player_num].callsign, nd_save_callsign, CALLSIGN_LEN);
3979 Cockpit_mode = Newdemo_old_cockpit;
3980 Game_mode = GM_GAME_OVER;
3981 Function_mode = FMODE_MENU;
3982 longjmp(LeaveGame,0); // Exit game loop
3988 #define BUF_SIZE 16384
3990 void newdemo_strip_frames(char *outname, int bytes_to_strip)
3994 int total_size, bytes_done, read_elems, bytes_back;
3995 int trailer_start, loc1, loc2, stop_loc, bytes_to_read;
3996 short last_frame_length;
3999 total_size = cfilelength(infile);
4000 outfile = cfopen(outname, "wb");
4001 if (outfile == NULL) {
4004 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't open output file";
4005 newmenu_do( NULL, NULL, 1, m, NULL );
4006 newdemo_stop_playback();
4009 buf = d_malloc(BUF_SIZE);
4013 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't malloc output buffer";
4014 newmenu_do( NULL, NULL, 1, m, NULL );
4016 newdemo_stop_playback();
4020 trailer_start = cftell(infile);
4021 cfseek(infile, 11, SEEK_CUR);
4023 while (bytes_back < bytes_to_strip) {
4024 loc1 = cftell(infile);
4025 //cfseek(infile, -10, SEEK_CUR);
4026 //nd_read_short(&last_frame_length);
4027 //cfseek(infile, 8 - last_frame_length, SEEK_CUR);
4028 newdemo_back_frames(1);
4029 loc2 = cftell(infile);
4030 bytes_back += (loc1 - loc2);
4032 cfseek(infile, -10, SEEK_CUR);
4033 nd_read_short(&last_frame_length);
4034 cfseek(infile, -3, SEEK_CUR);
4035 stop_loc = cftell(infile);
4036 cfseek(infile, 0, SEEK_SET);
4037 while (stop_loc > 0) {
4038 if (stop_loc < BUF_SIZE)
4039 bytes_to_read = stop_loc;
4041 bytes_to_read = BUF_SIZE;
4042 read_elems = cfread(buf, 1, bytes_to_read, infile);
4043 cfwrite(buf, 1, read_elems, outfile);
4044 stop_loc -= read_elems;
4046 stop_loc = cftell(outfile);
4047 cfseek(infile, trailer_start, SEEK_SET);
4048 while ((read_elems = cfread(buf, 1, BUF_SIZE, infile)) != 0)
4049 cfwrite(buf, 1, read_elems, outfile);
4050 cfseek(outfile, stop_loc, SEEK_SET);
4051 cfseek(outfile, 1, SEEK_CUR);
4052 cfwrite(&last_frame_length, 2, 1, outfile);
4054 newdemo_stop_playback();
4060 object DemoRightExtra,DemoLeftExtra;
4061 ubyte DemoDoRight=0,DemoDoLeft=0;
4063 void nd_render_extras (ubyte which,object *obj)
4066 ubyte type=which&15;
4070 Int3(); // how'd we get here?
4071 do_cockpit_window_view(w,NULL,0,WBU_WEAPON,NULL);
4077 memcpy (&DemoRightExtra,obj,sizeof(object)); DemoDoRight=type;
4081 memcpy (&DemoLeftExtra,obj,sizeof(object)); DemoDoLeft=type;
4086 void DoJasonInterpolate (fix recorded_time)
4090 JasonPlaybackTotal+=FrameTime;
4092 if (!First_time_playback)
4094 // get the difference between the recorded time and the playback time
4095 the_delay=(recorded_time - FrameTime);
4096 //mprintf ((0,"The delay=%d\n", f2i(the_delay)));
4097 if (the_delay >= f0_0)
4100 timer_delay(the_delay);
4105 while (JasonPlaybackTotal > nd_recorded_total)
4106 if (newdemo_read_frame_information() == -1)
4108 newdemo_stop_playback();
4112 //the_delay = nd_recorded_total - JasonPlaybackTotal;
4113 //if (the_delay > f0_0)
4114 // timer_delay(the_delay);
4119 First_time_playback=0;
4123 #pragma global_optimizer reset