This commit was manufactured by cvs2svn to create tag 'd2x-0_1_2'.
[btb/d2x.git] / main / newdemo.c
1 /*
2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.  
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
12 */
13
14 #ifdef HAVE_CONFIG_H
15 #include <conf.h>
16 #endif
17
18 #include <stdlib.h>
19 #include <stdio.h>
20 #include <stdarg.h>
21 #include <string.h>     // for memset
22 #include <errno.h>
23 #include <ctype.h>      /* for isdigit */
24 #ifdef __unix__
25 #include <sys/stat.h>
26 #include <sys/types.h>
27 #endif
28
29 #include "u_mem.h"
30 #include "inferno.h"
31 #include "game.h"
32 #include "gr.h"
33 #include "stdlib.h"
34 #include "bm.h"
35 //#include "error.h"
36 #include "mono.h"
37 #include "3d.h"
38 #include "segment.h"
39 #include "texmap.h"
40 #include "laser.h"
41 #include "key.h"
42 #include "gameseg.h"
43
44 #include "object.h"
45 #include "physics.h"
46 #include "slew.h"               
47 #include "render.h"
48 #include "wall.h"
49 #include "vclip.h"
50 #include "polyobj.h"
51 #include "fireball.h"
52 #include "laser.h"
53 #include "error.h"
54 #include "ai.h"
55 #include "hostage.h"
56 #include "morph.h"
57
58 #include "powerup.h"
59 #include "fuelcen.h"
60
61 #include "sounds.h"
62 #include "collide.h"
63
64 #include "lighting.h"
65 #include "newdemo.h"
66 #include "gameseq.h"
67 #include "gamesave.h"
68 #include "gamemine.h"
69 #include "switch.h"
70 #include "gauges.h"
71 #include "player.h"
72 #include "vecmat.h"
73 #include "newmenu.h"
74 #include "args.h"
75 #include "palette.h"
76 #include "multi.h"
77 #ifdef NETWORK
78 #include "network.h"
79 #endif
80 #include "text.h"
81 #include "cntrlcen.h"
82 #include "aistruct.h"
83 #include "mission.h"
84 #include "piggy.h"
85 #include "controls.h"
86 #include "d_io.h"
87 #include "d_delay.h"
88
89 #include "findfile.h"
90
91 #ifdef EDITOR
92 #include "editor/editor.h"
93 #endif
94
95 #ifdef MACINTOSH
96 #pragma global_optimizer off            // pretty much sucks...need to look into this
97 #endif
98
99 void DoJasonInterpolate (fix recorded_time);
100
101 //#include "nocfile.h"
102
103 //Does demo start automatically?
104 int Auto_demo = 0;
105
106 byte WasRecorded [MAX_OBJECTS];
107 byte ViewWasRecorded[MAX_OBJECTS];
108 byte RenderingWasRecorded[32];
109
110 #define ND_EVENT_EOF                                    0                       // EOF
111 #define ND_EVENT_START_DEMO             1                       // Followed by 16 character, NULL terminated filename of .SAV file to use
112 #define ND_EVENT_START_FRAME            2                       // Followed by integer frame number, then a fix FrameTime
113 #define ND_EVENT_VIEWER_OBJECT  3                       // Followed by an object structure
114 #define ND_EVENT_RENDER_OBJECT  4                       // Followed by an object structure
115 #define ND_EVENT_SOUND                          5                       // Followed by int soundum
116 #define ND_EVENT_SOUND_ONCE             6                       // Followed by int soundum
117 #define ND_EVENT_SOUND_3D                       7                       // Followed by int soundum, int angle, int volume
118 #define ND_EVENT_WALL_HIT_PROCESS 8                     // Followed by int segnum, int side, fix damage 
119 #define ND_EVENT_TRIGGER                        9                       // Followed by int segnum, int side, int objnum
120 #define ND_EVENT_HOSTAGE_RESCUED 10                     // Followed by int hostage_type
121 #define ND_EVENT_SOUND_3D_ONCE  11                      // Followed by int soundum, int angle, int volume
122 #define ND_EVENT_MORPH_FRAME            12                      // Followed by ? data
123 #define ND_EVENT_WALL_TOGGLE            13                      // Followed by int seg, int side
124 #define ND_EVENT_HUD_MESSAGE            14                      // Followed by char size, char * string (+null)
125 #define ND_EVENT_CONTROL_CENTER_DESTROYED 15    // Just a simple flag
126 #define ND_EVENT_PALETTE_EFFECT 16                      // Followed by short r,g,b
127 #define ND_EVENT_PLAYER_ENERGY   17       // followed by byte energy
128 #define ND_EVENT_PLAYER_SHIELD   18       // followed by byte shields
129 #define ND_EVENT_PLAYER_FLAGS    19                     // followed by player flags
130 #define ND_EVENT_PLAYER_WEAPON   20       // followed by weapon type and weapon number
131 #define ND_EVENT_EFFECT_BLOWUP   21                     // followed by segment, side, and pnt
132 #define ND_EVENT_HOMING_DISTANCE 22                     // followed by homing distance
133 #define ND_EVENT_LETTERBOX       23       // letterbox mode for death seq.
134 #define ND_EVENT_RESTORE_COCKPIT 24                     // restore cockpit after death
135 #define ND_EVENT_REARVIEW        25                     // going to rear view mode
136 #define ND_EVENT_WALL_SET_TMAP_NUM1 26          // Wall changed
137 #define ND_EVENT_WALL_SET_TMAP_NUM2 27          // Wall changed
138 #define ND_EVENT_NEW_LEVEL                      28                      // followed by level number
139 #define ND_EVENT_MULTI_CLOAK            29                      // followed by player num
140 #define ND_EVENT_MULTI_DECLOAK  30                      // followed by player num
141 #define ND_EVENT_RESTORE_REARVIEW       31              // restore cockpit after rearview mode
142 #define ND_EVENT_MULTI_DEATH            32                      // with player number
143 #define ND_EVENT_MULTI_KILL             33                      // with player number
144 #define ND_EVENT_MULTI_CONNECT  34                      // with player number
145 #define ND_EVENT_MULTI_RECONNECT        35                      // with player number
146 #define ND_EVENT_MULTI_DISCONNECT       36              // with player number
147 #define ND_EVENT_MULTI_SCORE            37                      // playernum / score
148 #define ND_EVENT_PLAYER_SCORE           38                      // followed by score
149 #define ND_EVENT_PRIMARY_AMMO           39                      // with old/new ammo count
150 #define ND_EVENT_SECONDARY_AMMO 40                      // with old/new ammo count
151 #define ND_EVENT_DOOR_OPENING           41                      // with segment/side
152 #define ND_EVENT_LASER_LEVEL            42                      // no data
153 #define ND_EVENT_PLAYER_AFTERBURNER     43       // followed by byte old ab, current ab
154 #define ND_EVENT_CLOAKING_WALL  44                      // info changing while wall cloaking
155 #define ND_EVENT_CHANGE_COCKPIT 45       // change the cockpit 
156 #define ND_EVENT_START_GUIDED      46                   // switch to guided view
157 #define ND_EVENT_END_GUIDED             47                      // stop guided view/return to ship
158 #define ND_EVENT_SECRET_THINGY  48                      // 0/1 = secret exit functional/non-functional
159 #define ND_EVENT_LINK_SOUND_TO_OBJ      49              // record digi_link_sound_to_object3
160 #define ND_EVENT_KILL_SOUND_TO_OBJ      50              // record digi_kill_sound_linked_to_object
161
162
163 #define NORMAL_PLAYBACK                 0
164 #define SKIP_PLAYBACK                   1
165 #define INTERPOLATE_PLAYBACK    2
166 #define INTERPOL_FACTOR       (F1_0 + (F1_0/5))
167
168 #define DEMO_VERSION                            15              //last D1 version was 13
169 #define DEMO_GAME_TYPE                  3               //1 was shareware, 2 registered
170
171 #ifndef MACINTOSH
172 #define DEMO_FILENAME                   "demos/tmpdemo.dem"
173 #define DEMO_DIR                                "demos/"
174 #else
175 #define DEMO_FILENAME                   ":Demos:tmpdemo.dem"
176 #define DEMO_DIR                                ":Demos:"
177 #endif
178
179 #define DEMO_MAX_LEVELS                 29
180
181
182 char nd_save_callsign[CALLSIGN_LEN+1];
183 int Newdemo_state = 0;
184 int Newdemo_vcr_state = 0;
185 int Newdemo_start_frame = -1;
186 unsigned int Newdemo_size;
187 int Newdemo_num_written;
188 int Newdemo_game_mode;
189 int Newdemo_old_cockpit;
190 byte Newdemo_no_space;
191 byte Newdemo_at_eof;
192 byte Newdemo_do_interpolate = 0; // 1
193 byte Newdemo_players_cloaked;
194 byte Newdemo_warning_given = 0;
195 byte Newdemo_cntrlcen_destroyed = 0;
196 static byte nd_bad_read;
197 int NewdemoFrameCount;
198 short frame_bytes_written = 0;
199 fix nd_playback_total;
200 fix nd_recorded_total;
201 fix nd_recorded_time;
202 byte playback_style;
203 byte First_time_playback=1;
204 fix JasonPlaybackTotal=0;
205
206
207 FILE *infile;
208 FILE *outfile=NULL;
209
210 int newdemo_get_percent_done()  {
211         if ( Newdemo_state == ND_STATE_PLAYBACK )       {
212                 return (ftell(infile)*100)/Newdemo_size;
213         }
214         if ( Newdemo_state == ND_STATE_RECORDING )      {
215                 return ftell(outfile);
216         }
217         return 0;
218 }
219
220 #define VEL_PRECISION 12
221
222 void my_extract_shortpos(object *objp, shortpos *spp)
223 {
224         int     segnum;
225         byte    *sp;
226
227         sp = spp->bytemat;
228         objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
229         objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
230         objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
231
232         objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
233         objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
234         objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
235
236         objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
237         objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
238         objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
239
240         segnum = spp->segment;
241         objp->segnum = segnum;
242
243         objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x;
244         objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y;
245         objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z;
246
247         objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION);
248         objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION);
249         objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION);
250 }
251
252 int newdemo_read( void *buffer, int elsize, int nelem )
253 {
254         int num_read;
255         num_read = fread( buffer,elsize,nelem, infile );
256         if (ferror(infile) || feof(infile))
257                 nd_bad_read = -1;
258
259         return num_read;
260 }
261
262 int newdemo_find_object( int signature )
263 {
264         int i;
265         object * objp;
266         objp = Objects;
267         for (i=0; i<=Highest_object_index; i++, objp++ )        {
268                 if ( (objp->type != OBJ_NONE) && (objp->signature == signature))
269                         return i;
270         }
271         return -1;
272 }
273
274 int newdemo_write( void *buffer, int elsize, int nelem )
275 {
276         int num_written, total_size;
277
278         total_size = elsize * nelem;
279         frame_bytes_written += total_size;
280         Newdemo_num_written += total_size;
281         Assert(outfile != NULL);
282         num_written = fwrite( buffer, elsize, nelem, outfile );
283 //      if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) {
284 //              Newdemo_no_space=1;
285 //              newdemo_stop_recording();
286 //              return -1;
287 //      }
288         if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space)
289                 Newdemo_no_space=1;
290         if (num_written == nelem && !Newdemo_no_space)
291                 return num_written;
292
293         Newdemo_no_space=2;
294         newdemo_stop_recording();
295         return -1;
296 }
297
298 /*
299  *  The next bunch of files taken from Matt's gamesave.c.  We have to modify
300  *  these since the demo must save more information about objects that
301  *  just a gamesave
302 */
303
304 static void nd_write_byte(byte b)
305 {
306         newdemo_write(&b, 1, 1);
307 }
308
309 static void nd_write_short(short s)
310 {
311         newdemo_write(&s, 2, 1);
312 }
313
314 static void nd_write_int(int i)
315 {
316         newdemo_write(&i, 4, 1);
317 }
318
319 static void nd_write_string(char *str)
320 {
321         nd_write_byte(strlen(str) + 1);
322         newdemo_write(str, strlen(str) + 1, 1);
323 }
324
325 static void nd_write_fix(fix f)
326 {
327         newdemo_write(&f, sizeof(fix), 1);
328 }
329
330 static void nd_write_fixang(fixang f)
331 {
332         newdemo_write(&f, sizeof(fixang), 1);
333 }
334
335 static void nd_write_vector(vms_vector *v)
336 {
337         nd_write_fix(v->x);
338         nd_write_fix(v->y);
339         nd_write_fix(v->z);
340 }
341
342 static void nd_write_angvec(vms_angvec *v)
343 {
344         nd_write_fixang(v->p);
345         nd_write_fixang(v->b);
346         nd_write_fixang(v->h);
347 }
348
349 void nd_write_shortpos(object *obj)
350 {
351         int i;
352         shortpos sp;
353         ubyte render_type;
354
355         create_shortpos(&sp, obj, 0);
356
357         render_type = obj->render_type;
358         if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
359                 for (i = 0; i < 9; i++)
360                         nd_write_byte(sp.bytemat[i]);
361                 for (i = 0; i < 9; i++) {
362                         if (sp.bytemat[i] != 0)
363                                 break;
364                 }
365                 if (i == 9) {
366                         Int3();                 // contact Allender about this.
367                 }
368         }
369
370         nd_write_short(sp.xo);
371         nd_write_short(sp.yo);
372         nd_write_short(sp.zo);
373         nd_write_short(sp.segment);
374         nd_write_short(sp.velx);
375         nd_write_short(sp.vely);
376         nd_write_short(sp.velz);
377 }
378
379 static void nd_read_byte(byte *b)
380 {
381         newdemo_read(b, 1, 1);
382 }
383
384 static void nd_read_short(short *s)
385 {
386         newdemo_read(s, 2, 1);
387 }
388
389 static void nd_read_int(int *i)
390 {
391         newdemo_read(i, 4, 1);
392 }
393
394 static void nd_read_string(char *str)
395 {
396         byte len;
397
398         nd_read_byte(&len);
399         newdemo_read(str, len, 1);
400 }
401
402 static void nd_read_fix(fix *f)
403 {
404         newdemo_read(f, sizeof(fix), 1);
405 }
406
407 static void nd_read_fixang(fixang *f)
408 {
409         newdemo_read(f, sizeof(fixang), 1);
410 }
411
412 static void nd_read_vector(vms_vector *v)
413 {
414         nd_read_fix(&(v->x));
415    nd_read_fix(&(v->y));
416         nd_read_fix(&(v->z));
417 }
418
419 static void nd_read_angvec(vms_angvec *v)
420 {
421         nd_read_fixang(&(v->p));
422         nd_read_fixang(&(v->b));
423         nd_read_fixang(&(v->h));
424 }
425
426 static void nd_read_shortpos(object *obj)
427 {
428         shortpos sp;
429         int i;
430         ubyte render_type;
431
432         render_type = obj->render_type;
433         if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
434                 for (i = 0; i < 9; i++)
435                         nd_read_byte(&(sp.bytemat[i]));
436         }
437
438         nd_read_short(&(sp.xo));
439         nd_read_short(&(sp.yo));
440         nd_read_short(&(sp.zo));
441         nd_read_short(&(sp.segment));
442         nd_read_short(&(sp.velx));
443         nd_read_short(&(sp.vely));
444         nd_read_short(&(sp.velz));
445
446         my_extract_shortpos(obj, &sp);
447         if ((obj->id == VCLIP_MORPHING_ROBOT) && (render_type == RT_FIREBALL) && (obj->control_type == CT_EXPLOSION))
448                 extract_orient_from_segment(&obj->orient,&Segments[obj->segnum]);
449
450 }
451
452 object *prev_obj=NULL;          //ptr to last object read in
453         
454 void nd_read_object(object *obj)
455 {
456         memset(obj, 0, sizeof(object));
457
458 /*
459  *  Do render type first, since with render_type == RT_NONE, we
460  *  blow by all other object information
461 */
462         nd_read_byte(&(obj->render_type));
463         nd_read_byte(&(obj->type));
464         if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
465                 return;
466
467         nd_read_byte(&(obj->id));
468         nd_read_byte(&(obj->flags));
469         nd_read_short((short *)&(obj->signature));
470         nd_read_shortpos(obj);
471
472 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
473         Int3();
474
475         obj->attached_obj               = -1;
476
477         switch(obj->type) {
478
479                 case OBJ_HOSTAGE:
480                         obj->control_type = CT_POWERUP; 
481                         obj->movement_type = MT_NONE;
482                         obj->size = HOSTAGE_SIZE;
483                         break;
484
485                 case OBJ_ROBOT:
486                         obj->control_type = CT_AI;
487                         //      (MarkA and MikeK said we should not do the crazy last secret stuff with multiple reactors...
488                         //      This necessary code is our vindication. --MK, 2/15/96)
489                         if (obj->id != SPECIAL_REACTOR_ROBOT)
490                                 obj->movement_type = MT_PHYSICS;
491                         else
492                                 obj->movement_type = MT_NONE;
493                         obj->size = Polygon_models[Robot_info[obj->id].model_num].rad;
494                         obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
495                         obj->rtype.pobj_info.subobj_flags = 0;
496                         obj->ctype.ai_info.CLOAKED = (Robot_info[obj->id].cloak_type?1:0);
497                         break;
498
499                 case OBJ_POWERUP:
500                         obj->control_type = CT_POWERUP;
501                         nd_read_byte(&(obj->movement_type));            // might have physics movement
502                         obj->size = Powerup_info[obj->id].size;
503                         break;
504
505                 case OBJ_PLAYER:
506                         obj->control_type = CT_NONE;
507                         obj->movement_type = MT_PHYSICS;
508                         obj->size = Polygon_models[Player_ship->model_num].rad;
509                         obj->rtype.pobj_info.model_num = Player_ship->model_num;
510                         obj->rtype.pobj_info.subobj_flags = 0;
511                         break;
512
513                 case OBJ_CLUTTER:
514                         obj->control_type = CT_NONE;
515                         obj->movement_type = MT_NONE;
516                         obj->size = Polygon_models[obj->id].rad;
517                         obj->rtype.pobj_info.model_num = obj->id;
518                         obj->rtype.pobj_info.subobj_flags = 0;
519                         break;
520
521                 default:
522                         nd_read_byte(&(obj->control_type));
523                         nd_read_byte(&(obj->movement_type));
524                         nd_read_fix(&(obj->size));
525                         break;  
526         }
527
528
529         nd_read_vector(&(obj->last_pos));
530         if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
531                 nd_read_fix(&(obj->lifeleft));
532         else {
533                 ubyte b;
534                 
535                 nd_read_byte(&b);
536                 obj->lifeleft = (fix)b;
537 // MWA old way -- won't work with big endian machines           nd_read_byte((ubyte *)&(obj->lifeleft));
538                 obj->lifeleft = (fix)((int)obj->lifeleft << 12);
539         }
540
541         if (obj->type == OBJ_ROBOT) {
542                 if (Robot_info[obj->id].boss_flag) {
543                         byte cloaked;
544
545                         nd_read_byte(&cloaked);
546                         obj->ctype.ai_info.CLOAKED = cloaked;
547                 }
548         }
549
550         switch (obj->movement_type) {
551
552                 case MT_PHYSICS:
553                         nd_read_vector(&(obj->mtype.phys_info.velocity));
554                         nd_read_vector(&(obj->mtype.phys_info.thrust));
555                         break;
556
557                 case MT_SPINNING:
558                         nd_read_vector(&(obj->mtype.spin_rate));
559                         break;
560
561                 case MT_NONE:
562                         break;
563
564                 default:
565                         Int3();
566         }
567
568         switch (obj->control_type) {
569
570                 case CT_EXPLOSION:
571
572                         nd_read_fix(&(obj->ctype.expl_info.spawn_time));
573                         nd_read_fix(&(obj->ctype.expl_info.delete_time));
574                         nd_read_short(&(obj->ctype.expl_info.delete_objnum));
575
576                         obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
577
578                         if (obj->flags & OF_ATTACHED) {         //attach to previous object
579                                 Assert(prev_obj!=NULL);
580                                 if (prev_obj->control_type == CT_EXPLOSION) {
581                                         if (prev_obj->flags & OF_ATTACHED && prev_obj->ctype.expl_info.attach_parent!=-1)
582                                                 obj_attach(&Objects[prev_obj->ctype.expl_info.attach_parent],obj);
583                                         else
584                                                 obj->flags &= ~OF_ATTACHED;
585                                 }
586                                 else
587                                         obj_attach(prev_obj,obj);
588                         }
589
590                         break;
591
592                 case CT_LIGHT:
593                         nd_read_fix(&(obj->ctype.light_info.intensity));
594                         break;
595
596                 case CT_AI:
597                 case CT_WEAPON:
598                 case CT_NONE:
599                 case CT_FLYING:
600                 case CT_DEBRIS:
601                 case CT_POWERUP:
602                 case CT_SLEW:
603                 case CT_CNTRLCEN:
604                 case CT_REMOTE:
605                 case CT_MORPH:
606                         break;
607
608                 case CT_FLYTHROUGH:
609                 case CT_REPAIRCEN:
610                 default:
611                         Int3();
612         
613         }
614
615         switch (obj->render_type) {
616
617                 case RT_NONE:
618                         break;
619
620                 case RT_MORPH:
621                 case RT_POLYOBJ: {
622                         int i, tmo;
623
624                         if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
625                                 nd_read_int(&(obj->rtype.pobj_info.model_num));
626                                 nd_read_int(&(obj->rtype.pobj_info.subobj_flags));
627                         }
628
629                         if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
630 #if 0
631                                 for (i=0;i<MAX_SUBMODELS;i++)
632                                         nd_read_angvec(&(obj->pobj_info.anim_angles[i]));
633 #endif
634                                 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
635                                         nd_read_angvec(&obj->rtype.pobj_info.anim_angles[i]);
636
637                         nd_read_int(&tmo);
638
639                         #ifndef EDITOR
640                         obj->rtype.pobj_info.tmap_override      = tmo;
641                         #else
642                         if (tmo==-1)
643                                 obj->rtype.pobj_info.tmap_override      = -1;
644                         else {
645                                 int xlated_tmo = tmap_xlate_table[tmo];
646                                 if (xlated_tmo < 0)     {
647 //                                      mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->pobj_info.model_num));
648                                         Int3();
649                                         xlated_tmo = 0;
650                                 }
651                                 obj->rtype.pobj_info.tmap_override      = xlated_tmo;
652                         }
653                         #endif
654
655                         break;
656                 }
657
658                 case RT_POWERUP:
659                 case RT_WEAPON_VCLIP:
660                 case RT_FIREBALL:
661                 case RT_HOSTAGE:
662                         nd_read_int(&(obj->rtype.vclip_info.vclip_num));
663                         nd_read_fix(&(obj->rtype.vclip_info.frametime));
664                         nd_read_byte(&(obj->rtype.vclip_info.framenum));
665                         break;
666
667                 case RT_LASER:
668                         break;
669
670                 default:
671                         Int3();
672
673         }
674
675         prev_obj = obj;
676 }
677
678 void nd_write_object(object *obj)
679 {
680         int life;
681
682 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
683         Int3();
684
685 /*
686  *  Do render_type first so on read, we can make determination of
687  *  what else to read in
688 */
689         nd_write_byte(obj->render_type);
690         nd_write_byte(obj->type);
691         if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
692                 return;
693
694         nd_write_byte(obj->id);
695         nd_write_byte(obj->flags);
696         nd_write_short((short)obj->signature);
697         nd_write_shortpos(obj);
698
699         if ((obj->type != OBJ_HOSTAGE) && (obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_POWERUP) && (obj->type != OBJ_CLUTTER)) {
700                 nd_write_byte(obj->control_type);
701                 nd_write_byte(obj->movement_type);
702                 nd_write_fix(obj->size);
703         }
704         if (obj->type == OBJ_POWERUP)
705                 nd_write_byte(obj->movement_type);
706
707         nd_write_vector(&obj->last_pos);
708
709         if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
710                 nd_write_fix(obj->lifeleft);
711         else {
712                 life = (int)obj->lifeleft;
713                 life = life >> 12;
714                 if (life > 255)
715                         life = 255;
716                 nd_write_byte((ubyte)life);
717         }
718
719         if (obj->type == OBJ_ROBOT) {
720                 if (Robot_info[obj->id].boss_flag) {
721                         if ((GameTime > Boss_cloak_start_time) && (GameTime < Boss_cloak_end_time))
722                                 nd_write_byte(1);
723                         else
724                                 nd_write_byte(0);
725                 }
726         }
727
728         switch (obj->movement_type) {
729
730                 case MT_PHYSICS:
731                         nd_write_vector(&obj->mtype.phys_info.velocity);
732                         nd_write_vector(&obj->mtype.phys_info.thrust);
733                         break;
734
735                 case MT_SPINNING:
736                         nd_write_vector(&obj->mtype.spin_rate);
737                         break;
738
739                 case MT_NONE:
740                         break;
741
742                 default:
743                         Int3();
744         }
745
746         switch (obj->control_type) {
747
748                 case CT_AI:
749                         break;
750
751                 case CT_EXPLOSION:
752                         nd_write_fix(obj->ctype.expl_info.spawn_time);
753                         nd_write_fix(obj->ctype.expl_info.delete_time);
754                         nd_write_short(obj->ctype.expl_info.delete_objnum);
755                         break;
756
757                 case CT_WEAPON:
758                         break;
759
760                 case CT_LIGHT:
761
762                         nd_write_fix(obj->ctype.light_info.intensity);
763                         break;
764
765                 case CT_NONE:
766                 case CT_FLYING:
767                 case CT_DEBRIS:
768                 case CT_POWERUP:
769                 case CT_SLEW:           //the player is generally saved as slew
770                 case CT_CNTRLCEN:
771                 case CT_REMOTE:
772                 case CT_MORPH:
773                         break;
774
775                 case CT_REPAIRCEN:
776                 case CT_FLYTHROUGH:
777                 default:
778                         Int3();
779         
780         }
781
782         switch (obj->render_type) {
783
784                 case RT_NONE:
785                         break;
786
787                 case RT_MORPH:
788                 case RT_POLYOBJ: {
789                         int i;
790
791                         if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
792                                 nd_write_int(obj->rtype.pobj_info.model_num);
793                                 nd_write_int(obj->rtype.pobj_info.subobj_flags);
794                         }
795
796                         if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
797 #if 0
798                                 for (i=0;i<MAX_SUBMODELS;i++)
799                                         nd_write_angvec(&obj->pobj_info.anim_angles[i]);
800 #endif
801                                 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
802                                         nd_write_angvec(&obj->rtype.pobj_info.anim_angles[i]);
803
804                         nd_write_int(obj->rtype.pobj_info.tmap_override);
805
806                         break;
807                 }
808
809                 case RT_POWERUP:
810                 case RT_WEAPON_VCLIP:
811                 case RT_FIREBALL:
812                 case RT_HOSTAGE:
813                         nd_write_int(obj->rtype.vclip_info.vclip_num);
814                         nd_write_fix(obj->rtype.vclip_info.frametime);
815                         nd_write_byte(obj->rtype.vclip_info.framenum);
816                         break;
817
818                 case RT_LASER:
819                         break;
820
821                 default:
822                         Int3();
823
824         }
825
826 }
827
828 int JustStartedRecording=0,JustStartedPlayback=0;
829
830 void newdemo_record_start_demo()
831 {
832         int i;
833
834         stop_time();
835         nd_write_byte(ND_EVENT_START_DEMO);
836         nd_write_byte(DEMO_VERSION);
837         nd_write_byte(DEMO_GAME_TYPE);
838         nd_write_fix(GameTime);
839  
840 #ifdef NETWORK
841         if (Game_mode & GM_MULTI)
842                 nd_write_int(Game_mode | (Player_num << 16));
843         else
844 #endif
845                 // NOTE LINK TO ABOVE!!!
846                 nd_write_int(Game_mode);
847 #ifdef NETWORK
848
849         if (Game_mode & GM_TEAM) {
850                 nd_write_byte(Netgame.team_vector);
851                 nd_write_string(Netgame.team_name[0]);
852                 nd_write_string(Netgame.team_name[1]);
853         }
854
855         if (Game_mode & GM_MULTI) {
856                 nd_write_byte((byte)N_players);
857                 for (i = 0; i < N_players; i++) {
858                         nd_write_string(Players[i].callsign);
859                         nd_write_byte(Players[i].connected);
860
861                         if (Game_mode & GM_MULTI_COOP) {
862                                 nd_write_int(Players[i].score);
863                         } else {
864                                 nd_write_short((short)Players[i].net_killed_total);
865                                 nd_write_short((short)Players[i].net_kills_total);
866                         }
867                 }
868         } else
869 #endif
870                 // NOTE LINK TO ABOVE!!!
871                 nd_write_int(Players[Player_num].score);
872
873         for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
874                 nd_write_short((short)Players[Player_num].primary_ammo[i]);
875
876         for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
877                 nd_write_short((short)Players[Player_num].secondary_ammo[i]);
878
879         nd_write_byte((byte)Players[Player_num].laser_level);
880
881 //  Support for missions added here
882
883         nd_write_string(Current_mission_filename);
884
885         nd_write_byte((byte)(f2ir(Players[Player_num].energy)));
886         nd_write_byte((byte)(f2ir(Players[Player_num].shields)));
887         nd_write_int(Players[Player_num].flags);                // be sure players flags are set
888         nd_write_byte((byte)Primary_weapon);
889         nd_write_byte((byte)Secondary_weapon);
890         Newdemo_start_frame = FrameCount;
891    JustStartedRecording=1;
892  
893         newdemo_set_new_level(Current_level_num);
894         start_time();
895
896 }
897
898 void newdemo_record_start_frame(int frame_number, fix frame_time )
899 {
900   int i;
901   
902         if (Newdemo_no_space) {
903                 newdemo_stop_playback();
904                 return;
905         }
906                                   
907   
908         stop_time();
909
910    for (i=0;i<MAX_OBJECTS;i++)
911          {
912       WasRecorded[i]=0;
913                 ViewWasRecorded[i]=0;
914          }
915         for (i=0;i<32;i++)
916         RenderingWasRecorded[i]=0;
917         
918         frame_number -= Newdemo_start_frame;
919
920         Assert(frame_number >= 0 );
921
922         nd_write_byte(ND_EVENT_START_FRAME);
923         nd_write_short(frame_bytes_written - 1);                // from previous frame
924         frame_bytes_written=3;
925         nd_write_int(frame_number);
926         nd_write_int(frame_time);
927         start_time();
928
929 }
930
931 void newdemo_record_render_object(object * obj)
932 {
933    if (ViewWasRecorded[obj-Objects])
934                 return;
935
936   //    if (obj==&Objects[Players[Player_num].objnum] && !Player_is_dead)
937     //  return;
938         
939         stop_time();
940         nd_write_byte(ND_EVENT_RENDER_OBJECT);
941         nd_write_object(obj);
942         start_time();
943 }
944
945 extern ubyte RenderingType;
946
947 void newdemo_record_viewer_object(object * obj)
948 {
949
950    if (ViewWasRecorded[obj-Objects] && (ViewWasRecorded[obj-Objects]-1)==RenderingType)
951                 return;
952         //if (WasRecorded[obj-Objects])
953           //    return;
954         if (RenderingWasRecorded[RenderingType])
955                 return;
956         
957    ViewWasRecorded[obj-Objects]=RenderingType+1;
958         RenderingWasRecorded[RenderingType]=1;
959         stop_time();
960         nd_write_byte(ND_EVENT_VIEWER_OBJECT);
961    nd_write_byte(RenderingType);
962         nd_write_object(obj);
963         start_time();
964 }
965
966 void newdemo_record_sound( int soundno )        {
967         stop_time();
968         nd_write_byte(ND_EVENT_SOUND);
969         nd_write_int( soundno );
970         start_time();
971 }
972 //--unused-- void newdemo_record_sound_once( int soundno )      {
973 //--unused--    stop_time();
974 //--unused--    nd_write_byte( ND_EVENT_SOUND_ONCE );
975 //--unused--    nd_write_int( soundno );
976 //--unused--    start_time();
977 //--unused-- }
978 //--unused-- 
979
980 void newdemo_record_cockpit_change (int mode)
981  {
982   stop_time();
983   nd_write_byte (ND_EVENT_CHANGE_COCKPIT);
984   nd_write_int(mode);
985   start_time();
986  }
987
988
989 void newdemo_record_sound_3d( int soundno, int angle, int volume )      {
990         stop_time();
991         nd_write_byte( ND_EVENT_SOUND_3D );
992         nd_write_int( soundno );
993         nd_write_int( angle );
994         nd_write_int( volume );
995         start_time();
996 }
997
998 void newdemo_record_sound_3d_once( int soundno, int angle, int volume ) {
999         stop_time();
1000         nd_write_byte( ND_EVENT_SOUND_3D_ONCE );
1001         nd_write_int( soundno );
1002         nd_write_int( angle );
1003         nd_write_int( volume );
1004         start_time();
1005 }
1006
1007
1008 void newdemo_record_link_sound_to_object3( int soundno, short objnum, fix max_volume, fix  max_distance, int loop_start, int loop_end )
1009 {
1010         stop_time();
1011         nd_write_byte( ND_EVENT_LINK_SOUND_TO_OBJ );
1012         nd_write_int( soundno );
1013         nd_write_int( Objects[objnum].signature );
1014         nd_write_int( max_volume );
1015         nd_write_int( max_distance );
1016         nd_write_int( loop_start );
1017         nd_write_int( loop_end );
1018         start_time();
1019 }
1020
1021 void newdemo_record_kill_sound_linked_to_object( int objnum )
1022 {
1023         stop_time();
1024         nd_write_byte( ND_EVENT_KILL_SOUND_TO_OBJ );
1025         nd_write_int( Objects[objnum].signature );
1026         start_time();
1027 }
1028
1029
1030 void newdemo_record_wall_hit_process( int segnum, int side, int damage, int playernum )
1031 {
1032         stop_time();
1033 //      segnum = segnum;
1034 //      side = side;
1035 //      damage = damage;
1036 //      playernum = playernum;
1037         nd_write_byte( ND_EVENT_WALL_HIT_PROCESS );
1038         nd_write_int( segnum );
1039         nd_write_int( side );
1040         nd_write_int( damage );
1041         nd_write_int( playernum );
1042         start_time();
1043 }
1044
1045 void newdemo_record_guided_start ()
1046  {
1047   nd_write_byte (ND_EVENT_START_GUIDED);
1048  }
1049 void newdemo_record_guided_end ()
1050  {
1051   nd_write_byte (ND_EVENT_END_GUIDED);
1052  }
1053
1054 void newdemo_record_secret_exit_blown(int truth)
1055 {
1056         stop_time();
1057         nd_write_byte( ND_EVENT_SECRET_THINGY );
1058         nd_write_int( truth );
1059         start_time();
1060 }
1061
1062 void newdemo_record_trigger( int segnum, int side, int objnum,int shot )
1063 {
1064         stop_time();
1065         nd_write_byte( ND_EVENT_TRIGGER );
1066         nd_write_int( segnum );
1067         nd_write_int( side );
1068         nd_write_int( objnum );
1069         nd_write_int(shot);
1070         start_time();
1071 }
1072
1073 void newdemo_record_hostage_rescued( int hostage_number )       {
1074         stop_time();
1075         nd_write_byte( ND_EVENT_HOSTAGE_RESCUED );
1076         nd_write_int( hostage_number );
1077         start_time();
1078 }
1079
1080 void newdemo_record_morph_frame(morph_data *md) {
1081         stop_time();
1082
1083         nd_write_byte( ND_EVENT_MORPH_FRAME );
1084 #if 0
1085         newdemo_write( md->morph_vecs, sizeof(md->morph_vecs), 1 );
1086         newdemo_write( md->submodel_active, sizeof(md->submodel_active), 1 );
1087         newdemo_write( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 );
1088 #endif
1089         nd_write_object( md->obj );
1090         start_time();
1091 }
1092
1093 void newdemo_record_wall_toggle( int segnum, int side ) {
1094         stop_time();
1095         nd_write_byte( ND_EVENT_WALL_TOGGLE );
1096         nd_write_int( segnum );
1097         nd_write_int( side );
1098         start_time();
1099 }
1100
1101 void newdemo_record_control_center_destroyed()
1102 {
1103         stop_time();
1104         nd_write_byte( ND_EVENT_CONTROL_CENTER_DESTROYED );
1105         nd_write_int( Countdown_seconds_left );
1106         start_time();
1107 }
1108
1109 void newdemo_record_hud_message( char * message )
1110 {
1111         stop_time();
1112         nd_write_byte( ND_EVENT_HUD_MESSAGE );
1113         nd_write_string(message);
1114         start_time();
1115 }
1116
1117 void newdemo_record_palette_effect(short r, short g, short b )
1118 {
1119         stop_time();
1120         nd_write_byte( ND_EVENT_PALETTE_EFFECT );
1121         nd_write_short( r );
1122         nd_write_short( g );
1123         nd_write_short( b );
1124         start_time();
1125 }
1126
1127 void newdemo_record_player_energy(int old_energy, int energy)
1128 {
1129         stop_time();
1130         nd_write_byte( ND_EVENT_PLAYER_ENERGY );
1131         nd_write_byte((byte) old_energy);
1132         nd_write_byte((byte) energy);
1133         start_time();
1134 }
1135
1136 void newdemo_record_player_afterburner(fix old_afterburner, fix afterburner)
1137 {
1138         stop_time();
1139         nd_write_byte( ND_EVENT_PLAYER_AFTERBURNER );
1140         nd_write_byte((byte) (old_afterburner>>9));
1141         nd_write_byte((byte) (afterburner>>9));
1142         start_time();
1143 }
1144
1145 void newdemo_record_player_shields(int old_shield, int shield)
1146 {
1147         stop_time();
1148         nd_write_byte( ND_EVENT_PLAYER_SHIELD );
1149         nd_write_byte((byte)old_shield);
1150         nd_write_byte((byte)shield);
1151         start_time();
1152 }
1153
1154 void newdemo_record_player_flags(uint oflags, uint flags)
1155 {
1156         stop_time();
1157         nd_write_byte( ND_EVENT_PLAYER_FLAGS );
1158         nd_write_int(((short)oflags << 16) | (short)flags);
1159         start_time();
1160 }
1161
1162 void newdemo_record_player_weapon(int weapon_type, int weapon_num)
1163 {
1164         stop_time();
1165         nd_write_byte( ND_EVENT_PLAYER_WEAPON );
1166         nd_write_byte((byte)weapon_type);
1167         nd_write_byte((byte)weapon_num);
1168         if (weapon_type)
1169                 nd_write_byte((byte)Secondary_weapon);
1170         else
1171                 nd_write_byte((byte)Primary_weapon);
1172         start_time();
1173 }
1174
1175 void newdemo_record_effect_blowup(short segment, int side, vms_vector *pnt)
1176 {
1177         stop_time();
1178         nd_write_byte (ND_EVENT_EFFECT_BLOWUP);
1179         nd_write_short(segment);
1180         nd_write_byte((byte)side);
1181         nd_write_vector(pnt);
1182         start_time();
1183 }
1184
1185 void newdemo_record_homing_distance(fix distance)
1186 {
1187         stop_time();
1188         nd_write_byte(ND_EVENT_HOMING_DISTANCE);
1189         nd_write_short((short)(distance>>16));
1190         start_time();
1191 }
1192
1193 void newdemo_record_letterbox(void)
1194 {
1195         stop_time();
1196         nd_write_byte(ND_EVENT_LETTERBOX);
1197         start_time();
1198 }
1199
1200 void newdemo_record_rearview(void)
1201 {
1202         stop_time();
1203         nd_write_byte(ND_EVENT_REARVIEW);
1204         start_time();
1205 }
1206
1207 void newdemo_record_restore_cockpit(void)
1208 {
1209         stop_time();
1210         nd_write_byte(ND_EVENT_RESTORE_COCKPIT);
1211         start_time();
1212 }
1213
1214 void newdemo_record_restore_rearview(void)
1215 {
1216         stop_time();
1217         nd_write_byte(ND_EVENT_RESTORE_REARVIEW);
1218         start_time();
1219 }
1220
1221 void newdemo_record_wall_set_tmap_num1(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1222 {
1223         stop_time();
1224         nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM1);
1225         nd_write_short(seg);
1226         nd_write_byte(side);
1227         nd_write_short(cseg);
1228         nd_write_byte(cside);
1229         nd_write_short(tmap);
1230         start_time();
1231 }
1232
1233 void newdemo_record_wall_set_tmap_num2(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1234 {
1235         stop_time();
1236         nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM2);
1237         nd_write_short(seg);
1238         nd_write_byte(side);
1239         nd_write_short(cseg);
1240         nd_write_byte(cside);
1241         nd_write_short(tmap);
1242         start_time();
1243 }
1244
1245 void newdemo_record_multi_cloak(int pnum)
1246 {
1247         stop_time();
1248         nd_write_byte(ND_EVENT_MULTI_CLOAK);
1249         nd_write_byte((byte)pnum);
1250         start_time();
1251 }
1252
1253 void newdemo_record_multi_decloak(int pnum)
1254 {
1255         stop_time();
1256         nd_write_byte(ND_EVENT_MULTI_DECLOAK);
1257         nd_write_byte((byte)pnum);
1258         start_time();
1259 }
1260
1261 void newdemo_record_multi_death(int pnum)
1262 {
1263         stop_time();
1264         nd_write_byte(ND_EVENT_MULTI_DEATH);
1265         nd_write_byte((byte)pnum);
1266         start_time();
1267 }
1268
1269 void newdemo_record_multi_kill(int pnum, byte kill)
1270 {
1271         stop_time();
1272         nd_write_byte(ND_EVENT_MULTI_KILL);
1273         nd_write_byte((byte)pnum);
1274         nd_write_byte(kill);
1275         start_time();
1276 }
1277
1278 void newdemo_record_multi_connect(int pnum, int new_player, char *new_callsign)
1279 {
1280         stop_time();
1281         nd_write_byte(ND_EVENT_MULTI_CONNECT);
1282         nd_write_byte((byte)pnum);
1283         nd_write_byte((byte)new_player);
1284         if (!new_player) {
1285                 nd_write_string(Players[pnum].callsign);
1286                 nd_write_int(Players[pnum].net_killed_total);
1287                 nd_write_int(Players[pnum].net_kills_total);
1288         }
1289         nd_write_string(new_callsign);
1290         start_time();
1291 }
1292
1293 void newdemo_record_multi_reconnect(int pnum)
1294 {
1295         stop_time();
1296         nd_write_byte(ND_EVENT_MULTI_RECONNECT);
1297         nd_write_byte((byte)pnum);
1298         start_time();
1299 }
1300
1301 void newdemo_record_multi_disconnect(int pnum)
1302 {
1303         stop_time();
1304         nd_write_byte(ND_EVENT_MULTI_DISCONNECT);
1305         nd_write_byte((byte)pnum);
1306         start_time();
1307 }
1308
1309 void newdemo_record_player_score(int score)
1310 {
1311         stop_time();
1312         nd_write_byte(ND_EVENT_PLAYER_SCORE);
1313         nd_write_int(score);
1314         start_time();
1315 }
1316
1317 void newdemo_record_multi_score(int pnum, int score)
1318 {
1319         stop_time();
1320         nd_write_byte(ND_EVENT_MULTI_SCORE);
1321         nd_write_byte((byte)pnum);
1322         nd_write_int(score - Players[pnum].score);              // called before score is changed!!!!
1323         start_time();
1324 }
1325
1326 void newdemo_record_primary_ammo(int old_ammo, int new_ammo)
1327 {
1328         stop_time();
1329         nd_write_byte(ND_EVENT_PRIMARY_AMMO);
1330         if (old_ammo < 0)
1331                 nd_write_short((short)new_ammo);
1332         else
1333                 nd_write_short((short)old_ammo);
1334         nd_write_short((short)new_ammo);
1335         start_time();
1336 }
1337
1338 void newdemo_record_secondary_ammo(int old_ammo, int new_ammo)
1339 {
1340         stop_time();
1341         nd_write_byte(ND_EVENT_SECONDARY_AMMO);
1342         if (old_ammo < 0)
1343                 nd_write_short((short)new_ammo);
1344         else
1345                 nd_write_short((short)old_ammo);
1346         nd_write_short((short)new_ammo);
1347         start_time();
1348 }
1349
1350 void newdemo_record_door_opening(int segnum, int side)
1351 {
1352         stop_time();
1353         nd_write_byte(ND_EVENT_DOOR_OPENING);
1354         nd_write_short((short)segnum);
1355         nd_write_byte((byte)side);
1356         start_time();
1357 }
1358
1359 void newdemo_record_laser_level(byte old_level, byte new_level)
1360 {
1361         stop_time();
1362         nd_write_byte(ND_EVENT_LASER_LEVEL);
1363         nd_write_byte(old_level);
1364         nd_write_byte(new_level);
1365         start_time();
1366 }
1367
1368 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)
1369 {
1370         Assert(front_wall_num <= 255 && back_wall_num <= 255);
1371
1372         stop_time();
1373         nd_write_byte(ND_EVENT_CLOAKING_WALL);
1374         nd_write_byte(front_wall_num);
1375         nd_write_byte(back_wall_num);
1376         nd_write_byte(type);
1377         nd_write_byte(state);
1378         nd_write_byte(cloak_value);
1379         nd_write_short(l0>>8); 
1380         nd_write_short(l1>>8);
1381         nd_write_short(l2>>8);
1382         nd_write_short(l3>>8);
1383         start_time();
1384 }
1385
1386 void newdemo_set_new_level(int level_num)
1387 {
1388         int i;
1389         int side;
1390         segment *seg;
1391
1392         stop_time();
1393         nd_write_byte(ND_EVENT_NEW_LEVEL);
1394         nd_write_byte((byte)level_num);
1395         nd_write_byte((byte)Current_level_num);
1396  
1397    if (JustStartedRecording==1)
1398          {
1399                 nd_write_int(Num_walls);
1400                 for (i=0;i<Num_walls;i++)
1401                  {
1402              nd_write_byte (Walls[i].type);
1403                   nd_write_byte (Walls[i].flags);
1404                   nd_write_byte (Walls[i].state);  
1405
1406                   seg = &Segments[Walls[i].segnum];
1407                   side = Walls[i].sidenum;
1408                   nd_write_short (seg->sides[side].tmap_num);
1409                   nd_write_short (seg->sides[side].tmap_num2);
1410                   JustStartedRecording=0;
1411             }
1412          }
1413                 
1414         start_time();
1415 }
1416
1417 int newdemo_read_demo_start(int rnd_demo)
1418 {
1419         byte i, version, game_type, laser_level;
1420         char c, energy, shield;
1421         char text[50], current_mission[9];
1422
1423         nd_read_byte(&c);
1424         if ((c != ND_EVENT_START_DEMO) || nd_bad_read) {
1425                 newmenu_item m[1];
1426
1427                 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_CORRUPT);
1428                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1429                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1430                 return 1;
1431         }
1432         nd_read_byte(&version);
1433         nd_read_byte(&game_type);
1434         if (game_type < DEMO_GAME_TYPE) {
1435                 newmenu_item m[2];
1436
1437                 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1438                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1439                 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = "    In Descent: First Strike";
1440         
1441                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1442                 return 1;
1443         }
1444         if (game_type != DEMO_GAME_TYPE) {
1445                 newmenu_item m[2];
1446
1447                 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1448                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1449                 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = "   In Unknown Descent version";
1450         
1451                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1452                 return 1;
1453         }
1454         if (version < DEMO_VERSION) {
1455                 if (!rnd_demo) {
1456                         newmenu_item m[1];
1457                         sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_OLD);
1458                         m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1459                         newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1460                 }
1461                 return 1;
1462         }
1463         nd_read_fix(&GameTime);
1464    Boss_cloak_start_time=Boss_cloak_end_time=GameTime;
1465         JasonPlaybackTotal=0;
1466  
1467         nd_read_int(&Newdemo_game_mode);
1468
1469 #ifdef NETWORK
1470         change_playernum_to((Newdemo_game_mode >> 16) & 0x7);
1471         if (Newdemo_game_mode & GM_TEAM) {
1472                 nd_read_byte(&(Netgame.team_vector));
1473                 nd_read_string(Netgame.team_name[0]);
1474                 nd_read_string(Netgame.team_name[1]);
1475         }
1476         if (Newdemo_game_mode & GM_MULTI) {
1477
1478                 multi_new_game();
1479                 nd_read_byte(&c);
1480                 N_players = (int)c;
1481 // changed this to above two lines -- breaks on the mac because of
1482 // endian issues
1483 //              nd_read_byte((byte *)&N_players);
1484                 for (i = 0 ; i < N_players; i++) {
1485                         Players[i].cloak_time = 0;
1486                         Players[i].invulnerable_time = 0;
1487                         nd_read_string(Players[i].callsign);
1488                         nd_read_byte(&(Players[i].connected));
1489
1490                         if (Newdemo_game_mode & GM_MULTI_COOP) {
1491                                 nd_read_int(&(Players[i].score));
1492                         } else {
1493                                 nd_read_short((short *)&(Players[i].net_killed_total));
1494                                 nd_read_short((short *)&(Players[i].net_kills_total));
1495                         }
1496                 }
1497                 Game_mode = Newdemo_game_mode;
1498                 multi_sort_kill_list();
1499                 Game_mode = GM_NORMAL;
1500         } else
1501 #endif
1502                 nd_read_int(&(Players[Player_num].score));              // Note link to above if!
1503
1504         for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
1505                 nd_read_short((short*)&(Players[Player_num].primary_ammo[i]));
1506
1507         for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
1508                         nd_read_short((short*)&(Players[Player_num].secondary_ammo[i]));
1509
1510         nd_read_byte(&laser_level);
1511         if (laser_level != Players[Player_num].laser_level) {
1512                 Players[Player_num].laser_level = laser_level;
1513                 update_laser_weapon_info();
1514         }
1515
1516 // Support for missions
1517
1518         nd_read_string(current_mission);
1519         if (!load_mission_by_name(current_mission)) {
1520                 if (!rnd_demo) {
1521                         newmenu_item m[1];
1522
1523                         sprintf(text, TXT_NOMISSION4DEMO, current_mission);
1524                         m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1525                         newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1526                 }
1527                 return 1;
1528         }
1529
1530         nd_recorded_total = 0;
1531         nd_playback_total = 0;
1532         nd_read_byte(&energy);
1533         nd_read_byte(&shield);
1534
1535         nd_read_int((int *)&(Players[Player_num].flags));
1536         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
1537                 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1538                 Newdemo_players_cloaked |= (1 << Player_num);
1539         }
1540         if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
1541                 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1542
1543         nd_read_byte((byte *)&Primary_weapon);
1544         nd_read_byte((byte *)&Secondary_weapon);
1545
1546 // Next bit of code to fix problem that I introduced between 1.0 and 1.1
1547 // check the next byte -- it _will_ be a load_new_level event.  If it is
1548 // not, then we must shift all bytes up by one.
1549
1550         Players[Player_num].energy = i2f(energy);
1551         Players[Player_num].shields = i2f(shield);
1552         JustStartedPlayback=1;
1553         return 0;
1554 }
1555
1556 void newdemo_pop_ctrlcen_triggers()
1557 {
1558         int anim_num, n, i;
1559         int side, cside;
1560         segment *seg, *csegp;
1561
1562         for (i = 0; i < ControlCenterTriggers.num_links; i++)   {
1563                 seg = &Segments[ControlCenterTriggers.seg[i]];
1564                 side = ControlCenterTriggers.side[i];
1565                 csegp = &Segments[seg->children[side]];
1566                 cside = find_connect_side(seg, csegp);
1567                 anim_num = Walls[seg->sides[side].wall_num].clip_num;
1568                 n = WallAnims[anim_num].num_frames;
1569                 if (WallAnims[anim_num].flags & WCF_TMAP1)      {
1570                 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[n-1];
1571                 } else {
1572                         seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[n-1];
1573                 }
1574         }
1575 }
1576
1577 #define N_PLAYER_SHIP_TEXTURES 6
1578
1579 void nd_render_extras (ubyte,object *);
1580 extern void multi_apply_goal_textures ();
1581 ubyte Newdemo_flying_guided=0;
1582
1583 int newdemo_read_frame_information()
1584 {
1585         int done, segnum, side, objnum, soundno, angle, volume, i,shot;
1586         object *obj;
1587         ubyte c,WhichWindow;
1588         static byte saved_letter_cockpit;
1589         static byte saved_rearview_cockpit;
1590    object extraobj;
1591    static char LastReadValue=101;
1592    segment *seg;        
1593
1594         done = 0;
1595
1596         if (Newdemo_vcr_state != ND_STATE_PAUSED)
1597                 for (segnum=0; segnum <= Highest_segment_index; segnum++)
1598                         Segments[segnum].objects = -1;
1599
1600         reset_objects(1);
1601         Players[Player_num].homing_object_dist = -F1_0;
1602
1603         prev_obj = NULL;
1604
1605         while( !done )  {
1606                 nd_read_byte(&c);
1607                 if (nd_bad_read) { done = -1; break; }
1608
1609                 switch( c )     {
1610
1611                 case ND_EVENT_START_FRAME:      {                               // Followed by an integer frame number, then a fix FrameTime
1612                         short last_frame_length;
1613
1614                         done=1;
1615                         nd_read_short(&last_frame_length);
1616                         nd_read_int(&NewdemoFrameCount);
1617                         nd_read_int((int *)&nd_recorded_time);
1618                         if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1619                                 nd_recorded_total += nd_recorded_time;
1620                         NewdemoFrameCount--;
1621                                         
1622                         if (nd_bad_read) { done = -1; break; }
1623                         break;
1624                 }
1625
1626                 case ND_EVENT_VIEWER_OBJECT:                            // Followed by an object structure
1627          nd_read_byte (&WhichWindow);
1628          if (WhichWindow&15)
1629                           { 
1630 //                               mprintf ((0,"Reading extra!\n"));
1631              nd_read_object (&extraobj);                                
1632                                  if (Newdemo_vcr_state!=ND_STATE_PAUSED)
1633                                   {
1634                                         if (nd_bad_read) { done = -1; break; }
1635                                                 
1636                            nd_render_extras (WhichWindow,&extraobj);    
1637                                   }
1638                           }
1639                         else
1640                           {     
1641   //                            mprintf ((0,"Reading viewer!\n"));
1642                                 //Viewer=&Objects[0];
1643                         nd_read_object(Viewer);
1644
1645                                 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1646                                         if (nd_bad_read) { done = -1; break; }
1647                                         segnum = Viewer->segnum;
1648                                         Viewer->next = Viewer->prev = Viewer->segnum = -1;
1649
1650 // HACK HACK HACK -- since we have multiple level recording, it can be the case
1651 // HACK HACK HACK -- that when rewinding the demo, the viewer is in a segment
1652 // HACK HACK HACK -- that is greater than the highest index of segments.  Bash
1653 // HACK HACK HACK -- the viewer to segment 0 for bogus view.
1654
1655                                         if (segnum > Highest_segment_index)
1656                                                 segnum = 0;
1657                                         obj_link(Viewer-Objects,segnum);
1658                                 }
1659                           }
1660                         break;
1661
1662                 case ND_EVENT_RENDER_OBJECT:                       // Followed by an object structure
1663                         objnum = obj_allocate();
1664                         if (objnum==-1)
1665                                 break;
1666                         obj = &Objects[objnum];
1667                         nd_read_object(obj);
1668                         if (nd_bad_read) { done = -1; break; }
1669                         if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1670                                 segnum = obj->segnum;
1671                                 obj->next = obj->prev = obj->segnum = -1;
1672
1673 // HACK HACK HACK -- don't render objects is segments greater than Highest_segment_index
1674 // HACK HACK HACK -- (see above)
1675
1676                                 if (segnum > Highest_segment_index)
1677                                         break;
1678
1679                                 obj_link(obj-Objects,segnum);
1680 #ifdef NETWORK
1681                                 if ((obj->type == OBJ_PLAYER) && (Newdemo_game_mode & GM_MULTI)) {
1682                                         int player;
1683
1684                                         if (Newdemo_game_mode & GM_TEAM)
1685                                                 player = get_team(obj->id);
1686                                         else
1687                                                 player = obj->id;
1688                                         if (player == 0)
1689                                                 break;
1690                                         player--;
1691
1692                                         for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
1693                                                 multi_player_textures[player][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[obj->rtype.pobj_info.model_num].first_texture+i]];
1694
1695                                         multi_player_textures[player][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2]];
1696                                         multi_player_textures[player][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2+1]];
1697                                         obj->rtype.pobj_info.alt_textures = player+1;
1698                                 }
1699 #endif
1700                         }
1701                         break;
1702
1703                 case ND_EVENT_SOUND:
1704                         nd_read_int(&soundno);
1705                         if (nd_bad_read) {done = -1; break; }
1706                         if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1707                                 digi_play_sample( soundno, F1_0 );
1708                         break;
1709
1710 //--unused              case ND_EVENT_SOUND_ONCE:
1711 //--unused                      nd_read_int(&soundno);
1712 //--unused                      if (nd_bad_read) { done = -1; break; }
1713 //--unused                      if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1714 //--unused                              digi_play_sample_once( soundno, F1_0 );
1715 //--unused                      break;
1716
1717                 case ND_EVENT_SOUND_3D:
1718                         nd_read_int(&soundno);
1719                         nd_read_int(&angle);
1720                         nd_read_int(&volume);
1721                         if (nd_bad_read) { done = -1; break; }
1722                         if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1723                                 digi_play_sample_3d( soundno, angle, volume, 0 );
1724                         break;
1725
1726                 case ND_EVENT_SOUND_3D_ONCE:
1727                         nd_read_int(&soundno);
1728                         nd_read_int(&angle);
1729                         nd_read_int(&volume);
1730                         if (nd_bad_read) { done = -1; break; }
1731                         if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1732                                 digi_play_sample_3d( soundno, angle, volume, 1 );
1733                         break;
1734
1735                 case ND_EVENT_LINK_SOUND_TO_OBJ:
1736                         {
1737                                 int soundno, objnum, max_volume, max_distance, loop_start, loop_end;
1738                                 int signature;
1739                                 nd_read_int( &soundno );
1740                                 nd_read_int( &signature );
1741                                 nd_read_int( &max_volume );
1742                                 nd_read_int( &max_distance );
1743                                 nd_read_int( &loop_start );
1744                                 nd_read_int( &loop_end );
1745                                 objnum = newdemo_find_object( signature );
1746                                 if ( objnum > -1 )      {       //      @mk, 2/22/96, John told me to.
1747                                         digi_link_sound_to_object3( soundno, objnum, 1, max_volume, max_distance, loop_start, loop_end );
1748                                 }
1749                         }
1750                         break;
1751
1752                 case ND_EVENT_KILL_SOUND_TO_OBJ:
1753                         {
1754                                 int objnum, signature;
1755                                 nd_read_int( &signature );
1756                                 objnum = newdemo_find_object( signature );
1757                                 if ( objnum > -1 )      {       //      @mk, 2/22/96, John told me to.
1758                                         digi_kill_sound_linked_to_object(objnum);
1759                                 }
1760                         }
1761                         break;
1762
1763                 case ND_EVENT_WALL_HIT_PROCESS: {
1764                         int player, segnum;
1765                         fix damage;
1766
1767                         nd_read_int(&segnum);
1768                         nd_read_int(&side);
1769                         nd_read_fix(&damage);
1770                         nd_read_int(&player);
1771                         if (nd_bad_read) { done = -1; break; }
1772                         if (Newdemo_vcr_state != ND_STATE_PAUSED)
1773                                 wall_hit_process(&Segments[segnum], side, damage, player, &(Objects[0]) );
1774                         break;
1775                 }
1776
1777                 case ND_EVENT_TRIGGER:
1778                         nd_read_int(&segnum);
1779                         nd_read_int(&side);
1780                         nd_read_int(&objnum);
1781                         nd_read_int(&shot);
1782                         if (nd_bad_read) { done = -1; break; }
1783                         if (Newdemo_vcr_state != ND_STATE_PAUSED)
1784                         {
1785                                 mprintf ((0,"EVENT TRIGGER! shot=%d\n",shot));
1786
1787                                 if (Triggers[Walls[Segments[segnum].sides[side].wall_num].trigger].type == TT_SECRET_EXIT) {
1788                                         int     truth;
1789
1790                                         nd_read_byte(&c);
1791                                         Assert(c == ND_EVENT_SECRET_THINGY);
1792                                         nd_read_int(&truth);
1793                                         if (!truth)
1794                                                 check_trigger(&Segments[segnum], side, objnum,shot);
1795                                 } else
1796                                         check_trigger(&Segments[segnum], side, objnum,shot);
1797                         }
1798                         break;
1799
1800                 case ND_EVENT_HOSTAGE_RESCUED: {
1801                         int hostage_number;
1802
1803                         nd_read_int(&hostage_number);
1804                         if (nd_bad_read) { done = -1; break; }
1805                         if (Newdemo_vcr_state != ND_STATE_PAUSED)
1806                                 hostage_rescue( hostage_number );
1807                         break;
1808                 }
1809
1810                 case ND_EVENT_MORPH_FRAME: {
1811 #if 0
1812                         morph_data *md;
1813
1814                         md = &morph_objects[0];
1815                         if (newdemo_read( md->morph_vecs, sizeof(md->morph_vecs), 1 )!=1) { done=-1; break; }
1816                         if (newdemo_read( md->submodel_active, sizeof(md->submodel_active), 1 )!=1) { done=-1; break; }
1817                         if (newdemo_read( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 )!=1) { done=-1; break; }
1818 #endif
1819                         objnum = obj_allocate();
1820                         if (objnum==-1)
1821                                 break;
1822                         obj = &Objects[objnum];
1823                         nd_read_object(obj);
1824                         obj->render_type = RT_POLYOBJ;
1825                         if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1826                                 if (nd_bad_read) { done = -1; break; }
1827                                 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1828                                         segnum = obj->segnum;
1829                                         obj->next = obj->prev = obj->segnum = -1;
1830                                         obj_link(obj-Objects,segnum);
1831                                 }
1832                         }
1833                         break;
1834                 }
1835
1836                 case ND_EVENT_WALL_TOGGLE:
1837                         nd_read_int(&segnum);
1838                         nd_read_int(&side);
1839                         if (nd_bad_read) {done = -1; break; }
1840                         if (Newdemo_vcr_state != ND_STATE_PAUSED)
1841                                 wall_toggle(&Segments[segnum], side);
1842                         break;
1843
1844                 case ND_EVENT_CONTROL_CENTER_DESTROYED:
1845                         nd_read_int(&Countdown_seconds_left);
1846                         Control_center_destroyed = 1;
1847                         if (nd_bad_read) { done = -1; break; }
1848                         if (!Newdemo_cntrlcen_destroyed) {
1849                                 newdemo_pop_ctrlcen_triggers();
1850                                 Newdemo_cntrlcen_destroyed = 1;
1851 //                              do_controlcen_destroyed_stuff(NULL);
1852                         }
1853                         break;
1854
1855                 case ND_EVENT_HUD_MESSAGE: {
1856                         char hud_msg[60];
1857
1858                         nd_read_string(&(hud_msg[0]));
1859                         if (nd_bad_read) { done = -1; break; }
1860                         HUD_init_message( hud_msg );
1861                         break;
1862                         }
1863            case ND_EVENT_START_GUIDED:
1864                         Newdemo_flying_guided=1;
1865                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) 
1866                                 Newdemo_flying_guided=0;
1867                         break;
1868            case ND_EVENT_END_GUIDED:
1869                         Newdemo_flying_guided=0;
1870                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) 
1871                                 Newdemo_flying_guided=1;
1872                         break;
1873
1874                 case ND_EVENT_PALETTE_EFFECT: {
1875                         short r, g, b;
1876
1877                         nd_read_short(&r);
1878                         nd_read_short(&g);
1879                         nd_read_short(&b);
1880                         if (nd_bad_read) { done = -1; break; }
1881                         PALETTE_FLASH_SET(r,g,b);
1882                         break;
1883                 }
1884
1885                 case ND_EVENT_PLAYER_ENERGY: {
1886                         ubyte energy;
1887                         ubyte old_energy;
1888
1889                         nd_read_byte(&old_energy);
1890                         nd_read_byte(&energy);
1891                         if (nd_bad_read) {done = -1; break; }
1892                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1893                                 Players[Player_num].energy = i2f(energy);
1894                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1895                                 if (old_energy != 255)
1896                                         Players[Player_num].energy = i2f(old_energy);
1897                         }
1898                         break;
1899                 }
1900
1901                 case ND_EVENT_PLAYER_AFTERBURNER: {
1902                         ubyte afterburner;
1903                         ubyte old_afterburner;
1904
1905                         nd_read_byte(&old_afterburner);
1906                         nd_read_byte(&afterburner);
1907                         if (nd_bad_read) {done = -1; break; }
1908                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1909                                 Afterburner_charge = afterburner<<9;
1910                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1911                                 if (old_afterburner != 255)
1912                                         Afterburner_charge = old_afterburner<<9;
1913                         }
1914                         break;
1915                 }
1916
1917                 case ND_EVENT_PLAYER_SHIELD: {
1918                         ubyte shield;
1919                         ubyte old_shield;
1920
1921                         nd_read_byte(&old_shield);
1922                         nd_read_byte(&shield);
1923                         if (nd_bad_read) {done = -1; break; }
1924                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1925                                 Players[Player_num].shields = i2f(shield);
1926                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1927                                 if (old_shield != 255)
1928                                         Players[Player_num].shields = i2f(old_shield);
1929                         }
1930                         break;
1931                 }
1932
1933                 case ND_EVENT_PLAYER_FLAGS: {
1934                         uint oflags;
1935
1936                         nd_read_int((int *)&(Players[Player_num].flags));
1937                         if (nd_bad_read) {done = -1; break; }
1938
1939                         oflags = Players[Player_num].flags >> 16;
1940                         Players[Player_num].flags &= 0xffff;
1941
1942                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || ((Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) && (oflags != 0xffff)) ) {
1943                                 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1944                                         Players[Player_num].cloak_time = 0;
1945                                         Newdemo_players_cloaked &= ~(1 << Player_num);
1946                                 }
1947                                 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1948                                         Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1949                                         Newdemo_players_cloaked |= (1 << Player_num);
1950                                 }
1951                                 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1952                                         Players[Player_num].invulnerable_time = 0;
1953                                 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1954                                         Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1955                                 Players[Player_num].flags = oflags;
1956                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1957                                 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1958                                         Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1959                                         Newdemo_players_cloaked |= (1 << Player_num);
1960                                 }
1961                                 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1962                                         Players[Player_num].cloak_time = 0;
1963                                         Newdemo_players_cloaked &= ~(1 << Player_num);
1964                                 }
1965                                 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1966                                         Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1967                                 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1968                                         Players[Player_num].invulnerable_time = 0;
1969                         }
1970                         update_laser_weapon_info();             // in case of quad laser change
1971                         break;
1972                 }
1973
1974                 case ND_EVENT_PLAYER_WEAPON: {
1975                         byte weapon_type, weapon_num;
1976                         byte old_weapon;
1977
1978                         nd_read_byte(&weapon_type);
1979                         nd_read_byte(&weapon_num);
1980                         nd_read_byte(&old_weapon);
1981                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1982                                 if (weapon_type == 0)
1983                                         Primary_weapon = (int)weapon_num;
1984                                 else
1985                                         Secondary_weapon = (int)weapon_num;
1986                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1987                                 if (weapon_type == 0)
1988                                         Primary_weapon = (int)old_weapon;
1989                                 else
1990                                         Secondary_weapon = (int)old_weapon;
1991                         }
1992                         break;
1993                 }
1994
1995                 case ND_EVENT_EFFECT_BLOWUP: {
1996                         short segnum;
1997                         byte side;
1998                         vms_vector pnt;
1999                         object dummy;
2000
2001                         //create a dummy object which will be the weapon that hits 
2002                         //the monitor. the blowup code wants to know who the parent of the
2003                         //laser is, so create a laser whose parent is the player
2004                         dummy.ctype.laser_info.parent_type = OBJ_PLAYER;
2005
2006                         nd_read_short(&segnum);
2007                         nd_read_byte(&side);
2008                         nd_read_vector(&pnt);
2009                         if (Newdemo_vcr_state != ND_STATE_PAUSED)
2010                                 check_effect_blowup(&(Segments[segnum]), side, &pnt, &dummy, 0);
2011                         break;
2012                 }
2013
2014                 case ND_EVENT_HOMING_DISTANCE: {
2015                         short distance;
2016
2017                         nd_read_short(&distance);
2018                         Players[Player_num].homing_object_dist = i2f((int)(distance << 16));
2019                         break;
2020                 }
2021
2022                 case ND_EVENT_LETTERBOX:
2023                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2024                                 saved_letter_cockpit = Cockpit_mode;
2025                                 select_cockpit(CM_LETTERBOX);
2026                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2027                                 select_cockpit(saved_letter_cockpit);
2028                         break;
2029
2030       case ND_EVENT_CHANGE_COCKPIT:
2031                   {
2032                         int dummy;
2033
2034                    nd_read_int (&dummy);
2035                         select_cockpit (dummy);
2036
2037                    break;
2038                   }
2039                 case ND_EVENT_REARVIEW:
2040                         if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2041                                 saved_rearview_cockpit = Cockpit_mode;
2042                                 if (Cockpit_mode == CM_FULL_COCKPIT)
2043                                         select_cockpit(CM_REAR_VIEW);
2044                                 Rear_view=1;
2045                         } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2046                                 if (saved_rearview_cockpit == CM_REAR_VIEW)             // hack to be sure we get a good cockpit on restore
2047                                         saved_rearview_cockpit = CM_FULL_COCKPIT;
2048                                 select_cockpit(saved_rearview_cockpit);
2049                                 Rear_view=0;
2050                         }
2051                         break;
2052
2053                 case ND_EVENT_RESTORE_COCKPIT:
2054                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2055                                 saved_letter_cockpit = Cockpit_mode;
2056                                 select_cockpit(CM_LETTERBOX);
2057                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2058                                 select_cockpit(saved_letter_cockpit);
2059                         break;
2060
2061
2062                 case ND_EVENT_RESTORE_REARVIEW:
2063                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2064                                 saved_rearview_cockpit = Cockpit_mode;
2065                                 if (Cockpit_mode == CM_FULL_COCKPIT)
2066                                         select_cockpit(CM_REAR_VIEW);
2067                                 Rear_view=1;
2068                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2069                                 if (saved_rearview_cockpit == CM_REAR_VIEW)             // hack to be sure we get a good cockpit on restore
2070                                         saved_rearview_cockpit = CM_FULL_COCKPIT;
2071                                 select_cockpit(saved_rearview_cockpit);
2072                                 Rear_view=0;
2073                         }
2074                         break;
2075
2076
2077                 case ND_EVENT_WALL_SET_TMAP_NUM1:       {
2078                         short seg, cseg, tmap;
2079                         ubyte side,cside;
2080
2081                         nd_read_short(&seg);
2082                         nd_read_byte(&side);
2083                         nd_read_short(&cseg);
2084                         nd_read_byte(&cside);
2085                         nd_read_short( &tmap );
2086                         if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD))
2087                                 Segments[seg].sides[side].tmap_num = Segments[cseg].sides[cside].tmap_num = tmap;
2088                         break;
2089                 }
2090
2091                 case ND_EVENT_WALL_SET_TMAP_NUM2:       {
2092                         short seg, cseg, tmap;
2093                         ubyte side,cside;
2094
2095                         nd_read_short(&seg);
2096                         nd_read_byte(&side);
2097                         nd_read_short(&cseg);
2098                         nd_read_byte(&cside);
2099                         nd_read_short( &tmap );
2100                         if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) {
2101                                 Assert(tmap!=0 && Segments[seg].sides[side].tmap_num2!=0);
2102                                 Segments[seg].sides[side].tmap_num2 = Segments[cseg].sides[cside].tmap_num2 = tmap;
2103                         }
2104                         break;
2105                 }
2106
2107                 case ND_EVENT_MULTI_CLOAK: {
2108                         byte pnum;
2109
2110                         nd_read_byte(&pnum);
2111                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2112                                 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2113                                 Players[pnum].cloak_time = 0;
2114                                 Newdemo_players_cloaked &= ~(1 << pnum);
2115                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2116                                 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2117                                 Players[pnum].cloak_time = GameTime  - (CLOAK_TIME_MAX / 2);
2118                                 Newdemo_players_cloaked |= (1 << pnum);
2119                         }
2120                         break;
2121                 }
2122
2123                 case ND_EVENT_MULTI_DECLOAK: {
2124                         byte pnum;
2125
2126                         nd_read_byte(&pnum);
2127
2128                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2129                                 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2130                                 Players[pnum].cloak_time = GameTime  - (CLOAK_TIME_MAX / 2);
2131                                 Newdemo_players_cloaked |= (1 << pnum);
2132                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2133                                 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2134                                 Players[pnum].cloak_time = 0;
2135                                 Newdemo_players_cloaked &= ~(1 << pnum);
2136                         }
2137                         break;
2138                 }
2139
2140                 case ND_EVENT_MULTI_DEATH: {
2141                         byte pnum;
2142
2143                         nd_read_byte(&pnum);
2144                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2145                                 Players[pnum].net_killed_total--;
2146                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2147                                 Players[pnum].net_killed_total++;
2148                         break;
2149                 }
2150
2151 #ifdef NETWORK
2152                 case ND_EVENT_MULTI_KILL: {
2153                         byte pnum, kill;
2154
2155                         nd_read_byte(&pnum);
2156                         nd_read_byte(&kill);
2157                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2158                                 Players[pnum].net_kills_total -= kill;
2159                                 if (Newdemo_game_mode & GM_TEAM)
2160                                         team_kills[get_team(pnum)] -= kill;
2161                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2162                                 Players[pnum].net_kills_total += kill;
2163                                 if (Newdemo_game_mode & GM_TEAM)
2164                                         team_kills[get_team(pnum)] += kill;
2165                         }
2166                         Game_mode = Newdemo_game_mode;
2167                         multi_sort_kill_list();
2168                         Game_mode = GM_NORMAL;
2169                         break;
2170                 }
2171                 
2172                 case ND_EVENT_MULTI_CONNECT: {
2173                         byte pnum, new_player;
2174                         int killed_total, kills_total;
2175                         char new_callsign[CALLSIGN_LEN+1], old_callsign[CALLSIGN_LEN+1];
2176
2177                         nd_read_byte(&pnum);
2178                         nd_read_byte(&new_player);
2179                         if (!new_player) {
2180                                 nd_read_string(old_callsign);
2181                                 nd_read_int(&killed_total);
2182                                 nd_read_int(&kills_total);
2183                         }
2184                         nd_read_string(new_callsign);
2185                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2186                                 Players[pnum].connected = 0;
2187                                 if (!new_player) {
2188                                         memcpy(Players[pnum].callsign, old_callsign, CALLSIGN_LEN+1);
2189                                         Players[pnum].net_killed_total = killed_total;
2190                                         Players[pnum].net_kills_total = kills_total;
2191                                 } else {
2192                                         N_players--;
2193                                 }
2194                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2195                                 Players[pnum].connected = 1;
2196                                 Players[pnum].net_kills_total = 0;
2197                                 Players[pnum].net_killed_total = 0;
2198                                 memcpy(Players[pnum].callsign, new_callsign, CALLSIGN_LEN+1);
2199                                 if (new_player)
2200                                         N_players++;
2201                         }
2202                         break;
2203                 }
2204
2205                 case ND_EVENT_MULTI_RECONNECT: {
2206                         byte pnum;
2207
2208                         nd_read_byte(&pnum);
2209                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2210                                 Players[pnum].connected = 0;
2211                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2212                                 Players[pnum].connected = 1;
2213                         break;
2214                 }
2215
2216                 case ND_EVENT_MULTI_DISCONNECT: {
2217                         byte pnum;
2218
2219                         nd_read_byte(&pnum);
2220                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2221                                 Players[pnum].connected = 1;
2222                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2223                                 Players[pnum].connected = 0;
2224                         break;
2225                 }
2226
2227                 case ND_EVENT_MULTI_SCORE: {
2228                         int score;
2229                         byte pnum;
2230
2231                         nd_read_byte(&pnum);
2232                         nd_read_int(&score);
2233                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2234                                 Players[pnum].score -= score;
2235                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2236                                 Players[pnum].score += score;
2237                         Game_mode = Newdemo_game_mode;
2238                         multi_sort_kill_list();
2239                         Game_mode = GM_NORMAL;
2240                         break;
2241                 }
2242
2243 #endif // NETWORK
2244                 case ND_EVENT_PLAYER_SCORE: {
2245                         int score;
2246
2247                         nd_read_int(&score);
2248                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2249                                 Players[Player_num].score -= score;
2250                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2251                                 Players[Player_num].score += score;
2252                         break;
2253                 }
2254
2255
2256                 case ND_EVENT_PRIMARY_AMMO: {
2257                         short old_ammo, new_ammo;
2258
2259                         nd_read_short(&old_ammo);
2260                         nd_read_short(&new_ammo);
2261
2262                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2263                                 Players[Player_num].primary_ammo[Primary_weapon] = old_ammo;
2264                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2265                                 Players[Player_num].primary_ammo[Primary_weapon] = new_ammo;
2266                         break;
2267                 }
2268
2269                 case ND_EVENT_SECONDARY_AMMO: {
2270                         short old_ammo, new_ammo;
2271
2272                         nd_read_short(&old_ammo);
2273                         nd_read_short(&new_ammo);
2274
2275                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2276                                 Players[Player_num].secondary_ammo[Secondary_weapon] = old_ammo;
2277                         else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2278                                 Players[Player_num].secondary_ammo[Secondary_weapon] = new_ammo;
2279                         break;
2280                 }
2281
2282                 case ND_EVENT_DOOR_OPENING: {
2283                         short segnum;
2284                         byte side;
2285
2286                         nd_read_short(&segnum);
2287                         nd_read_byte(&side);
2288                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2289                                 int anim_num;
2290                                 int cside;
2291                                 segment *segp, *csegp;
2292
2293                                 segp = &Segments[segnum];
2294                                 csegp = &Segments[segp->children[side]];
2295                                 cside = find_connect_side(segp, csegp);
2296                                 anim_num = Walls[segp->sides[side].wall_num].clip_num;
2297
2298                                 if (WallAnims[anim_num].flags & WCF_TMAP1)      {
2299                                         segp->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[0];
2300                                 } else  {
2301                                         segp->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[0];
2302                                 }
2303                         }
2304                         break;
2305                 }
2306
2307                 case ND_EVENT_LASER_LEVEL: {
2308                         byte old_level, new_level;
2309
2310                         nd_read_byte(&old_level);
2311                         nd_read_byte(&new_level);
2312                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2313                                 Players[Player_num].laser_level = old_level;
2314                                 update_laser_weapon_info();
2315                         } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2316                                 Players[Player_num].laser_level = new_level;
2317                                 update_laser_weapon_info();
2318                         }
2319                         break;
2320                 }
2321
2322                 case ND_EVENT_CLOAKING_WALL: {
2323                         ubyte back_wall_num,front_wall_num,type,state,cloak_value;
2324                         short l0,l1,l2,l3;
2325                         segment *segp;
2326                         int sidenum;
2327
2328                         nd_read_byte(&front_wall_num);
2329                         nd_read_byte(&back_wall_num);
2330                         nd_read_byte(&type);
2331                         nd_read_byte(&state);
2332                         nd_read_byte(&cloak_value);
2333                         nd_read_short(&l0);
2334                         nd_read_short(&l1);
2335                         nd_read_short(&l2);
2336                         nd_read_short(&l3);
2337
2338                         Walls[front_wall_num].type = type;
2339                         Walls[front_wall_num].state = state;
2340                         Walls[front_wall_num].cloak_value = cloak_value;
2341                         segp = &Segments[Walls[front_wall_num].segnum];
2342                         sidenum = Walls[front_wall_num].sidenum;
2343                         segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2344                         segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2345                         segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2346                         segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2347
2348                         Walls[back_wall_num].type = type;
2349                         Walls[back_wall_num].state = state;
2350                         Walls[back_wall_num].cloak_value = cloak_value;
2351                         segp = &Segments[Walls[back_wall_num].segnum];
2352                         sidenum = Walls[back_wall_num].sidenum;
2353                         segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2354                         segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2355                         segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2356                         segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2357
2358                         break;
2359                 }
2360
2361                 case ND_EVENT_NEW_LEVEL:        {
2362                         byte new_level, old_level, loaded_level;
2363  
2364                         nd_read_byte (&new_level);
2365                         nd_read_byte (&old_level);
2366                         if (Newdemo_vcr_state == ND_STATE_PAUSED)
2367                                 break;
2368
2369                         stop_time();
2370                         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2371                                 loaded_level = old_level;
2372                         else {
2373                                 loaded_level = new_level;
2374                                 for (i = 0; i < MAX_PLAYERS; i++) {
2375                                         Players[i].cloak_time = 0;
2376                                         Players[i].flags &= ~PLAYER_FLAGS_CLOAKED;
2377                                 }
2378                         }
2379                         if ((loaded_level < Last_secret_level) || (loaded_level > Last_level)) {
2380                                 newmenu_item m[3];
2381
2382                                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2383                                 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2384                                 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2385                                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2386                                 return -1;
2387                         }
2388
2389                         LoadLevel((int)loaded_level,1);
2390                         Newdemo_cntrlcen_destroyed = 0;
2391
2392                         if (JustStartedPlayback)
2393                          { 
2394                                 nd_read_int (&Num_walls);
2395                                 for (i=0;i<Num_walls;i++)    // restore the walls
2396                                         {
2397                                          nd_read_byte (&Walls[i].type);
2398                                          nd_read_byte (&Walls[i].flags);
2399                                          nd_read_byte (&Walls[i].state);
2400         
2401                                           seg = &Segments[Walls[i].segnum];
2402                                           side = Walls[i].sidenum;
2403                                           nd_read_short (&seg->sides[side].tmap_num);
2404                                           nd_read_short (&seg->sides[side].tmap_num2);
2405                                         }                                       
2406 #ifdef NETWORK
2407                           if (Newdemo_game_mode & GM_CAPTURE)
2408                         multi_apply_goal_textures ();   
2409 #endif
2410                           JustStartedPlayback=0;        
2411                     }
2412                 
2413         
2414 // so says Rob H.!!!                    if (Newdemo_game_mode & GM_MULTI) {
2415 // so says Rob H.!!!                            for (i = 0; i < Num_walls; i++) {
2416 // so says Rob H.!!!                                    if (Walls[i].type == WALL_BLASTABLE) 
2417 // so says Rob H.!!!                                    {
2418 // so says Rob H.!!!                                            int a, n;
2419 // so says Rob H.!!!                                            int side;
2420 // so says Rob H.!!!                                            segment *seg;
2421 // so says Rob H.!!!
2422 // so says Rob H.!!!                                            seg = &Segments[Walls[i].segnum];
2423 // so says Rob H.!!!                                            side = Walls[i].sidenum;
2424 // so says Rob H.!!!                                            a = Walls[i].clip_num;
2425 // so says Rob H.!!!                                            n = WallAnims[a].num_frames;
2426 // so says Rob H.!!!                                            seg->sides[side].tmap_num = WallAnims[a].frames[n-1];
2427 // so says Rob H.!!!                                            Walls[i].flags |= WALL_BLASTED;
2428 // so says Rob H.!!!                                    }
2429 // so says Rob H.!!!                            }
2430 // so says Rob H.!!!                    }
2431
2432                         reset_palette_add();                                    // get palette back to normal
2433                         start_time();
2434                         break;
2435                 }
2436
2437                 case ND_EVENT_EOF: {
2438                         done=-1;
2439                         fseek(infile, -1, SEEK_CUR);                                    // get back to the EOF marker
2440                         Newdemo_at_eof = 1;
2441                         NewdemoFrameCount++;
2442                         break;
2443                 }
2444
2445                 default:
2446                         Int3();
2447                 }
2448         }
2449           
2450    LastReadValue=c;
2451   
2452         if (nd_bad_read) {
2453                 newmenu_item m[2];
2454
2455                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_ERR_READING;
2456                 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_DEMO_OLD_CORRUPT;
2457                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2458         }
2459
2460         return done;
2461 }
2462
2463 void newdemo_goto_beginning()
2464 {
2465 //      if (NewdemoFrameCount == 0)
2466 //              return;
2467         fseek(infile, 0, SEEK_SET);
2468         Newdemo_vcr_state = ND_STATE_PLAYBACK;
2469         if (newdemo_read_demo_start(0))
2470                 newdemo_stop_playback();
2471         if (newdemo_read_frame_information() == -1)
2472                 newdemo_stop_playback();
2473         if (newdemo_read_frame_information() == -1)
2474                 newdemo_stop_playback();
2475         Newdemo_vcr_state = ND_STATE_PAUSED;
2476         Newdemo_at_eof = 0;
2477 }
2478
2479 void newdemo_goto_end()
2480 {
2481         short frame_length, byte_count, bshort;
2482         byte level, bbyte, laser_level;
2483         ubyte energy, shield, c;
2484         int i, loc, bint;
2485
2486         fseek(infile, -2, SEEK_END);
2487         nd_read_byte(&level);
2488
2489         if ((level < Last_secret_level) || (level > Last_level)) {
2490                 newmenu_item m[3];
2491
2492                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2493                 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2494                 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2495                 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2496                 newdemo_stop_playback();
2497                 return;
2498         }
2499         if (level != Current_level_num)
2500                 LoadLevel(level,1);
2501
2502         fseek(infile, -4, SEEK_END);
2503         nd_read_short(&byte_count);
2504         fseek(infile, -2 - byte_count, SEEK_CUR);
2505
2506         nd_read_short(&frame_length);
2507         loc = ftell(infile);
2508         if (Newdemo_game_mode & GM_MULTI)
2509                 nd_read_byte(&Newdemo_players_cloaked);
2510         else
2511                 nd_read_byte(&bbyte);
2512         nd_read_byte(&bbyte);
2513         nd_read_short(&bshort);
2514         nd_read_int(&bint);
2515         
2516         nd_read_byte(&energy);
2517         nd_read_byte(&shield);
2518         Players[Player_num].energy = i2f(energy);
2519         Players[Player_num].shields = i2f(shield);
2520         nd_read_int((int *)&(Players[Player_num].flags));
2521         if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
2522                 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2523                 Newdemo_players_cloaked |= (1 << Player_num);
2524         }
2525         if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2526                 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2527         nd_read_byte((byte *)&Primary_weapon);
2528         nd_read_byte((byte *)&Secondary_weapon);
2529         for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
2530                 nd_read_short((short *)&(Players[Player_num].primary_ammo[i])); 
2531         for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
2532                 nd_read_short((short *)&(Players[Player_num].secondary_ammo[i]));       
2533         nd_read_byte(&laser_level);
2534         if (laser_level != Players[Player_num].laser_level) {
2535                 Players[Player_num].laser_level = laser_level;
2536                 update_laser_weapon_info();
2537         }
2538
2539         if (Newdemo_game_mode & GM_MULTI) {
2540                 nd_read_byte(&c);
2541                 N_players = (int)c;
2542 // see newdemo_read_start_demo for explanation of
2543 // why this is commented out
2544 //              nd_read_byte((byte *)&N_players);
2545                 for (i = 0; i < N_players; i++) {
2546                         nd_read_string(Players[i].callsign);
2547                         nd_read_byte(&(Players[i].connected));
2548                         if (Newdemo_game_mode & GM_MULTI_COOP) {
2549                                 nd_read_int(&(Players[i].score));
2550                         } else {
2551                                 nd_read_short((short *)&(Players[i].net_killed_total));
2552                                 nd_read_short((short *)&(Players[i].net_kills_total));
2553                         }
2554                 }
2555         } else {
2556                 nd_read_int(&(Players[Player_num].score));
2557         }
2558
2559         fseek(infile, loc, SEEK_SET);
2560         fseek(infile, -frame_length, SEEK_CUR);
2561         nd_read_int(&NewdemoFrameCount);                                // get the frame count
2562         NewdemoFrameCount--;
2563         fseek(infile, 4, SEEK_CUR);
2564         Newdemo_vcr_state = ND_STATE_PLAYBACK;
2565         newdemo_read_frame_information();                       // then the frame information
2566         Newdemo_vcr_state = ND_STATE_PAUSED;
2567         return;
2568 }
2569
2570 void newdemo_back_frames(int frames)
2571 {
2572         short last_frame_length;
2573         int i;
2574
2575         for (i = 0; i < frames; i++)
2576         {
2577                 fseek(infile, -10, SEEK_CUR);
2578                 nd_read_short(&last_frame_length);                      
2579                 fseek(infile, 8 - last_frame_length, SEEK_CUR);
2580
2581                 if (!Newdemo_at_eof && newdemo_read_frame_information() == -1) {
2582                         newdemo_stop_playback();
2583                         return;
2584                 }
2585                 if (Newdemo_at_eof)
2586                         Newdemo_at_eof = 0;
2587
2588                 fseek(infile, -10, SEEK_CUR);
2589                 nd_read_short(&last_frame_length);                      
2590                 fseek(infile, 8 - last_frame_length, SEEK_CUR);
2591         }
2592
2593 }
2594
2595 /*
2596  *  routine to interpolate the viewer position.  the current position is
2597  *  stored in the Viewer object.  Save this position, and read the next
2598  *  frame to get all objects read in.  Calculate the delta playback and
2599  *  the delta recording frame times between the two frames, then intepolate
2600  *  the viewers position accordingly.  nd_recorded_time is the time that it
2601  *  took the recording to render the frame that we are currently looking
2602  *  at.
2603 */
2604
2605 void interpolate_frame(fix d_play, fix d_recorded)
2606 {
2607         int i, j, num_cur_objs;
2608         fix factor;
2609         object *cur_objs;
2610
2611         factor = fixdiv(d_play, d_recorded);
2612         if (factor > F1_0)
2613                 factor = F1_0;
2614
2615         num_cur_objs = Highest_object_index;
2616         cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1));
2617         if (cur_objs == NULL) {
2618                 mprintf((0,"Couldn't get %d bytes for cur_objs in interpolate_frame\n", sizeof(object) * num_cur_objs));
2619                 Int3();
2620                 return;
2621         }
2622         for (i = 0; i <= num_cur_objs; i++)
2623                 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2624
2625         Newdemo_vcr_state = ND_STATE_PAUSED;
2626         if (newdemo_read_frame_information() == -1) {
2627                 d_free(cur_objs);
2628                 newdemo_stop_playback();
2629                 return;
2630         }
2631
2632         for (i = 0; i <= num_cur_objs; i++) {
2633                 for (j = 0; j <= Highest_object_index; j++) {
2634                         if (cur_objs[i].signature == Objects[j].signature) {
2635                                 ubyte render_type = cur_objs[i].render_type;
2636                                 // fix delta_p, delta_h, delta_b;
2637                                 fix     delta_x, delta_y, delta_z;
2638                                 // vms_angvec cur_angles, dest_angles;
2639
2640 //  Extract the angles from the object orientation matrix.
2641 //  Some of this code taken from ai_turn_towards_vector
2642 //  Don't do the interpolation on certain render types which don't use an orientation matrix
2643
2644                                 if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) {
2645
2646 vms_vector      fvec1, fvec2, rvec1, rvec2;
2647 fix                     mag1;
2648
2649 fvec1 = cur_objs[i].orient.fvec;
2650 vm_vec_scale(&fvec1, F1_0-factor);
2651 fvec2 = Objects[j].orient.fvec;
2652 vm_vec_scale(&fvec2, factor);
2653 vm_vec_add2(&fvec1, &fvec2);
2654 mag1 = vm_vec_normalize_quick(&fvec1);
2655 if (mag1 > F1_0/256) {
2656         rvec1 = cur_objs[i].orient.rvec;
2657         vm_vec_scale(&rvec1, F1_0-factor);
2658         rvec2 = Objects[j].orient.rvec;
2659         vm_vec_scale(&rvec2, factor);
2660         vm_vec_add2(&rvec1, &rvec2);
2661         vm_vec_normalize_quick(&rvec1); //      Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1
2662         vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2663 }
2664
2665 //--old new way --      vms_vector      fvec1, fvec2, rvec1, rvec2;
2666 //--old new way --
2667 //--old new way --      fvec1 = cur_objs[i].orient.fvec;
2668 //--old new way --      vm_vec_scale(&fvec1, F1_0-factor);
2669 //--old new way --      fvec2 = Objects[j].orient.fvec;
2670 //--old new way --      vm_vec_scale(&fvec2, factor);
2671 //--old new way --      vm_vec_add2(&fvec1, &fvec2);
2672 //--old new way --      vm_vec_normalize_quick(&fvec1);
2673 //--old new way --
2674 //--old new way --      rvec1 = cur_objs[i].orient.rvec;
2675 //--old new way --      vm_vec_scale(&rvec1, F1_0-factor);
2676 //--old new way --      rvec2 = Objects[j].orient.rvec;
2677 //--old new way --      vm_vec_scale(&rvec2, factor);
2678 //--old new way --      vm_vec_add2(&rvec1, &rvec2);
2679 //--old new way --      vm_vec_normalize_quick(&rvec1);
2680 //--old new way --
2681 //--old new way --      vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2682
2683 // -- old fashioned way --                                      vm_extract_angles_matrix(&cur_angles, &(cur_objs[i].orient));
2684 // -- old fashioned way --                                      vm_extract_angles_matrix(&dest_angles, &(Objects[j].orient));
2685 // -- old fashioned way --
2686 // -- old fashioned way --                                      delta_p = (dest_angles.p - cur_angles.p);
2687 // -- old fashioned way --                                      delta_h = (dest_angles.h - cur_angles.h);
2688 // -- old fashioned way --                                      delta_b = (dest_angles.b - cur_angles.b);
2689 // -- old fashioned way --
2690 // -- old fashioned way --                                      if (delta_p != 0) {
2691 // -- old fashioned way --                                              if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
2692 // -- old fashioned way --                                              if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
2693 // -- old fashioned way --                                              delta_p = fixmul(delta_p, factor);
2694 // -- old fashioned way --                                              cur_angles.p += delta_p;
2695 // -- old fashioned way --                                      }
2696 // -- old fashioned way --                                      if (delta_h != 0) {
2697 // -- old fashioned way --                                              if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
2698 // -- old fashioned way --                                              if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
2699 // -- old fashioned way --                                              delta_h = fixmul(delta_h, factor);
2700 // -- old fashioned way --                                              cur_angles.h += delta_h;
2701 // -- old fashioned way --                                      }
2702 // -- old fashioned way --                                      if (delta_b != 0) {
2703 // -- old fashioned way --                                              if (delta_b > F1_0/2) delta_b = dest_angles.b - cur_angles.b - F1_0;
2704 // -- old fashioned way --                                              if (delta_b < -F1_0/2) delta_b = dest_angles.b - cur_angles.b + F1_0;
2705 // -- old fashioned way --                                              delta_b = fixmul(delta_b, factor);
2706 // -- old fashioned way --                                              cur_angles.b += delta_b;
2707 // -- old fashioned way --                                      }
2708                                 }
2709
2710 // Interpolate the object position.  This is just straight linear
2711 // interpolation.
2712
2713                                 delta_x = Objects[j].pos.x - cur_objs[i].pos.x;
2714                                 delta_y = Objects[j].pos.y - cur_objs[i].pos.y;
2715                                 delta_z = Objects[j].pos.z - cur_objs[i].pos.z;
2716
2717                                 delta_x = fixmul(delta_x, factor);
2718                                 delta_y = fixmul(delta_y, factor);
2719                                 delta_z = fixmul(delta_z, factor);
2720
2721                                 cur_objs[i].pos.x += delta_x;
2722                                 cur_objs[i].pos.y += delta_y;
2723                                 cur_objs[i].pos.z += delta_z;
2724         
2725 // -- old fashioned way --// stuff the new angles back into the object structure
2726 // -- old fashioned way --                              vm_angles_2_matrix(&(cur_objs[i].orient), &cur_angles);
2727                         }
2728                 }
2729         }
2730
2731 // get back to original position in the demo file.  Reread the current
2732 // frame information again to reset all of the object stuff not covered
2733 // with Highest_object_index and the object array (previously rendered
2734 // objects, etc....)
2735
2736         newdemo_back_frames(1);
2737         newdemo_back_frames(1);
2738         if (newdemo_read_frame_information() == -1)
2739                 newdemo_stop_playback();
2740         Newdemo_vcr_state = ND_STATE_PLAYBACK;
2741
2742         for (i = 0; i <= num_cur_objs; i++)
2743                 memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object));
2744         Highest_object_index = num_cur_objs;
2745         d_free(cur_objs);
2746 }
2747
2748 void newdemo_playback_one_frame()
2749 {
2750         int frames_back, i, level;
2751         static fix base_interpol_time = 0;
2752         static fix d_recorded = 0;
2753    
2754         for (i = 0; i < MAX_PLAYERS; i++)
2755                 if (Newdemo_players_cloaked & (1 << i))
2756                         Players[i].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2757
2758         if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2759                 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2760         
2761         if (Newdemo_vcr_state == ND_STATE_PAUSED)                       // render a frame or not
2762                 return;
2763    
2764         if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2765                 DoJasonInterpolate(nd_recorded_time);
2766         
2767         Control_center_destroyed = 0;
2768         Countdown_seconds_left = -1;
2769         PALETTE_FLASH_SET(0,0,0);               //clear flash
2770
2771         if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2772         {
2773                 level = Current_level_num;
2774                 if (NewdemoFrameCount == 0)
2775                         return;
2776                 else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (NewdemoFrameCount < 10)) {
2777                         newdemo_goto_beginning();
2778                         return;
2779                 }
2780                 if (Newdemo_vcr_state == ND_STATE_REWINDING)
2781                         frames_back = 10;
2782                 else
2783                         frames_back = 1;
2784                 if (Newdemo_at_eof) {
2785                         fseek(infile, 11, SEEK_CUR);
2786                 }
2787                 newdemo_back_frames(frames_back);
2788
2789                 if (level != Current_level_num)
2790                         newdemo_pop_ctrlcen_triggers();
2791
2792                 if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) {
2793                         if (level != Current_level_num)
2794                                 newdemo_back_frames(1);
2795                         Newdemo_vcr_state = ND_STATE_PAUSED;
2796                 }
2797         }
2798         else if (Newdemo_vcr_state == ND_STATE_FASTFORWARD) {
2799                 if (!Newdemo_at_eof)
2800                 {
2801                         for (i = 0; i < 10; i++)
2802                         {
2803                                 if (newdemo_read_frame_information() == -1)
2804                                 {
2805                                         if (Newdemo_at_eof)
2806                                                 Newdemo_vcr_state = ND_STATE_PAUSED;
2807                                         else
2808                                                 newdemo_stop_playback();
2809                                         break;
2810                                 }
2811                         }
2812                 }
2813                 else
2814                         Newdemo_vcr_state = ND_STATE_PAUSED;
2815         }
2816         else if (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD) {
2817                 if (!Newdemo_at_eof) {
2818                         level = Current_level_num;
2819                         if (newdemo_read_frame_information() == -1) {
2820                                 if (!Newdemo_at_eof)
2821                                         newdemo_stop_playback();
2822                         }
2823                         if (level != Current_level_num) {
2824                                 if (newdemo_read_frame_information() == -1) {
2825                                         if (!Newdemo_at_eof)
2826                                                 newdemo_stop_playback();
2827                                 }
2828                         }
2829                         Newdemo_vcr_state = ND_STATE_PAUSED;
2830                 } else
2831                         Newdemo_vcr_state = ND_STATE_PAUSED;
2832         }
2833         else {
2834
2835 //  First, uptate the total playback time to date.  Then we check to see
2836 //  if we need to change the playback style to interpolate frames or
2837 //  skip frames based on where the playback time is relative to the
2838 //  recorded time.
2839
2840                 if (NewdemoFrameCount <= 0)
2841                         nd_playback_total = nd_recorded_total;          // baseline total playback time
2842                 else
2843                         nd_playback_total += FrameTime;
2844                 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2845                         if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) {
2846                                 playback_style = INTERPOLATE_PLAYBACK;
2847                                 nd_playback_total = nd_recorded_total + FrameTime;              // baseline playback time
2848                                 base_interpol_time = nd_recorded_total;
2849                                 d_recorded = nd_recorded_time;                                                                  // baseline delta recorded
2850                         }
2851                 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2852                         if (nd_playback_total > nd_recorded_total)
2853                                 playback_style = SKIP_PLAYBACK;
2854
2855                                                         
2856                 if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) {
2857                         fix d_play = 0;
2858
2859                         if (nd_recorded_total - nd_playback_total < FrameTime) {
2860                                 d_recorded = nd_recorded_total - nd_playback_total;
2861
2862                                 while (nd_recorded_total - nd_playback_total < FrameTime) {
2863                                         object *cur_objs;
2864                                         int i, j, num_objs, level;
2865
2866                                         num_objs = Highest_object_index;
2867                                         cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1));
2868                                         if (cur_objs == NULL) {
2869                                                 Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs);
2870                                                 break;
2871                                         }
2872                                         for (i = 0; i <= num_objs; i++)
2873                                                 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2874
2875                                         level = Current_level_num;
2876                                         if (newdemo_read_frame_information() == -1) {
2877                                                 d_free(cur_objs);
2878                                                 newdemo_stop_playback();
2879                                                 return;
2880                                         }
2881                                         if (level != Current_level_num) {
2882                                                 d_free(cur_objs);
2883                                                 if (newdemo_read_frame_information() == -1)
2884                                                         newdemo_stop_playback();
2885                                                 break;
2886                                         }
2887
2888 //  for each new object in the frame just read in, determine if there is
2889 //  a corresponding object that we have been interpolating.  If so, then
2890 //  copy that interpolated object to the new Objects array so that the
2891 //  interpolated position and orientation can be preserved.
2892
2893                                         for (i = 0; i <= num_objs; i++) {
2894                                                 for (j = 0; j <= Highest_object_index; j++) {
2895                                                         if (cur_objs[i].signature == Objects[j].signature) {
2896                                                                 memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix));
2897                                                                 memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector));
2898                                                                 break;
2899                                                         }
2900                                                 }
2901                                         }
2902                                         d_free(cur_objs);
2903                                         d_recorded += nd_recorded_time;
2904                                         base_interpol_time = nd_playback_total - FrameTime;
2905                                 }
2906                         }
2907
2908                         d_play = nd_playback_total - base_interpol_time;
2909                         interpolate_frame(d_play, d_recorded);
2910                         return;
2911                 }
2912                 else {
2913 //                      mprintf ((0, "*"));
2914                         if (newdemo_read_frame_information() == -1) {
2915                                 newdemo_stop_playback();
2916                                 return;
2917                         }
2918                         if (playback_style == SKIP_PLAYBACK) {
2919 //                              mprintf ((0, "."));
2920                                 while (nd_playback_total > nd_recorded_total) {
2921                                         if (newdemo_read_frame_information() == -1) {
2922                                                 newdemo_stop_playback();
2923                                                 return;
2924                                         }
2925                                 }
2926                         }
2927                 }
2928         }
2929 }
2930
2931 void newdemo_start_recording()
2932 {  
2933    #ifdef WINDOWS
2934                 Newdemo_size=GetFreeDiskSpace();
2935                 mprintf((0, "Free space = %d\n", Newdemo_size));
2936         #else
2937                 Newdemo_size = GetDiskFree();
2938         #endif
2939
2940         Newdemo_size -= 100000;
2941
2942         if ((Newdemo_size+100000) <  2000000000)        {
2943                 if (((int)(Newdemo_size)) < 500000) {
2944                         #ifndef MACINTOSH
2945                         nm_messagebox(NULL, 1, TXT_OK, TXT_DEMO_NO_SPACE);
2946                         #else
2947                         nm_messagebox(NULL, 1, TXT_OK, "Not enough space on current\ndrive to start demo recording.");
2948                         #endif
2949                         return;
2950                 }
2951         }
2952
2953         Newdemo_num_written = 0;
2954         Newdemo_no_space=0;
2955         Newdemo_state = ND_STATE_RECORDING;
2956         outfile = fopen( DEMO_FILENAME, "wb" );
2957
2958         #ifndef MACINTOSH
2959         if (outfile == NULL && errno == ENOENT) {               //dir doesn't exist?
2960         #else
2961         if (outfile == NULL) {                                                  //dir doesn't exist and no errno on mac!
2962         #endif  
2963                 d_mkdir(DEMO_DIR); //try making directory
2964                 outfile = fopen( DEMO_FILENAME, "wb" );
2965         }
2966
2967         if (outfile == NULL)
2968      {
2969                 nm_messagebox(NULL, 1, TXT_OK, "Cannot open demo temp file");
2970         Newdemo_state = ND_STATE_NORMAL;
2971      }
2972         else
2973                 newdemo_record_start_demo();
2974                 
2975 }
2976
2977 char demoname_allowed_chars[] = "azAZ09__--";
2978 void newdemo_stop_recording()
2979 {
2980         newmenu_item m[6];
2981         int l, exit;
2982         static char filename[15] = "", *s;
2983         static ubyte tmpcnt = 0;
2984         ubyte cloaked = 0;
2985         char fullname[15+FILENAME_LEN] = DEMO_DIR;
2986         unsigned short byte_count = 0;
2987
2988         exit = 0;
2989
2990         nd_write_byte(ND_EVENT_EOF);
2991         nd_write_short(frame_bytes_written - 1);
2992         if (Game_mode & GM_MULTI) {
2993                 for (l = 0; l < N_players; l++) {
2994                         if (Players[l].flags & PLAYER_FLAGS_CLOAKED)
2995                                 cloaked |= (1 << l);
2996                 }
2997                 nd_write_byte(cloaked);
2998                 nd_write_byte(ND_EVENT_EOF);
2999         } else {
3000                 nd_write_short(ND_EVENT_EOF);
3001         }
3002         nd_write_short(ND_EVENT_EOF);
3003         nd_write_int(ND_EVENT_EOF);
3004
3005         byte_count += 10;               // from frame_bytes_written
3006  
3007         nd_write_byte((byte)(f2ir(Players[Player_num].energy)));
3008         nd_write_byte((byte)(f2ir(Players[Player_num].shields)));
3009         nd_write_int(Players[Player_num].flags);                // be sure players flags are set
3010         nd_write_byte((byte)Primary_weapon);
3011         nd_write_byte((byte)Secondary_weapon);
3012         byte_count += 8;
3013
3014         for (l = 0; l < MAX_PRIMARY_WEAPONS; l++)
3015                 nd_write_short((short)Players[Player_num].primary_ammo[l]);
3016
3017         for (l = 0; l < MAX_SECONDARY_WEAPONS; l++)
3018                 nd_write_short((short)Players[Player_num].secondary_ammo[l]);
3019         byte_count += (sizeof(short) * (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS));
3020
3021         nd_write_byte(Players[Player_num].laser_level);
3022         byte_count++;
3023
3024         if (Game_mode & GM_MULTI) {
3025                 nd_write_byte((byte)N_players);
3026                 byte_count++;
3027                 for (l = 0; l < N_players; l++) {
3028                         nd_write_string(Players[l].callsign);
3029                         byte_count += (strlen(Players[l].callsign) + 2);
3030                         nd_write_byte(Players[l].connected);
3031                         if (Game_mode & GM_MULTI_COOP) {
3032                                 nd_write_int(Players[l].score);
3033                                 byte_count += 5;
3034                         } else {
3035                                 nd_write_short((short)Players[l].net_killed_total);
3036                                 nd_write_short((short)Players[l].net_kills_total);
3037                                 byte_count += 5;
3038                         }
3039                 }
3040         } else {
3041                 nd_write_int(Players[Player_num].score);
3042                 byte_count += 4;
3043         }
3044         nd_write_short(byte_count);
3045
3046         nd_write_byte(Current_level_num);
3047         nd_write_byte(ND_EVENT_EOF);
3048
3049         l = ftell(outfile);
3050         fclose(outfile);
3051         outfile = NULL;
3052         Newdemo_state = ND_STATE_NORMAL;
3053         gr_palette_load( gr_palette );
3054
3055         if (filename[0] != '\0') {
3056                 int num, i = strlen(filename) - 1;
3057                 char newfile[15];
3058
3059                 while (isdigit(filename[i])) {
3060                         i--;
3061                         if (i == -1)
3062                                 break;
3063                 }
3064                 i++;
3065                 num = atoi(&(filename[i]));
3066                 num++;
3067                 filename[i] = '\0';
3068                 sprintf (newfile, "%s%d", filename, num);
3069                 strncpy(filename, newfile, 8);
3070                 filename[8] = '\0';
3071         }
3072
3073 try_again:
3074         ;
3075
3076         Newmenu_allowed_chars = demoname_allowed_chars;
3077         if (!Newdemo_no_space) {
3078                 m[0].type=NM_TYPE_INPUT; m[0].text_len = 8; m[0].text = filename;
3079                 exit = newmenu_do( NULL, TXT_SAVE_DEMO_AS, 1, &(m[0]), NULL );
3080         } else if (Newdemo_no_space == 1) {
3081                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_BAD;
3082                 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3083                 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3084         } else if (Newdemo_no_space == 2) {
3085                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_NOSPACE;
3086                 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3087                 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3088         }
3089         Newmenu_allowed_chars = NULL;
3090
3091         if (exit == -2) {                                       // got bumped out from network menu
3092                 char save_file[7+FILENAME_LEN];
3093
3094                 if (filename[0] != '\0') {
3095                         strcpy(save_file, DEMO_DIR);
3096                         strcat(save_file, filename);
3097                         strcat(save_file, ".dem");
3098                 } else
3099                         sprintf (save_file, "%stmp%d.dem", DEMO_DIR, tmpcnt++);
3100                 remove(save_file);
3101                 rename(DEMO_FILENAME, save_file);
3102                 return;
3103         }
3104         if (exit == -1) {                                       // pressed ESC
3105                 remove(DEMO_FILENAME);          // might as well remove the file
3106                 return;                                                 // return without doing anything
3107         }
3108         
3109         if (filename[0]==0)     //null string
3110                 goto try_again;
3111
3112         //check to make sure name is ok
3113         for (s=filename;*s;s++)
3114                 if (!isalnum(*s) && *s!='_') {
3115                         nm_messagebox1(NULL, NULL,1,TXT_CONTINUE, TXT_DEMO_USE_LETTERS);
3116                         goto try_again;
3117                 }
3118
3119         if (Newdemo_no_space)
3120                 strcat(fullname, m[1].text);
3121         else
3122                 strcat(fullname, m[0].text);
3123         strcat(fullname, ".dem");
3124         remove(fullname);
3125         rename(DEMO_FILENAME, fullname);
3126 }
3127
3128 //returns the number of demo files on the disk
3129 int newdemo_count_demos()
3130 {
3131         FILEFINDSTRUCT find;
3132         int NumFiles=0;
3133
3134         if( !FileFindFirst("demos/*.dem", &find ) )     {
3135                 do      {
3136                         NumFiles++;
3137                 } while( !FileFindNext( &find ) );
3138                 FileFindClose();
3139         }
3140
3141         return NumFiles;
3142 }
3143
3144 void newdemo_start_playback(char * filename)
3145 {
3146         FILEFINDSTRUCT find;
3147         int rnd_demo = 0;
3148         char filename2[7+FILENAME_LEN] = DEMO_DIR;
3149
3150 #ifdef NETWORK
3151         change_playernum_to(0);
3152 #endif
3153    First_time_playback=1;
3154         JasonPlaybackTotal=0;
3155
3156         if (filename==NULL) {
3157                 // Randomly pick a filename 
3158                 int NumFiles = 0, RandFileNum;
3159                 rnd_demo = 1;
3160
3161                 NumFiles = newdemo_count_demos();
3162
3163                 if ( NumFiles == 0 ) { 
3164                         return;         // No files found!
3165                 }
3166                 RandFileNum = d_rand() % NumFiles;
3167                 NumFiles = 0;
3168                 if( !FileFindFirst( "demos/*.dem", &find ) )    {
3169                         do      {
3170                                 if ( NumFiles==RandFileNum )    {
3171                                         filename = (char *)&find.name;
3172                                         break;
3173                                 }
3174                                 NumFiles++;
3175                         } while( !FileFindNext( &find ) );
3176                         FileFindClose();
3177                 }
3178                 if ( filename==NULL) return;
3179         }
3180
3181         if (!filename)
3182                 return;
3183
3184         strcat(filename2,filename);
3185
3186         infile = fopen( filename2, "rb" );
3187         if (infile==NULL)       {
3188                 mprintf( (0, "Error reading '%s'\n", filename ));
3189                 return;
3190         }
3191
3192         nd_bad_read = 0;
3193 #ifdef NETWORK
3194         change_playernum_to(0);                                         // force playernum to 0
3195 #endif
3196         strncpy(nd_save_callsign, Players[Player_num].callsign, CALLSIGN_LEN);
3197         Viewer = ConsoleObject = &Objects[0];   // play properly as if console player
3198         if (newdemo_read_demo_start(rnd_demo)) {
3199                 fclose(infile);
3200                 return;
3201         }
3202
3203         Game_mode = GM_NORMAL;
3204         Newdemo_state = ND_STATE_PLAYBACK;
3205         Newdemo_vcr_state = ND_STATE_PLAYBACK;
3206         Newdemo_old_cockpit = Cockpit_mode;
3207         Newdemo_size = filelength(fileno(infile));
3208         nd_bad_read = 0;
3209         Newdemo_at_eof = 0;
3210         NewdemoFrameCount = 0;
3211         Newdemo_players_cloaked = 0;
3212         playback_style = NORMAL_PLAYBACK;
3213         Function_mode = FMODE_GAME;
3214         Cockpit_3d_view[0] = CV_NONE;                   //turn off 3d views on cockpit
3215         Cockpit_3d_view[1] = CV_NONE;                   //turn off 3d views on cockpit
3216         newdemo_playback_one_frame();           // this one loads new level
3217         newdemo_playback_one_frame();           // get all of the objects to renderb game
3218 }
3219
3220 void newdemo_stop_playback()
3221 {
3222         fclose( infile );
3223         Newdemo_state = ND_STATE_NORMAL;
3224 #ifdef NETWORK
3225         change_playernum_to(0);                                         //this is reality
3226 #endif
3227         strncpy(Players[Player_num].callsign, nd_save_callsign, CALLSIGN_LEN);
3228         Cockpit_mode = Newdemo_old_cockpit;
3229         Game_mode = GM_GAME_OVER;
3230         Function_mode = FMODE_MENU;
3231         longjmp(LeaveGame,0);                   // Exit game loop
3232 }
3233
3234
3235 #ifndef NDEBUG
3236
3237 #define BUF_SIZE 16384
3238
3239 void newdemo_strip_frames(char *outname, int bytes_to_strip)
3240 {
3241         FILE *outfile;
3242         char *buf;
3243         int total_size, bytes_done, read_elems, bytes_back;
3244         int trailer_start, loc1, loc2, stop_loc, bytes_to_read;
3245         short last_frame_length;
3246
3247         bytes_done = 0;
3248         total_size = filelength(fileno(infile));
3249         outfile = fopen(outname, "wb");
3250         if (outfile == NULL) {
3251                 newmenu_item m[1];
3252
3253                 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't open output file";
3254                 newmenu_do( NULL, NULL, 1, m, NULL );
3255                 newdemo_stop_playback();
3256                 return;