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