2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
16 * Code to make a complete demo playback system.
28 #include <string.h> // for memset
32 #include <ctype.h> /* for isdigit */
36 #include <sys/types.h>
55 #include "editor/editor.h"
60 #pragma global_optimizer off // pretty much sucks...need to look into this
63 void DoJasonInterpolate (fix recorded_time);
65 //#include "nocfile.h"
67 //Does demo start automatically?
70 sbyte WasRecorded [MAX_OBJECTS];
71 sbyte ViewWasRecorded[MAX_OBJECTS];
72 sbyte RenderingWasRecorded[32];
74 #define ND_EVENT_EOF 0 // EOF
75 #define ND_EVENT_START_DEMO 1 // Followed by 16 character, NULL terminated filename of .SAV file to use
76 #define ND_EVENT_START_FRAME 2 // Followed by integer frame number, then a fix FrameTime
77 #define ND_EVENT_VIEWER_OBJECT 3 // Followed by an object structure
78 #define ND_EVENT_RENDER_OBJECT 4 // Followed by an object structure
79 #define ND_EVENT_SOUND 5 // Followed by int soundum
80 #define ND_EVENT_SOUND_ONCE 6 // Followed by int soundum
81 #define ND_EVENT_SOUND_3D 7 // Followed by int soundum, int angle, int volume
82 #define ND_EVENT_WALL_HIT_PROCESS 8 // Followed by int segnum, int side, fix damage
83 #define ND_EVENT_TRIGGER 9 // Followed by int segnum, int side, int objnum
84 #define ND_EVENT_HOSTAGE_RESCUED 10 // Followed by int hostage_type
85 #define ND_EVENT_SOUND_3D_ONCE 11 // Followed by int soundum, int angle, int volume
86 #define ND_EVENT_MORPH_FRAME 12 // Followed by ? data
87 #define ND_EVENT_WALL_TOGGLE 13 // Followed by int seg, int side
88 #define ND_EVENT_HUD_MESSAGE 14 // Followed by char size, char * string (+null)
89 #define ND_EVENT_CONTROL_CENTER_DESTROYED 15 // Just a simple flag
90 #define ND_EVENT_PALETTE_EFFECT 16 // Followed by short r,g,b
91 #define ND_EVENT_PLAYER_ENERGY 17 // followed by byte energy
92 #define ND_EVENT_PLAYER_SHIELD 18 // followed by byte shields
93 #define ND_EVENT_PLAYER_FLAGS 19 // followed by player flags
94 #define ND_EVENT_PLAYER_WEAPON 20 // followed by weapon type and weapon number
95 #define ND_EVENT_EFFECT_BLOWUP 21 // followed by segment, side, and pnt
96 #define ND_EVENT_HOMING_DISTANCE 22 // followed by homing distance
97 #define ND_EVENT_LETTERBOX 23 // letterbox mode for death seq.
98 #define ND_EVENT_RESTORE_COCKPIT 24 // restore cockpit after death
99 #define ND_EVENT_REARVIEW 25 // going to rear view mode
100 #define ND_EVENT_WALL_SET_TMAP_NUM1 26 // Wall changed
101 #define ND_EVENT_WALL_SET_TMAP_NUM2 27 // Wall changed
102 #define ND_EVENT_NEW_LEVEL 28 // followed by level number
103 #define ND_EVENT_MULTI_CLOAK 29 // followed by player num
104 #define ND_EVENT_MULTI_DECLOAK 30 // followed by player num
105 #define ND_EVENT_RESTORE_REARVIEW 31 // restore cockpit after rearview mode
106 #define ND_EVENT_MULTI_DEATH 32 // with player number
107 #define ND_EVENT_MULTI_KILL 33 // with player number
108 #define ND_EVENT_MULTI_CONNECT 34 // with player number
109 #define ND_EVENT_MULTI_RECONNECT 35 // with player number
110 #define ND_EVENT_MULTI_DISCONNECT 36 // with player number
111 #define ND_EVENT_MULTI_SCORE 37 // playernum / score
112 #define ND_EVENT_PLAYER_SCORE 38 // followed by score
113 #define ND_EVENT_PRIMARY_AMMO 39 // with old/new ammo count
114 #define ND_EVENT_SECONDARY_AMMO 40 // with old/new ammo count
115 #define ND_EVENT_DOOR_OPENING 41 // with segment/side
116 #define ND_EVENT_LASER_LEVEL 42 // no data
117 #define ND_EVENT_PLAYER_AFTERBURNER 43 // followed by byte old ab, current ab
118 #define ND_EVENT_CLOAKING_WALL 44 // info changing while wall cloaking
119 #define ND_EVENT_CHANGE_COCKPIT 45 // change the cockpit
120 #define ND_EVENT_START_GUIDED 46 // switch to guided view
121 #define ND_EVENT_END_GUIDED 47 // stop guided view/return to ship
122 #define ND_EVENT_SECRET_THINGY 48 // 0/1 = secret exit functional/non-functional
123 #define ND_EVENT_LINK_SOUND_TO_OBJ 49 // record digi_link_sound_to_object3
124 #define ND_EVENT_KILL_SOUND_TO_OBJ 50 // record digi_kill_sound_linked_to_object
127 #define NORMAL_PLAYBACK 0
128 #define SKIP_PLAYBACK 1
129 #define INTERPOLATE_PLAYBACK 2
130 #define INTERPOL_FACTOR (F1_0 + (F1_0/5))
132 #define DEMO_VERSION_D1_SHARE 5
133 #define DEMO_VERSION_D1 13
134 #define DEMO_VERSION_D2 15
135 #define DEMO_VERSION DEMO_VERSION_D2
137 #define DEMO_GAME_TYPE_D1_SHARE 1
138 #define DEMO_GAME_TYPE_D1 2
139 #define DEMO_GAME_TYPE_D2 3
140 #define DEMO_GAME_TYPE DEMO_GAME_TYPE_D2
142 #define DEMO_FILENAME DEMO_DIR "tmpdemo.dem"
144 #define DEMO_MAX_LEVELS 29
147 char nd_save_callsign[CALLSIGN_LEN+1];
148 int Newdemo_state = 0;
149 int Newdemo_vcr_state = 0;
150 int Newdemo_start_frame = -1;
151 unsigned int Newdemo_size;
152 int Newdemo_num_written;
153 int Newdemo_game_mode;
154 sbyte Newdemo_game_type;
155 int Newdemo_old_cockpit;
156 int Newdemo_is_d2demo = 0;
157 sbyte Newdemo_no_space;
158 sbyte Newdemo_at_eof;
159 sbyte Newdemo_do_interpolate = 0; // 1
160 sbyte Newdemo_players_cloaked;
161 sbyte Newdemo_warning_given = 0;
162 sbyte Newdemo_cntrlcen_destroyed = 0;
163 static sbyte nd_bad_read;
164 int NewdemoFrameCount;
165 short frame_bytes_written = 0;
166 fix nd_playback_total;
167 fix nd_recorded_total;
168 fix nd_recorded_time;
169 sbyte playback_style;
170 sbyte First_time_playback=1;
171 fix JasonPlaybackTotal=0;
175 PHYSFS_file *outfile = NULL;
177 int newdemo_get_percent_done() {
178 if ( Newdemo_state == ND_STATE_PLAYBACK ) {
179 return ((unsigned int)PHYSFS_tell(infile) * 100) / Newdemo_size;
181 if ( Newdemo_state == ND_STATE_RECORDING ) {
182 return (int)PHYSFS_tell(outfile);
187 #define VEL_PRECISION 12
189 void my_extract_shortpos(object *objp, shortpos *spp)
195 objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
196 objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
197 objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
199 objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
200 objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
201 objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
203 objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
204 objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
205 objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
207 segnum = spp->segment;
208 objp->segnum = segnum;
210 objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x;
211 objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y;
212 objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z;
214 objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION);
215 objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION);
216 objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION);
219 int newdemo_read( void *buffer, int elsize, int nelem )
222 num_read = (int)PHYSFS_read(infile, buffer, elsize, nelem);
223 if (num_read < nelem || PHYSFS_eof(infile))
229 int newdemo_find_object( int signature )
234 for (i=0; i<=Highest_object_index; i++, objp++ ) {
235 if ( (objp->type != OBJ_NONE) && (objp->signature == signature))
241 int newdemo_write( void *buffer, int elsize, int nelem )
243 int num_written, total_size;
245 total_size = elsize * nelem;
246 frame_bytes_written += total_size;
247 Newdemo_num_written += total_size;
248 Assert(outfile != NULL);
249 num_written = (int)PHYSFS_write(outfile, buffer, elsize, nelem);
250 //if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) {
251 // Newdemo_no_space=1;
252 // newdemo_stop_recording();
255 if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space)
257 if (num_written == nelem && !Newdemo_no_space)
261 newdemo_stop_recording();
266 * The next bunch of files taken from Matt's gamesave.c. We have to modify
267 * these since the demo must save more information about objects that
271 static void nd_write_byte(sbyte b)
273 newdemo_write(&b, 1, 1);
276 static void nd_write_short(short s)
278 newdemo_write(&s, 2, 1);
281 static void nd_write_int(int i)
283 newdemo_write(&i, 4, 1);
286 static void nd_write_string(char *str)
288 nd_write_byte(strlen(str) + 1);
289 newdemo_write(str, (int)strlen(str) + 1, 1);
292 static void nd_write_fix(fix f)
294 newdemo_write(&f, sizeof(fix), 1);
297 static void nd_write_fixang(fixang f)
299 newdemo_write(&f, sizeof(fixang), 1);
302 static void nd_write_vector(vms_vector *v)
309 static void nd_write_angvec(vms_angvec *v)
311 nd_write_fixang(v->p);
312 nd_write_fixang(v->b);
313 nd_write_fixang(v->h);
316 void nd_write_shortpos(object *obj)
322 create_shortpos(&sp, obj, 0);
324 render_type = obj->render_type;
325 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
326 for (i = 0; i < 9; i++)
327 nd_write_byte(sp.bytemat[i]);
328 for (i = 0; i < 9; i++) {
329 if (sp.bytemat[i] != 0)
333 Int3(); // contact Allender about this.
337 nd_write_short(sp.xo);
338 nd_write_short(sp.yo);
339 nd_write_short(sp.zo);
340 nd_write_short(sp.segment);
341 nd_write_short(sp.velx);
342 nd_write_short(sp.vely);
343 nd_write_short(sp.velz);
346 static void nd_read_byte(sbyte *b)
348 newdemo_read(b, 1, 1);
351 static void nd_read_short(short *s)
353 newdemo_read(s, 2, 1);
356 static void nd_read_int(int *i)
358 newdemo_read(i, 4, 1);
361 static void nd_read_string(char *str)
366 newdemo_read(str, len, 1);
369 static void nd_read_fix(fix *f)
371 newdemo_read(f, sizeof(fix), 1);
374 static void nd_read_fixang(fixang *f)
376 newdemo_read(f, sizeof(fixang), 1);
379 static void nd_read_vector(vms_vector *v)
381 nd_read_fix(&(v->x));
382 nd_read_fix(&(v->y));
383 nd_read_fix(&(v->z));
386 static void nd_read_angvec(vms_angvec *v)
388 nd_read_fixang(&(v->p));
389 nd_read_fixang(&(v->b));
390 nd_read_fixang(&(v->h));
393 static void nd_read_shortpos(object *obj)
399 render_type = obj->render_type;
400 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
401 for (i = 0; i < 9; i++)
402 nd_read_byte(&(sp.bytemat[i]));
405 nd_read_short(&(sp.xo));
406 nd_read_short(&(sp.yo));
407 nd_read_short(&(sp.zo));
408 nd_read_short(&(sp.segment));
409 nd_read_short(&(sp.velx));
410 nd_read_short(&(sp.vely));
411 nd_read_short(&(sp.velz));
413 my_extract_shortpos(obj, &sp);
414 if ((obj->id == VCLIP_MORPHING_ROBOT) && (render_type == RT_FIREBALL) && (obj->control_type == CT_EXPLOSION))
415 extract_orient_from_segment(&obj->orient,&Segments[obj->segnum]);
419 object *prev_obj=NULL; //ptr to last object read in
421 void nd_read_object(object *obj)
423 memset(obj, 0, sizeof(object));
426 * Do render type first, since with render_type == RT_NONE, we
427 * blow by all other object information
429 nd_read_byte((sbyte *) &(obj->render_type));
430 nd_read_byte((sbyte *) &(obj->type));
431 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
434 nd_read_byte((sbyte *) &(obj->id));
435 nd_read_byte((sbyte *) &(obj->flags));
436 nd_read_short((short *)&(obj->signature));
437 nd_read_shortpos(obj);
439 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
442 obj->attached_obj = -1;
447 obj->control_type = CT_POWERUP;
448 obj->movement_type = MT_NONE;
449 obj->size = HOSTAGE_SIZE;
453 obj->control_type = CT_AI;
454 // (MarkA and MikeK said we should not do the crazy last secret stuff with multiple reactors...
455 // This necessary code is our vindication. --MK, 2/15/96)
456 if (obj->id != SPECIAL_REACTOR_ROBOT)
457 obj->movement_type = MT_PHYSICS;
459 obj->movement_type = MT_NONE;
460 obj->size = Polygon_models[Robot_info[obj->id].model_num].rad;
461 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
462 obj->rtype.pobj_info.subobj_flags = 0;
463 obj->ctype.ai_info.CLOAKED = (Robot_info[obj->id].cloak_type?1:0);
467 obj->control_type = CT_POWERUP;
468 nd_read_byte((sbyte *) &(obj->movement_type)); // might have physics movement
469 obj->size = Powerup_info[obj->id].size;
473 obj->control_type = CT_NONE;
474 obj->movement_type = MT_PHYSICS;
475 obj->size = Polygon_models[Player_ship->model_num].rad;
476 obj->rtype.pobj_info.model_num = Player_ship->model_num;
477 obj->rtype.pobj_info.subobj_flags = 0;
481 obj->control_type = CT_NONE;
482 obj->movement_type = MT_NONE;
483 obj->size = Polygon_models[obj->id].rad;
484 obj->rtype.pobj_info.model_num = obj->id;
485 obj->rtype.pobj_info.subobj_flags = 0;
489 nd_read_byte((sbyte *) &(obj->control_type));
490 nd_read_byte((sbyte *) &(obj->movement_type));
491 nd_read_fix(&(obj->size));
496 nd_read_vector(&(obj->last_pos));
497 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
498 nd_read_fix(&(obj->lifeleft));
503 obj->lifeleft = (fix)b;
504 // MWA old way -- won't work with big endian machines nd_read_byte((ubyte *)&(obj->lifeleft));
505 obj->lifeleft = (fix)((int)obj->lifeleft << 12);
508 if (obj->type == OBJ_ROBOT) {
509 if (Robot_info[obj->id].boss_flag) {
512 nd_read_byte(&cloaked);
513 obj->ctype.ai_info.CLOAKED = cloaked;
517 switch (obj->movement_type) {
520 nd_read_vector(&(obj->mtype.phys_info.velocity));
521 nd_read_vector(&(obj->mtype.phys_info.thrust));
525 nd_read_vector(&(obj->mtype.spin_rate));
535 switch (obj->control_type) {
539 nd_read_fix(&(obj->ctype.expl_info.spawn_time));
540 nd_read_fix(&(obj->ctype.expl_info.delete_time));
541 nd_read_short(&(obj->ctype.expl_info.delete_objnum));
543 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
545 if (obj->flags & OF_ATTACHED) { //attach to previous object
546 Assert(prev_obj!=NULL);
547 if (prev_obj->control_type == CT_EXPLOSION) {
548 if (prev_obj->flags & OF_ATTACHED && prev_obj->ctype.expl_info.attach_parent!=-1)
549 obj_attach(&Objects[prev_obj->ctype.expl_info.attach_parent],obj);
551 obj->flags &= ~OF_ATTACHED;
554 obj_attach(prev_obj,obj);
560 nd_read_fix(&(obj->ctype.light_info.intensity));
582 switch (obj->render_type) {
591 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
592 nd_read_int(&(obj->rtype.pobj_info.model_num));
593 if (Newdemo_game_type == DEMO_GAME_TYPE_D1_SHARE)
594 obj->rtype.pobj_info.model_num = D1Share_Polymodel_map[obj->rtype.pobj_info.model_num];
595 else if (Newdemo_game_type == DEMO_GAME_TYPE_D1)
596 obj->rtype.pobj_info.model_num = D1_Polymodel_map[obj->rtype.pobj_info.model_num];
597 else if (Newdemo_is_d2demo)
598 obj->rtype.pobj_info.model_num = D2Demo_Polymodel_map[obj->rtype.pobj_info.model_num];
599 if (obj->rtype.pobj_info.model_num < 0)
601 nd_read_int(&(obj->rtype.pobj_info.subobj_flags));
604 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
606 for (i=0;i<MAX_SUBMODELS;i++)
607 nd_read_angvec(&(obj->pobj_info.anim_angles[i]));
609 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
610 nd_read_angvec(&obj->rtype.pobj_info.anim_angles[i]);
615 obj->rtype.pobj_info.tmap_override = tmo;
618 obj->rtype.pobj_info.tmap_override = -1;
620 int xlated_tmo = tmap_xlate_table[tmo];
621 if (xlated_tmo < 0) {
622 //mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->pobj_info.model_num));
626 obj->rtype.pobj_info.tmap_override = xlated_tmo;
634 case RT_WEAPON_VCLIP:
637 nd_read_int(&(obj->rtype.vclip_info.vclip_num));
638 nd_read_fix(&(obj->rtype.vclip_info.frametime));
639 nd_read_byte(&(obj->rtype.vclip_info.framenum));
653 void nd_write_object(object *obj)
657 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
661 * Do render_type first so on read, we can make determination of
662 * what else to read in
664 nd_write_byte(obj->render_type);
665 nd_write_byte(obj->type);
666 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
669 nd_write_byte(obj->id);
670 nd_write_byte(obj->flags);
671 nd_write_short((short)obj->signature);
672 nd_write_shortpos(obj);
674 if ((obj->type != OBJ_HOSTAGE) && (obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_POWERUP) && (obj->type != OBJ_CLUTTER)) {
675 nd_write_byte(obj->control_type);
676 nd_write_byte(obj->movement_type);
677 nd_write_fix(obj->size);
679 if (obj->type == OBJ_POWERUP)
680 nd_write_byte(obj->movement_type);
682 nd_write_vector(&obj->last_pos);
684 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
685 nd_write_fix(obj->lifeleft);
687 life = (int)obj->lifeleft;
691 nd_write_byte((ubyte)life);
694 if (obj->type == OBJ_ROBOT) {
695 if (Robot_info[obj->id].boss_flag) {
696 if ((GameTime > Boss_cloak_start_time) && (GameTime < Boss_cloak_end_time))
703 switch (obj->movement_type) {
706 nd_write_vector(&obj->mtype.phys_info.velocity);
707 nd_write_vector(&obj->mtype.phys_info.thrust);
711 nd_write_vector(&obj->mtype.spin_rate);
721 switch (obj->control_type) {
727 nd_write_fix(obj->ctype.expl_info.spawn_time);
728 nd_write_fix(obj->ctype.expl_info.delete_time);
729 nd_write_short(obj->ctype.expl_info.delete_objnum);
737 nd_write_fix(obj->ctype.light_info.intensity);
744 case CT_SLEW: //the player is generally saved as slew
757 switch (obj->render_type) {
766 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
767 nd_write_int(obj->rtype.pobj_info.model_num);
768 nd_write_int(obj->rtype.pobj_info.subobj_flags);
771 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
773 for (i=0;i<MAX_SUBMODELS;i++)
774 nd_write_angvec(&obj->pobj_info.anim_angles[i]);
776 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
777 nd_write_angvec(&obj->rtype.pobj_info.anim_angles[i]);
779 nd_write_int(obj->rtype.pobj_info.tmap_override);
785 case RT_WEAPON_VCLIP:
788 nd_write_int(obj->rtype.vclip_info.vclip_num);
789 nd_write_fix(obj->rtype.vclip_info.frametime);
790 nd_write_byte(obj->rtype.vclip_info.framenum);
803 int JustStartedRecording=0,JustStartedPlayback=0;
805 void newdemo_record_start_demo()
810 nd_write_byte(ND_EVENT_START_DEMO);
811 nd_write_byte(DEMO_VERSION);
812 nd_write_byte(DEMO_GAME_TYPE);
813 nd_write_fix(GameTime);
816 if (Game_mode & GM_MULTI)
817 nd_write_int(Game_mode | (Player_num << 16));
820 // NOTE LINK TO ABOVE!!!
821 nd_write_int(Game_mode);
824 if (Game_mode & GM_TEAM) {
825 nd_write_byte(Netgame.team_vector);
826 nd_write_string(Netgame.team_name[0]);
827 nd_write_string(Netgame.team_name[1]);
830 if (Game_mode & GM_MULTI) {
831 nd_write_byte((sbyte)N_players);
832 for (i = 0; i < N_players; i++) {
833 nd_write_string(Players[i].callsign);
834 nd_write_byte(Players[i].connected);
836 if (Game_mode & GM_MULTI_COOP) {
837 nd_write_int(Players[i].score);
839 nd_write_short((short)Players[i].net_killed_total);
840 nd_write_short((short)Players[i].net_kills_total);
845 // NOTE LINK TO ABOVE!!!
846 nd_write_int(Players[Player_num].score);
848 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
849 nd_write_short((short)Players[Player_num].primary_ammo[i]);
851 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
852 nd_write_short((short)Players[Player_num].secondary_ammo[i]);
854 nd_write_byte((sbyte)Players[Player_num].laser_level);
856 // Support for missions added here
858 nd_write_string(Current_mission_filename);
860 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
861 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
862 nd_write_int(Players[Player_num].flags); // be sure players flags are set
863 nd_write_byte((sbyte)Primary_weapon);
864 nd_write_byte((sbyte)Secondary_weapon);
865 Newdemo_start_frame = FrameCount;
866 JustStartedRecording=1;
868 newdemo_set_new_level(Current_level_num);
873 void newdemo_record_start_frame(int frame_number, fix frame_time )
877 if (Newdemo_no_space) {
878 newdemo_stop_playback();
884 for (i=0;i<MAX_OBJECTS;i++)
887 ViewWasRecorded[i]=0;
890 RenderingWasRecorded[i]=0;
892 frame_number -= Newdemo_start_frame;
894 Assert(frame_number >= 0 );
896 nd_write_byte(ND_EVENT_START_FRAME);
897 nd_write_short(frame_bytes_written - 1); // from previous frame
898 frame_bytes_written=3;
899 nd_write_int(frame_number);
900 nd_write_int(frame_time);
905 void newdemo_record_render_object(object * obj)
907 if (ViewWasRecorded[OBJECT_NUMBER(obj)])
910 //if (obj==&Objects[Players[Player_num].objnum] && !Player_is_dead)
914 nd_write_byte(ND_EVENT_RENDER_OBJECT);
915 nd_write_object(obj);
919 extern ubyte RenderingType;
921 void newdemo_record_viewer_object(object * obj)
924 if (ViewWasRecorded[OBJECT_NUMBER(obj)] && (ViewWasRecorded[OBJECT_NUMBER(obj)] - 1) == RenderingType)
926 //if (WasRecorded[OBJECT_NUMBER(obj)])
928 if (RenderingWasRecorded[RenderingType])
931 ViewWasRecorded[OBJECT_NUMBER(obj)] = RenderingType + 1;
932 RenderingWasRecorded[RenderingType]=1;
934 nd_write_byte(ND_EVENT_VIEWER_OBJECT);
935 nd_write_byte(RenderingType);
936 nd_write_object(obj);
940 void newdemo_record_sound( int soundno )
943 nd_write_byte(ND_EVENT_SOUND);
944 nd_write_int( soundno );
948 //--unused-- void newdemo_record_sound_once( int soundno ) {
949 //--unused-- stop_time();
950 //--unused-- nd_write_byte( ND_EVENT_SOUND_ONCE );
951 //--unused-- nd_write_int( soundno );
952 //--unused-- start_time();
956 void newdemo_record_cockpit_change (int mode)
959 nd_write_byte (ND_EVENT_CHANGE_COCKPIT);
965 void newdemo_record_sound_3d( int soundno, int angle, int volume )
968 nd_write_byte( ND_EVENT_SOUND_3D );
969 nd_write_int( soundno );
970 nd_write_int( angle );
971 nd_write_int( volume );
975 void newdemo_record_sound_3d_once( int soundno, int angle, int volume )
978 nd_write_byte( ND_EVENT_SOUND_3D_ONCE );
979 nd_write_int( soundno );
980 nd_write_int( angle );
981 nd_write_int( volume );
986 void newdemo_record_link_sound_to_object3( int soundno, short objnum, fix max_volume, fix max_distance, int loop_start, int loop_end )
989 nd_write_byte( ND_EVENT_LINK_SOUND_TO_OBJ );
990 nd_write_int( soundno );
991 nd_write_int( Objects[objnum].signature );
992 nd_write_int( max_volume );
993 nd_write_int( max_distance );
994 nd_write_int( loop_start );
995 nd_write_int( loop_end );
999 void newdemo_record_kill_sound_linked_to_object( int objnum )
1002 nd_write_byte( ND_EVENT_KILL_SOUND_TO_OBJ );
1003 nd_write_int( Objects[objnum].signature );
1008 void newdemo_record_wall_hit_process( int segnum, int side, int damage, int playernum )
1014 //playernum = playernum;
1015 nd_write_byte( ND_EVENT_WALL_HIT_PROCESS );
1016 nd_write_int( segnum );
1017 nd_write_int( side );
1018 nd_write_int( damage );
1019 nd_write_int( playernum );
1023 void newdemo_record_guided_start ()
1025 nd_write_byte (ND_EVENT_START_GUIDED);
1028 void newdemo_record_guided_end ()
1030 nd_write_byte (ND_EVENT_END_GUIDED);
1033 void newdemo_record_secret_exit_blown(int truth)
1036 nd_write_byte( ND_EVENT_SECRET_THINGY );
1037 nd_write_int( truth );
1041 void newdemo_record_trigger( int segnum, int side, int objnum,int shot )
1044 nd_write_byte( ND_EVENT_TRIGGER );
1045 nd_write_int( segnum );
1046 nd_write_int( side );
1047 nd_write_int( objnum );
1052 void newdemo_record_hostage_rescued( int hostage_number ) {
1054 nd_write_byte( ND_EVENT_HOSTAGE_RESCUED );
1055 nd_write_int( hostage_number );
1059 void newdemo_record_morph_frame(morph_data *md)
1063 nd_write_byte( ND_EVENT_MORPH_FRAME );
1065 newdemo_write( md->morph_vecs, sizeof(md->morph_vecs), 1 );
1066 newdemo_write( md->submodel_active, sizeof(md->submodel_active), 1 );
1067 newdemo_write( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 );
1069 nd_write_object( md->obj );
1073 void newdemo_record_wall_toggle( int segnum, int side )
1076 nd_write_byte( ND_EVENT_WALL_TOGGLE );
1077 nd_write_int( segnum );
1078 nd_write_int( side );
1082 void newdemo_record_control_center_destroyed()
1085 nd_write_byte( ND_EVENT_CONTROL_CENTER_DESTROYED );
1086 nd_write_int( Countdown_seconds_left );
1090 void newdemo_record_hud_message( char * message )
1093 nd_write_byte( ND_EVENT_HUD_MESSAGE );
1094 nd_write_string(message);
1098 void newdemo_record_palette_effect(short r, short g, short b )
1101 nd_write_byte( ND_EVENT_PALETTE_EFFECT );
1102 nd_write_short( r );
1103 nd_write_short( g );
1104 nd_write_short( b );
1108 void newdemo_record_player_energy(int old_energy, int energy)
1111 nd_write_byte( ND_EVENT_PLAYER_ENERGY );
1112 nd_write_byte((sbyte) old_energy);
1113 nd_write_byte((sbyte) energy);
1117 void newdemo_record_player_afterburner(fix old_afterburner, fix afterburner)
1120 nd_write_byte( ND_EVENT_PLAYER_AFTERBURNER );
1121 nd_write_byte((sbyte) (old_afterburner>>9));
1122 nd_write_byte((sbyte) (afterburner>>9));
1126 void newdemo_record_player_shields(int old_shield, int shield)
1129 nd_write_byte( ND_EVENT_PLAYER_SHIELD );
1130 nd_write_byte((sbyte)old_shield);
1131 nd_write_byte((sbyte)shield);
1135 void newdemo_record_player_flags(uint oflags, uint flags)
1138 nd_write_byte( ND_EVENT_PLAYER_FLAGS );
1139 nd_write_int(((short)oflags << 16) | (short)flags);
1143 void newdemo_record_player_weapon(int weapon_type, int weapon_num)
1146 nd_write_byte( ND_EVENT_PLAYER_WEAPON );
1147 nd_write_byte((sbyte)weapon_type);
1148 nd_write_byte((sbyte)weapon_num);
1150 nd_write_byte((sbyte)Secondary_weapon);
1152 nd_write_byte((sbyte)Primary_weapon);
1156 void newdemo_record_effect_blowup(short segment, int side, vms_vector *pnt)
1159 nd_write_byte (ND_EVENT_EFFECT_BLOWUP);
1160 nd_write_short(segment);
1161 nd_write_byte((sbyte)side);
1162 nd_write_vector(pnt);
1166 void newdemo_record_homing_distance(fix distance)
1169 nd_write_byte(ND_EVENT_HOMING_DISTANCE);
1170 nd_write_short((short)(distance>>16));
1174 void newdemo_record_letterbox(void)
1177 nd_write_byte(ND_EVENT_LETTERBOX);
1181 void newdemo_record_rearview(void)
1184 nd_write_byte(ND_EVENT_REARVIEW);
1188 void newdemo_record_restore_cockpit(void)
1191 nd_write_byte(ND_EVENT_RESTORE_COCKPIT);
1195 void newdemo_record_restore_rearview(void)
1198 nd_write_byte(ND_EVENT_RESTORE_REARVIEW);
1202 void newdemo_record_wall_set_tmap_num1(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1205 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM1);
1206 nd_write_short(seg);
1207 nd_write_byte(side);
1208 nd_write_short(cseg);
1209 nd_write_byte(cside);
1210 nd_write_short(tmap);
1214 void newdemo_record_wall_set_tmap_num2(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1217 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM2);
1218 nd_write_short(seg);
1219 nd_write_byte(side);
1220 nd_write_short(cseg);
1221 nd_write_byte(cside);
1222 nd_write_short(tmap);
1226 void newdemo_record_multi_cloak(int pnum)
1229 nd_write_byte(ND_EVENT_MULTI_CLOAK);
1230 nd_write_byte((sbyte)pnum);
1234 void newdemo_record_multi_decloak(int pnum)
1237 nd_write_byte(ND_EVENT_MULTI_DECLOAK);
1238 nd_write_byte((sbyte)pnum);
1242 void newdemo_record_multi_death(int pnum)
1245 nd_write_byte(ND_EVENT_MULTI_DEATH);
1246 nd_write_byte((sbyte)pnum);
1250 void newdemo_record_multi_kill(int pnum, sbyte kill)
1253 nd_write_byte(ND_EVENT_MULTI_KILL);
1254 nd_write_byte((sbyte)pnum);
1255 nd_write_byte(kill);
1259 void newdemo_record_multi_connect(int pnum, int new_player, char *new_callsign)
1262 nd_write_byte(ND_EVENT_MULTI_CONNECT);
1263 nd_write_byte((sbyte)pnum);
1264 nd_write_byte((sbyte)new_player);
1266 nd_write_string(Players[pnum].callsign);
1267 nd_write_int(Players[pnum].net_killed_total);
1268 nd_write_int(Players[pnum].net_kills_total);
1270 nd_write_string(new_callsign);
1274 void newdemo_record_multi_reconnect(int pnum)
1277 nd_write_byte(ND_EVENT_MULTI_RECONNECT);
1278 nd_write_byte((sbyte)pnum);
1282 void newdemo_record_multi_disconnect(int pnum)
1285 nd_write_byte(ND_EVENT_MULTI_DISCONNECT);
1286 nd_write_byte((sbyte)pnum);
1290 void newdemo_record_player_score(int score)
1293 nd_write_byte(ND_EVENT_PLAYER_SCORE);
1294 nd_write_int(score);
1298 void newdemo_record_multi_score(int pnum, int score)
1301 nd_write_byte(ND_EVENT_MULTI_SCORE);
1302 nd_write_byte((sbyte)pnum);
1303 nd_write_int(score - Players[pnum].score); // called before score is changed!!!!
1307 void newdemo_record_primary_ammo(int old_ammo, int new_ammo)
1310 nd_write_byte(ND_EVENT_PRIMARY_AMMO);
1312 nd_write_short((short)new_ammo);
1314 nd_write_short((short)old_ammo);
1315 nd_write_short((short)new_ammo);
1319 void newdemo_record_secondary_ammo(int old_ammo, int new_ammo)
1322 nd_write_byte(ND_EVENT_SECONDARY_AMMO);
1324 nd_write_short((short)new_ammo);
1326 nd_write_short((short)old_ammo);
1327 nd_write_short((short)new_ammo);
1331 void newdemo_record_door_opening(int segnum, int side)
1334 nd_write_byte(ND_EVENT_DOOR_OPENING);
1335 nd_write_short((short)segnum);
1336 nd_write_byte((sbyte)side);
1340 void newdemo_record_laser_level(sbyte old_level, sbyte new_level)
1343 nd_write_byte(ND_EVENT_LASER_LEVEL);
1344 nd_write_byte(old_level);
1345 nd_write_byte(new_level);
1349 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)
1351 Assert(front_wall_num <= 255 && back_wall_num <= 255);
1354 nd_write_byte(ND_EVENT_CLOAKING_WALL);
1355 nd_write_byte(front_wall_num);
1356 nd_write_byte(back_wall_num);
1357 nd_write_byte(type);
1358 nd_write_byte(state);
1359 nd_write_byte(cloak_value);
1360 nd_write_short(l0>>8);
1361 nd_write_short(l1>>8);
1362 nd_write_short(l2>>8);
1363 nd_write_short(l3>>8);
1367 void newdemo_set_new_level(int level_num)
1374 nd_write_byte(ND_EVENT_NEW_LEVEL);
1375 nd_write_byte((sbyte)level_num);
1376 nd_write_byte((sbyte)Current_level_num);
1378 if (JustStartedRecording==1)
1380 nd_write_int(Num_walls);
1381 for (i=0;i<Num_walls;i++)
1383 nd_write_byte (Walls[i].type);
1384 nd_write_byte (Walls[i].flags);
1385 nd_write_byte (Walls[i].state);
1387 seg = &Segments[Walls[i].segnum];
1388 side = Walls[i].sidenum;
1389 nd_write_short (seg->sides[side].tmap_num);
1390 nd_write_short (seg->sides[side].tmap_num2);
1391 JustStartedRecording=0;
1398 int newdemo_read_demo_start(int rnd_demo)
1400 sbyte i, version, laser_level;
1401 sbyte c, energy, shield;
1402 char text[128], current_mission[9] = "";
1405 if ((c != ND_EVENT_START_DEMO) || nd_bad_read) {
1408 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_CORRUPT);
1409 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1410 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1413 nd_read_byte(&version);
1414 nd_read_byte(&Newdemo_game_type);
1415 if (Newdemo_game_type < DEMO_GAME_TYPE_D1_SHARE) {
1418 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1419 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1420 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Descent: First Strike";
1422 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1425 if (Newdemo_game_type > DEMO_GAME_TYPE) {
1428 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1429 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1430 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Unknown Descent version";
1432 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1435 if (version < DEMO_VERSION_D1_SHARE) {
1438 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_OLD);
1439 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1440 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1444 nd_read_fix(&GameTime);
1445 Boss_cloak_start_time=Boss_cloak_end_time=GameTime;
1446 JasonPlaybackTotal=0;
1448 nd_read_int(&Newdemo_game_mode);
1451 change_playernum_to((Newdemo_game_mode >> 16) & 0x7);
1452 if (Newdemo_game_mode & GM_TEAM) {
1453 nd_read_byte((sbyte *) &(Netgame.team_vector));
1455 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1) {
1456 nd_read_string(Netgame.team_name[0]);
1457 nd_read_string(Netgame.team_name[1]);
1461 if (Newdemo_game_mode & GM_MULTI) {
1466 // changed this to above two lines -- breaks on the mac because of
1468 // nd_read_byte((sbyte *)&N_players);
1469 for (i = 0 ; i < N_players; i++) {
1470 Players[i].cloak_time = 0;
1471 Players[i].invulnerable_time = 0;
1472 nd_read_string(Players[i].callsign);
1473 nd_read_byte(&(Players[i].connected));
1475 if (Newdemo_game_mode & GM_MULTI_COOP) {
1476 nd_read_int(&(Players[i].score));
1478 nd_read_short((short *)&(Players[i].net_killed_total));
1479 nd_read_short((short *)&(Players[i].net_kills_total));
1482 Game_mode = Newdemo_game_mode;
1483 multi_sort_kill_list();
1484 Game_mode = GM_NORMAL;
1487 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1)
1488 nd_read_int(&(Players[Player_num].score)); // Note link to above if!
1490 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1) {
1491 for (i = 0; i < (Newdemo_game_type < DEMO_GAME_TYPE_D2 ? 5 : MAX_PRIMARY_WEAPONS); i++)
1492 nd_read_short((short*)&(Players[Player_num].primary_ammo[i]));
1494 for (i = 0; i < (Newdemo_game_type < DEMO_GAME_TYPE_D2 ? 5 : MAX_SECONDARY_WEAPONS); i++)
1495 nd_read_short((short*)&(Players[Player_num].secondary_ammo[i]));
1497 nd_read_byte(&laser_level);
1498 if (laser_level != Players[Player_num].laser_level) {
1499 Players[Player_num].laser_level = laser_level;
1500 update_laser_weapon_info();
1503 // Support for missions
1505 nd_read_string(current_mission);
1507 if (!strcmp(current_mission, ""))
1508 strcpy(current_mission, "descent");
1509 if (!strcmp(current_mission, "d2demo") && !cfexist("d2demo.hog")) {
1510 strcpy(current_mission, "d2");
1511 Newdemo_is_d2demo = 1;
1513 if (!load_mission_by_name(current_mission)) {
1517 sprintf(text, TXT_NOMISSION4DEMO, current_mission);
1518 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1519 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1524 nd_recorded_total = 0;
1525 nd_playback_total = 0;
1526 nd_read_byte(&energy);
1527 nd_read_byte(&shield);
1529 nd_read_int((int *)&(Players[Player_num].flags));
1530 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
1531 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1532 Newdemo_players_cloaked |= (1 << Player_num);
1534 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
1535 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1537 nd_read_byte((sbyte *)&Primary_weapon);
1538 nd_read_byte((sbyte *)&Secondary_weapon);
1540 // Next bit of code to fix problem that I introduced between 1.0 and 1.1
1541 // check the next byte -- it _will_ be a load_new_level event. If it is
1542 // not, then we must shift all bytes up by one.
1544 if (Newdemo_game_type < DEMO_GAME_TYPE_D1)
1549 if (c != ND_EVENT_NEW_LEVEL) {
1552 flags = Players[Player_num].flags;
1554 shield = (unsigned char)flags;
1555 flags = (flags >> 8) & 0x00ffffff;
1556 flags |= (Primary_weapon << 24);
1557 Primary_weapon = Secondary_weapon;
1558 Secondary_weapon = c;
1560 PHYSFS_seek(infile, PHYSFS_tell(infile) - 1);
1563 Players[Player_num].energy = i2f(energy);
1564 Players[Player_num].shields = i2f(shield);
1565 JustStartedPlayback=1;
1569 void newdemo_pop_ctrlcen_triggers()
1573 segment *seg, *csegp;
1575 for (i = 0; i < ControlCenterTriggers.num_links; i++) {
1576 seg = &Segments[ControlCenterTriggers.seg[i]];
1577 side = ControlCenterTriggers.side[i];
1578 csegp = &Segments[seg->children[side]];
1579 cside = find_connect_side(seg, csegp);
1580 anim_num = Walls[seg->sides[side].wall_num].clip_num;
1581 n = WallAnims[anim_num].num_frames;
1582 if (WallAnims[anim_num].flags & WCF_TMAP1) {
1583 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[n-1];
1585 seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[n-1];
1590 #define N_PLAYER_SHIP_TEXTURES 6
1592 void nd_render_extras (ubyte,object *);
1593 extern void multi_apply_goal_textures ();
1594 ubyte Newdemo_flying_guided=0;
1596 int newdemo_read_frame_information()
1598 int done, segnum, side, objnum, soundno, angle, volume, i,shot;
1600 sbyte c = 0, WhichWindow;
1601 static sbyte saved_letter_cockpit;
1602 static sbyte saved_rearview_cockpit;
1604 static char LastReadValue=101;
1609 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1610 for (segnum=0; segnum <= Highest_segment_index; segnum++)
1611 Segments[segnum].objects = -1;
1614 Players[Player_num].homing_object_dist = -F1_0;
1620 if (nd_bad_read) { done = -1; break; }
1624 case ND_EVENT_START_FRAME: { // Followed by an integer frame number, then a fix FrameTime
1625 short last_frame_length;
1628 nd_read_short(&last_frame_length);
1629 nd_read_int(&NewdemoFrameCount);
1630 nd_read_int((int *)&nd_recorded_time);
1631 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1632 nd_recorded_total += nd_recorded_time;
1633 NewdemoFrameCount--;
1635 if (nd_bad_read) { done = -1; break; }
1639 case ND_EVENT_VIEWER_OBJECT: // Followed by an object structure
1640 if (Newdemo_game_type < DEMO_GAME_TYPE_D2)
1643 nd_read_byte (&WhichWindow);
1646 //mprintf ((0,"Reading extra!\n"));
1647 nd_read_object (&extraobj);
1648 if (Newdemo_vcr_state!=ND_STATE_PAUSED)
1650 if (nd_bad_read) { done = -1; break; }
1652 nd_render_extras (WhichWindow,&extraobj);
1657 //mprintf ((0,"Reading viewer!\n"));
1658 //Viewer=&Objects[0];
1659 nd_read_object(Viewer);
1661 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1662 if (nd_bad_read) { done = -1; break; }
1663 segnum = Viewer->segnum;
1664 Viewer->next = Viewer->prev = Viewer->segnum = -1;
1666 // HACK HACK HACK -- since we have multiple level recording, it can be the case
1667 // HACK HACK HACK -- that when rewinding the demo, the viewer is in a segment
1668 // HACK HACK HACK -- that is greater than the highest index of segments. Bash
1669 // HACK HACK HACK -- the viewer to segment 0 for bogus view.
1671 if (segnum > Highest_segment_index)
1673 obj_link(OBJECT_NUMBER(Viewer), segnum);
1678 case ND_EVENT_RENDER_OBJECT: // Followed by an object structure
1679 objnum = obj_allocate();
1682 obj = &Objects[objnum];
1683 nd_read_object(obj);
1684 if (nd_bad_read) { done = -1; break; }
1685 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1686 segnum = obj->segnum;
1687 obj->next = obj->prev = obj->segnum = -1;
1689 // HACK HACK HACK -- don't render objects is segments greater than Highest_segment_index
1690 // HACK HACK HACK -- (see above)
1692 if (segnum > Highest_segment_index)
1695 obj_link(OBJECT_NUMBER(obj), segnum);
1697 if ((obj->type == OBJ_PLAYER) && (Newdemo_game_mode & GM_MULTI)) {
1700 if (Newdemo_game_mode & GM_TEAM)
1701 player = get_team(obj->id);
1708 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
1709 multi_player_textures[player][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[obj->rtype.pobj_info.model_num].first_texture+i]];
1711 multi_player_textures[player][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2]];
1712 multi_player_textures[player][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2+1]];
1713 obj->rtype.pobj_info.alt_textures = player+1;
1719 case ND_EVENT_SOUND:
1720 nd_read_int(&soundno);
1721 if (nd_bad_read) {done = -1; break; }
1722 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1723 digi_play_sample( soundno, F1_0 );
1726 //--unused case ND_EVENT_SOUND_ONCE:
1727 //--unused nd_read_int(&soundno);
1728 //--unused if (nd_bad_read) { done = -1; break; }
1729 //--unused if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1730 //--unused digi_play_sample_once( soundno, F1_0 );
1733 case ND_EVENT_SOUND_3D:
1734 nd_read_int(&soundno);
1735 nd_read_int(&angle);
1736 nd_read_int(&volume);
1737 if (nd_bad_read) { done = -1; break; }
1738 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1739 digi_play_sample_3d( soundno, angle, volume, 0 );
1742 case ND_EVENT_SOUND_3D_ONCE:
1743 nd_read_int(&soundno);
1744 nd_read_int(&angle);
1745 nd_read_int(&volume);
1746 if (nd_bad_read) { done = -1; break; }
1747 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1748 digi_play_sample_3d( soundno, angle, volume, 1 );
1751 case ND_EVENT_LINK_SOUND_TO_OBJ:
1753 int soundno, objnum, max_volume, max_distance, loop_start, loop_end;
1755 nd_read_int( &soundno );
1756 nd_read_int( &signature );
1757 nd_read_int( &max_volume );
1758 nd_read_int( &max_distance );
1759 nd_read_int( &loop_start );
1760 nd_read_int( &loop_end );
1761 objnum = newdemo_find_object( signature );
1762 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1763 digi_link_sound_to_object3( soundno, objnum, 1, max_volume, max_distance, loop_start, loop_end );
1768 case ND_EVENT_KILL_SOUND_TO_OBJ:
1770 int objnum, signature;
1771 nd_read_int( &signature );
1772 objnum = newdemo_find_object( signature );
1773 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1774 digi_kill_sound_linked_to_object(objnum);
1779 case ND_EVENT_WALL_HIT_PROCESS: {
1783 nd_read_int(&segnum);
1785 nd_read_fix(&damage);
1786 nd_read_int(&player);
1787 if (nd_bad_read) { done = -1; break; }
1788 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1789 wall_hit_process(&Segments[segnum], side, damage, player, &(Objects[0]) );
1793 case ND_EVENT_TRIGGER:
1794 nd_read_int(&segnum);
1796 nd_read_int(&objnum);
1798 if (nd_bad_read) { done = -1; break; }
1799 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1801 mprintf ((0,"EVENT TRIGGER! shot=%d\n",shot));
1803 if (Triggers[Walls[Segments[segnum].sides[side].wall_num].trigger].type == TT_SECRET_EXIT) {
1807 Assert(c == ND_EVENT_SECRET_THINGY);
1808 nd_read_int(&truth);
1810 check_trigger(&Segments[segnum], side, objnum,shot);
1812 check_trigger(&Segments[segnum], side, objnum,shot);
1816 case ND_EVENT_HOSTAGE_RESCUED: {
1819 nd_read_int(&hostage_number);
1820 if (nd_bad_read) { done = -1; break; }
1821 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1822 hostage_rescue( hostage_number );
1826 case ND_EVENT_MORPH_FRAME: {
1830 md = &morph_objects[0];
1831 if (newdemo_read( md->morph_vecs, sizeof(md->morph_vecs), 1 )!=1) { done=-1; break; }
1832 if (newdemo_read( md->submodel_active, sizeof(md->submodel_active), 1 )!=1) { done=-1; break; }
1833 if (newdemo_read( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 )!=1) { done=-1; break; }
1835 objnum = obj_allocate();
1838 obj = &Objects[objnum];
1839 nd_read_object(obj);
1840 obj->render_type = RT_POLYOBJ;
1841 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1842 if (nd_bad_read) { done = -1; break; }
1843 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1844 segnum = obj->segnum;
1845 obj->next = obj->prev = obj->segnum = -1;
1846 obj_link(OBJECT_NUMBER(obj), segnum);
1852 case ND_EVENT_WALL_TOGGLE:
1853 nd_read_int(&segnum);
1855 if (nd_bad_read) {done = -1; break; }
1856 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1857 wall_toggle(&Segments[segnum], side);
1860 case ND_EVENT_CONTROL_CENTER_DESTROYED:
1861 nd_read_int(&Countdown_seconds_left);
1862 Control_center_destroyed = 1;
1863 if (nd_bad_read) { done = -1; break; }
1864 if (!Newdemo_cntrlcen_destroyed) {
1865 newdemo_pop_ctrlcen_triggers();
1866 Newdemo_cntrlcen_destroyed = 1;
1867 //do_controlcen_destroyed_stuff(NULL);
1871 case ND_EVENT_HUD_MESSAGE: {
1874 nd_read_string(&(hud_msg[0]));
1875 if (nd_bad_read) { done = -1; break; }
1876 HUD_init_message( hud_msg );
1879 case ND_EVENT_START_GUIDED:
1880 Newdemo_flying_guided=1;
1881 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1882 Newdemo_flying_guided=0;
1884 case ND_EVENT_END_GUIDED:
1885 Newdemo_flying_guided=0;
1886 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1887 Newdemo_flying_guided=1;
1890 case ND_EVENT_PALETTE_EFFECT: {
1896 if (nd_bad_read) { done = -1; break; }
1897 PALETTE_FLASH_SET(r,g,b);
1901 case ND_EVENT_PLAYER_ENERGY: {
1903 sbyte old_energy = 0;
1905 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1)
1906 nd_read_byte(&old_energy);
1907 nd_read_byte(&energy);
1908 if (nd_bad_read) {done = -1; break; }
1909 if ((Newdemo_game_type < DEMO_GAME_TYPE_D1) || (Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1910 Players[Player_num].energy = i2f(energy);
1911 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1912 if (old_energy != -128)
1913 Players[Player_num].energy = i2f(old_energy);
1918 case ND_EVENT_PLAYER_AFTERBURNER: {
1920 sbyte old_afterburner;
1922 nd_read_byte(&old_afterburner);
1923 nd_read_byte(&afterburner);
1924 if (nd_bad_read) {done = -1; break; }
1925 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1926 Afterburner_charge = afterburner<<9;
1927 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1928 if (old_afterburner != -128)
1929 Afterburner_charge = old_afterburner<<9;
1934 case ND_EVENT_PLAYER_SHIELD: {
1936 sbyte old_shield = 0;
1938 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1)
1939 nd_read_byte(&old_shield);
1940 nd_read_byte(&shield);
1941 if (nd_bad_read) {done = -1; break; }
1942 if ((Newdemo_game_type < DEMO_GAME_TYPE_D1) || (Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1943 Players[Player_num].shields = i2f(shield);
1944 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1945 if (old_shield != -128)
1946 Players[Player_num].shields = i2f(old_shield);
1951 case ND_EVENT_PLAYER_FLAGS: {
1954 nd_read_int((int *)&(Players[Player_num].flags));
1955 if (nd_bad_read) {done = -1; break; }
1957 oflags = Players[Player_num].flags >> 16;
1958 Players[Player_num].flags &= 0xffff;
1960 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || ((Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) && (oflags != 0xffff)) ) {
1961 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1962 Players[Player_num].cloak_time = 0;
1963 Newdemo_players_cloaked &= ~(1 << Player_num);
1965 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1966 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1967 Newdemo_players_cloaked |= (1 << Player_num);
1969 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1970 Players[Player_num].invulnerable_time = 0;
1971 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1972 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1973 Players[Player_num].flags = oflags;
1974 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1975 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1976 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1977 Newdemo_players_cloaked |= (1 << Player_num);
1979 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1980 Players[Player_num].cloak_time = 0;
1981 Newdemo_players_cloaked &= ~(1 << Player_num);
1983 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1984 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1985 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1986 Players[Player_num].invulnerable_time = 0;
1988 update_laser_weapon_info(); // in case of quad laser change
1992 case ND_EVENT_PLAYER_WEAPON: {
1993 sbyte weapon_type, weapon_num;
1994 sbyte old_weapon = 0;
1996 nd_read_byte(&weapon_type);
1997 nd_read_byte(&weapon_num);
1998 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1)
1999 nd_read_byte(&old_weapon);
2000 if ((Newdemo_game_type < DEMO_GAME_TYPE_D1) || (Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2001 if (weapon_type == 0)
2002 Primary_weapon = (int)weapon_num;
2004 Secondary_weapon = (int)weapon_num;
2005 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2006 if (weapon_type == 0)
2007 Primary_weapon = (int)old_weapon;
2009 Secondary_weapon = (int)old_weapon;
2014 case ND_EVENT_EFFECT_BLOWUP: {
2020 //create a dummy object which will be the weapon that hits
2021 //the monitor. the blowup code wants to know who the parent of the
2022 //laser is, so create a laser whose parent is the player
2023 dummy.ctype.laser_info.parent_type = OBJ_PLAYER;
2025 nd_read_short(&segnum);
2026 nd_read_byte(&side);
2027 nd_read_vector(&pnt);
2028 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2029 check_effect_blowup(&(Segments[segnum]), side, &pnt, &dummy, 0);
2033 case ND_EVENT_HOMING_DISTANCE: {
2036 nd_read_short(&distance);
2037 Players[Player_num].homing_object_dist = i2f((int)(distance << 16));
2041 case ND_EVENT_LETTERBOX:
2042 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2043 saved_letter_cockpit = Cockpit_mode.intval;
2044 select_cockpit(CM_LETTERBOX);
2045 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2046 select_cockpit(saved_letter_cockpit);
2049 case ND_EVENT_CHANGE_COCKPIT: {
2052 nd_read_int (&dummy);
2053 select_cockpit (dummy);
2057 case ND_EVENT_REARVIEW:
2058 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2059 saved_rearview_cockpit = Cockpit_mode.intval;
2060 if (Cockpit_mode.intval == CM_FULL_COCKPIT)
2061 select_cockpit(CM_REAR_VIEW);
2063 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2064 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2065 saved_rearview_cockpit = CM_FULL_COCKPIT;
2066 select_cockpit(saved_rearview_cockpit);
2071 case ND_EVENT_RESTORE_COCKPIT:
2072 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2073 saved_letter_cockpit = Cockpit_mode.intval;
2074 select_cockpit(CM_LETTERBOX);
2075 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2076 select_cockpit(saved_letter_cockpit);
2080 case ND_EVENT_RESTORE_REARVIEW:
2081 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2082 saved_rearview_cockpit = Cockpit_mode.intval;
2083 if (Cockpit_mode.intval == CM_FULL_COCKPIT)
2084 select_cockpit(CM_REAR_VIEW);
2086 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2087 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2088 saved_rearview_cockpit = CM_FULL_COCKPIT;
2089 select_cockpit(saved_rearview_cockpit);
2095 case ND_EVENT_WALL_SET_TMAP_NUM1: {
2096 short seg, cseg, tmap;
2099 nd_read_short(&seg);
2100 nd_read_byte(&side);
2101 nd_read_short(&cseg);
2102 nd_read_byte(&cside);
2103 nd_read_short( &tmap );
2104 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD))
2105 Segments[seg].sides[side].tmap_num = Segments[cseg].sides[cside].tmap_num = tmap;
2109 case ND_EVENT_WALL_SET_TMAP_NUM2: {
2110 short seg, cseg, tmap;
2113 nd_read_short(&seg);
2114 nd_read_byte(&side);
2115 nd_read_short(&cseg);
2116 nd_read_byte(&cside);
2117 nd_read_short( &tmap );
2118 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) {
2119 Assert(tmap!=0 && Segments[seg].sides[side].tmap_num2!=0);
2120 Segments[seg].sides[side].tmap_num2 = Segments[cseg].sides[cside].tmap_num2 = tmap;
2125 case ND_EVENT_MULTI_CLOAK: {
2128 nd_read_byte(&pnum);
2129 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2130 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2131 Players[pnum].cloak_time = 0;
2132 Newdemo_players_cloaked &= ~(1 << pnum);
2133 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2134 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2135 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2136 Newdemo_players_cloaked |= (1 << pnum);
2141 case ND_EVENT_MULTI_DECLOAK: {
2144 nd_read_byte(&pnum);
2146 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2147 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2148 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2149 Newdemo_players_cloaked |= (1 << pnum);
2150 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2151 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2152 Players[pnum].cloak_time = 0;
2153 Newdemo_players_cloaked &= ~(1 << pnum);
2158 case ND_EVENT_MULTI_DEATH: {
2161 nd_read_byte(&pnum);
2162 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2163 Players[pnum].net_killed_total--;
2164 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2165 Players[pnum].net_killed_total++;
2170 case ND_EVENT_MULTI_KILL: {
2173 nd_read_byte(&pnum);
2174 nd_read_byte(&kill);
2175 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2176 Players[pnum].net_kills_total -= kill;
2177 if (Newdemo_game_mode & GM_TEAM)
2178 team_kills[get_team(pnum)] -= kill;
2179 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2180 Players[pnum].net_kills_total += kill;
2181 if (Newdemo_game_mode & GM_TEAM)
2182 team_kills[get_team(pnum)] += kill;
2184 Game_mode = Newdemo_game_mode;
2185 multi_sort_kill_list();
2186 Game_mode = GM_NORMAL;
2190 case ND_EVENT_MULTI_CONNECT: {
2191 sbyte pnum, new_player;
2192 int killed_total = 0, kills_total = 0;
2193 char new_callsign[CALLSIGN_LEN+1], old_callsign[CALLSIGN_LEN+1];
2195 nd_read_byte(&pnum);
2196 nd_read_byte(&new_player);
2198 nd_read_string(old_callsign);
2199 nd_read_int(&killed_total);
2200 nd_read_int(&kills_total);
2202 nd_read_string(new_callsign);
2203 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2204 Players[pnum].connected = 0;
2206 memcpy(Players[pnum].callsign, old_callsign, CALLSIGN_LEN+1);
2207 Players[pnum].net_killed_total = killed_total;
2208 Players[pnum].net_kills_total = kills_total;
2212 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2213 Players[pnum].connected = 1;
2214 Players[pnum].net_kills_total = 0;
2215 Players[pnum].net_killed_total = 0;
2216 memcpy(Players[pnum].callsign, new_callsign, CALLSIGN_LEN+1);
2223 case ND_EVENT_MULTI_RECONNECT: {
2226 nd_read_byte(&pnum);
2227 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2228 Players[pnum].connected = 0;
2229 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2230 Players[pnum].connected = 1;
2234 case ND_EVENT_MULTI_DISCONNECT: {
2237 nd_read_byte(&pnum);
2238 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2239 Players[pnum].connected = 1;
2240 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2241 Players[pnum].connected = 0;
2245 case ND_EVENT_MULTI_SCORE: {
2249 nd_read_byte(&pnum);
2250 nd_read_int(&score);
2251 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2252 Players[pnum].score -= score;
2253 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2254 Players[pnum].score += score;
2255 Game_mode = Newdemo_game_mode;
2256 multi_sort_kill_list();
2257 Game_mode = GM_NORMAL;
2262 case ND_EVENT_PLAYER_SCORE: {
2265 nd_read_int(&score);
2266 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2267 Players[Player_num].score -= score;
2268 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2269 Players[Player_num].score += score;
2274 case ND_EVENT_PRIMARY_AMMO: {
2275 short old_ammo, new_ammo;
2277 nd_read_short(&old_ammo);
2278 nd_read_short(&new_ammo);
2280 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2281 Players[Player_num].primary_ammo[Primary_weapon] = old_ammo;
2282 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2283 Players[Player_num].primary_ammo[Primary_weapon] = new_ammo;
2287 case ND_EVENT_SECONDARY_AMMO: {
2288 short old_ammo, new_ammo;
2290 nd_read_short(&old_ammo);
2291 nd_read_short(&new_ammo);
2293 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2294 Players[Player_num].secondary_ammo[Secondary_weapon] = old_ammo;
2295 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2296 Players[Player_num].secondary_ammo[Secondary_weapon] = new_ammo;
2300 case ND_EVENT_DOOR_OPENING: {
2304 nd_read_short(&segnum);
2305 nd_read_byte(&side);
2306 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2309 segment *segp, *csegp;
2311 segp = &Segments[segnum];
2312 csegp = &Segments[segp->children[side]];
2313 cside = find_connect_side(segp, csegp);
2314 anim_num = Walls[segp->sides[side].wall_num].clip_num;
2316 if (WallAnims[anim_num].flags & WCF_TMAP1) {
2317 segp->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[0];
2319 segp->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[0];
2325 case ND_EVENT_LASER_LEVEL: {
2326 sbyte old_level, new_level;
2328 nd_read_byte(&old_level);
2329 nd_read_byte(&new_level);
2330 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2331 Players[Player_num].laser_level = old_level;
2332 update_laser_weapon_info();
2333 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2334 Players[Player_num].laser_level = new_level;
2335 update_laser_weapon_info();
2340 case ND_EVENT_CLOAKING_WALL: {
2341 sbyte back_wall_num,front_wall_num,type,state,cloak_value;
2346 nd_read_byte(&front_wall_num);
2347 nd_read_byte(&back_wall_num);
2348 nd_read_byte(&type);
2349 nd_read_byte(&state);
2350 nd_read_byte(&cloak_value);
2356 Walls[front_wall_num].type = type;
2357 Walls[front_wall_num].state = state;
2358 Walls[front_wall_num].cloak_value = cloak_value;
2359 segp = &Segments[Walls[front_wall_num].segnum];
2360 sidenum = Walls[front_wall_num].sidenum;
2361 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2362 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2363 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2364 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2366 Walls[back_wall_num].type = type;
2367 Walls[back_wall_num].state = state;
2368 Walls[back_wall_num].cloak_value = cloak_value;
2369 segp = &Segments[Walls[back_wall_num].segnum];
2370 sidenum = Walls[back_wall_num].sidenum;
2371 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2372 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2373 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2374 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2379 case ND_EVENT_NEW_LEVEL: {
2380 sbyte new_level, old_level, loaded_level;
2382 nd_read_byte (&new_level);
2383 nd_read_byte (&old_level);
2384 if (Newdemo_vcr_state == ND_STATE_PAUSED)
2388 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2389 loaded_level = old_level;
2391 loaded_level = new_level;
2392 for (i = 0; i < MAX_PLAYERS; i++) {
2393 Players[i].cloak_time = 0;
2394 Players[i].flags &= ~PLAYER_FLAGS_CLOAKED;
2397 if ((loaded_level < Last_secret_level) || (loaded_level > Last_level)) {
2400 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2401 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2402 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2403 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2407 LoadLevel((int)loaded_level,1);
2408 Newdemo_cntrlcen_destroyed = 0;
2410 if (JustStartedPlayback && Newdemo_game_type >= DEMO_GAME_TYPE_D2)
2412 nd_read_int (&Num_walls);
2413 for (i=0;i<Num_walls;i++) // restore the walls
2415 nd_read_byte ((signed char *)&Walls[i].type);
2416 nd_read_byte ((signed char *)&Walls[i].flags);
2417 nd_read_byte ((signed char *)&Walls[i].state);
2419 seg = &Segments[Walls[i].segnum];
2420 side = Walls[i].sidenum;
2421 nd_read_short (&seg->sides[side].tmap_num);
2422 nd_read_short (&seg->sides[side].tmap_num2);
2425 if (Newdemo_game_mode & GM_CAPTURE)
2426 multi_apply_goal_textures ();
2428 JustStartedPlayback=0;
2432 // so says Rob H.!!! if (Newdemo_game_mode & GM_MULTI) {
2433 // so says Rob H.!!! for (i = 0; i < Num_walls; i++) {
2434 // so says Rob H.!!! if (Walls[i].type == WALL_BLASTABLE)
2435 // so says Rob H.!!! {
2436 // so says Rob H.!!! int a, n;
2437 // so says Rob H.!!! int side;
2438 // so says Rob H.!!! segment *seg;
2439 // so says Rob H.!!!
2440 // so says Rob H.!!! seg = &Segments[Walls[i].segnum];
2441 // so says Rob H.!!! side = Walls[i].sidenum;
2442 // so says Rob H.!!! a = Walls[i].clip_num;
2443 // so says Rob H.!!! n = WallAnims[a].num_frames;
2444 // so says Rob H.!!! seg->sides[side].tmap_num = WallAnims[a].frames[n-1];
2445 // so says Rob H.!!! Walls[i].flags |= WALL_BLASTED;
2446 // so says Rob H.!!! }
2447 // so says Rob H.!!! }
2448 // so says Rob H.!!! }
2450 reset_palette_add(); // get palette back to normal
2455 case ND_EVENT_EOF: {
2457 PHYSFS_seek(infile, PHYSFS_tell(infile) - 1); // get back to the EOF marker
2459 NewdemoFrameCount++;
2473 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_ERR_READING;
2474 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_DEMO_OLD_CORRUPT;
2475 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2481 void newdemo_goto_beginning()
2483 //if (NewdemoFrameCount == 0)
2485 PHYSFS_seek(infile, 0);
2486 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2487 if (newdemo_read_demo_start(0))
2488 newdemo_stop_playback();
2489 if (newdemo_read_frame_information() == -1)
2490 newdemo_stop_playback();
2491 if (newdemo_read_frame_information() == -1)
2492 newdemo_stop_playback();
2493 Newdemo_vcr_state = ND_STATE_PAUSED;
2497 void newdemo_goto_end()
2499 short frame_length, byte_count, bshort;
2500 sbyte level, bbyte, laser_level;
2501 sbyte energy, shield, c;
2504 PHYSFS_seek(infile, PHYSFS_fileLength(infile) - 2);
2505 nd_read_byte(&level);
2507 if ((level < Last_secret_level) || (level > Last_level)) {
2510 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2511 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2512 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2513 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2514 newdemo_stop_playback();
2517 if (level != Current_level_num)
2520 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1) {
2521 PHYSFS_seek(infile, PHYSFS_fileLength(infile) - 4);
2522 nd_read_short(&byte_count);
2523 PHYSFS_seek(infile, PHYSFS_tell(infile) - 2 - byte_count);
2525 PHYSFS_seek(infile, PHYSFS_fileLength(infile) - 12);
2527 nd_read_short(&frame_length);
2528 loc = (int)PHYSFS_tell(infile);
2529 if (Newdemo_game_mode & GM_MULTI)
2530 nd_read_byte(&Newdemo_players_cloaked);
2532 nd_read_byte(&bbyte);
2533 nd_read_byte(&bbyte);
2534 nd_read_short(&bshort);
2537 nd_read_byte(&energy);
2538 nd_read_byte(&shield);
2539 Players[Player_num].energy = i2f(energy);
2540 Players[Player_num].shields = i2f(shield);
2541 nd_read_int((int *)&(Players[Player_num].flags));
2542 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
2543 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2544 Newdemo_players_cloaked |= (1 << Player_num);
2546 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2547 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2548 nd_read_byte((sbyte *)&Primary_weapon);
2549 nd_read_byte((sbyte *)&Secondary_weapon);
2550 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
2551 nd_read_short((short *)&(Players[Player_num].primary_ammo[i]));
2552 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
2553 nd_read_short((short *)&(Players[Player_num].secondary_ammo[i]));
2554 nd_read_byte(&laser_level);
2555 if (laser_level != Players[Player_num].laser_level) {
2556 Players[Player_num].laser_level = laser_level;
2557 update_laser_weapon_info();
2560 if (Newdemo_game_mode & GM_MULTI) {
2563 // see newdemo_read_start_demo for explanation of
2564 // why this is commented out
2565 // nd_read_byte((sbyte *)&N_players);
2566 for (i = 0; i < N_players; i++) {
2567 nd_read_string(Players[i].callsign);
2568 nd_read_byte(&(Players[i].connected));
2569 if (Newdemo_game_mode & GM_MULTI_COOP) {
2570 nd_read_int(&(Players[i].score));
2572 nd_read_short((short *)&(Players[i].net_killed_total));
2573 nd_read_short((short *)&(Players[i].net_kills_total));
2577 nd_read_int(&(Players[Player_num].score));
2580 PHYSFS_seek(infile, loc);
2581 PHYSFS_seek(infile, PHYSFS_tell(infile) - frame_length);
2582 nd_read_int(&NewdemoFrameCount); // get the frame count
2583 NewdemoFrameCount--;
2584 PHYSFS_seek(infile, PHYSFS_tell(infile) + 4);
2585 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2586 newdemo_read_frame_information(); // then the frame information
2587 Newdemo_vcr_state = ND_STATE_PAUSED;
2591 void newdemo_back_frames(int frames)
2593 short last_frame_length;
2596 for (i = 0; i < frames; i++)
2598 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
2599 nd_read_short(&last_frame_length);
2600 PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
2602 if (!Newdemo_at_eof && newdemo_read_frame_information() == -1) {
2603 newdemo_stop_playback();
2609 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
2610 nd_read_short(&last_frame_length);
2611 PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
2617 * routine to interpolate the viewer position. the current position is
2618 * stored in the Viewer object. Save this position, and read the next
2619 * frame to get all objects read in. Calculate the delta playback and
2620 * the delta recording frame times between the two frames, then intepolate
2621 * the viewers position accordingly. nd_recorded_time is the time that it
2622 * took the recording to render the frame that we are currently looking
2626 void interpolate_frame(fix d_play, fix d_recorded)
2628 int i, j, num_cur_objs;
2632 factor = fixdiv(d_play, d_recorded);
2636 num_cur_objs = Highest_object_index;
2637 cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1));
2638 if (cur_objs == NULL) {
2639 mprintf((0,"Couldn't get %d bytes for cur_objs in interpolate_frame\n", sizeof(object) * num_cur_objs));
2643 for (i = 0; i <= num_cur_objs; i++)
2644 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2646 Newdemo_vcr_state = ND_STATE_PAUSED;
2647 if (newdemo_read_frame_information() == -1) {
2649 newdemo_stop_playback();
2653 for (i = 0; i <= num_cur_objs; i++) {
2654 for (j = 0; j <= Highest_object_index; j++) {
2655 if (cur_objs[i].signature == Objects[j].signature) {
2656 sbyte render_type = cur_objs[i].render_type;
2657 //fix delta_p, delta_h, delta_b;
2658 fix delta_x, delta_y, delta_z;
2659 //vms_angvec cur_angles, dest_angles;
2661 // Extract the angles from the object orientation matrix.
2662 // Some of this code taken from ai_turn_towards_vector
2663 // Don't do the interpolation on certain render types which don't use an orientation matrix
2665 if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) {
2667 vms_vector fvec1, fvec2, rvec1, rvec2;
2670 fvec1 = cur_objs[i].orient.fvec;
2671 vm_vec_scale(&fvec1, F1_0-factor);
2672 fvec2 = Objects[j].orient.fvec;
2673 vm_vec_scale(&fvec2, factor);
2674 vm_vec_add2(&fvec1, &fvec2);
2675 mag1 = vm_vec_normalize_quick(&fvec1);
2676 if (mag1 > F1_0/256) {
2677 rvec1 = cur_objs[i].orient.rvec;
2678 vm_vec_scale(&rvec1, F1_0-factor);
2679 rvec2 = Objects[j].orient.rvec;
2680 vm_vec_scale(&rvec2, factor);
2681 vm_vec_add2(&rvec1, &rvec2);
2682 vm_vec_normalize_quick(&rvec1); // Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1
2683 vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2686 //--old new way -- vms_vector fvec1, fvec2, rvec1, rvec2;
2688 //--old new way -- fvec1 = cur_objs[i].orient.fvec;
2689 //--old new way -- vm_vec_scale(&fvec1, F1_0-factor);
2690 //--old new way -- fvec2 = Objects[j].orient.fvec;
2691 //--old new way -- vm_vec_scale(&fvec2, factor);
2692 //--old new way -- vm_vec_add2(&fvec1, &fvec2);
2693 //--old new way -- vm_vec_normalize_quick(&fvec1);
2695 //--old new way -- rvec1 = cur_objs[i].orient.rvec;
2696 //--old new way -- vm_vec_scale(&rvec1, F1_0-factor);
2697 //--old new way -- rvec2 = Objects[j].orient.rvec;
2698 //--old new way -- vm_vec_scale(&rvec2, factor);
2699 //--old new way -- vm_vec_add2(&rvec1, &rvec2);
2700 //--old new way -- vm_vec_normalize_quick(&rvec1);
2702 //--old new way -- vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2704 // -- old fashioned way -- vm_extract_angles_matrix(&cur_angles, &(cur_objs[i].orient));
2705 // -- old fashioned way -- vm_extract_angles_matrix(&dest_angles, &(Objects[j].orient));
2706 // -- old fashioned way --
2707 // -- old fashioned way -- delta_p = (dest_angles.p - cur_angles.p);
2708 // -- old fashioned way -- delta_h = (dest_angles.h - cur_angles.h);
2709 // -- old fashioned way -- delta_b = (dest_angles.b - cur_angles.b);
2710 // -- old fashioned way --
2711 // -- old fashioned way -- if (delta_p != 0) {
2712 // -- old fashioned way -- if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
2713 // -- old fashioned way -- if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
2714 // -- old fashioned way -- delta_p = fixmul(delta_p, factor);
2715 // -- old fashioned way -- cur_angles.p += delta_p;
2716 // -- old fashioned way -- }
2717 // -- old fashioned way -- if (delta_h != 0) {
2718 // -- old fashioned way -- if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
2719 // -- old fashioned way -- if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
2720 // -- old fashioned way -- delta_h = fixmul(delta_h, factor);
2721 // -- old fashioned way -- cur_angles.h += delta_h;
2722 // -- old fashioned way -- }
2723 // -- old fashioned way -- if (delta_b != 0) {
2724 // -- old fashioned way -- if (delta_b > F1_0/2) delta_b = dest_angles.b - cur_angles.b - F1_0;
2725 // -- old fashioned way -- if (delta_b < -F1_0/2) delta_b = dest_angles.b - cur_angles.b + F1_0;
2726 // -- old fashioned way -- delta_b = fixmul(delta_b, factor);
2727 // -- old fashioned way -- cur_angles.b += delta_b;
2728 // -- old fashioned way -- }
2731 // Interpolate the object position. This is just straight linear
2734 delta_x = Objects[j].pos.x - cur_objs[i].pos.x;
2735 delta_y = Objects[j].pos.y - cur_objs[i].pos.y;
2736 delta_z = Objects[j].pos.z - cur_objs[i].pos.z;
2738 delta_x = fixmul(delta_x, factor);
2739 delta_y = fixmul(delta_y, factor);
2740 delta_z = fixmul(delta_z, factor);
2742 cur_objs[i].pos.x += delta_x;
2743 cur_objs[i].pos.y += delta_y;
2744 cur_objs[i].pos.z += delta_z;
2746 // -- old fashioned way --// stuff the new angles back into the object structure
2747 // -- old fashioned way -- vm_angles_2_matrix(&(cur_objs[i].orient), &cur_angles);
2752 // get back to original position in the demo file. Reread the current
2753 // frame information again to reset all of the object stuff not covered
2754 // with Highest_object_index and the object array (previously rendered
2755 // objects, etc....)
2757 newdemo_back_frames(1);
2758 newdemo_back_frames(1);
2759 if (newdemo_read_frame_information() == -1)
2760 newdemo_stop_playback();
2761 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2763 for (i = 0; i <= num_cur_objs; i++)
2764 memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object));
2765 Highest_object_index = num_cur_objs;
2769 void newdemo_playback_one_frame()
2771 int frames_back, i, level;
2772 static fix base_interpol_time = 0;
2773 static fix d_recorded = 0;
2775 for (i = 0; i < MAX_PLAYERS; i++)
2776 if (Newdemo_players_cloaked & (1 << i))
2777 Players[i].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2779 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2780 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2782 if (Newdemo_vcr_state == ND_STATE_PAUSED) // render a frame or not
2785 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2786 DoJasonInterpolate(nd_recorded_time);
2788 Control_center_destroyed = 0;
2789 Countdown_seconds_left = -1;
2790 PALETTE_FLASH_SET(0,0,0); //clear flash
2792 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2794 level = Current_level_num;
2795 if (NewdemoFrameCount == 0)
2797 else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (NewdemoFrameCount < 10)) {
2798 newdemo_goto_beginning();
2801 if (Newdemo_vcr_state == ND_STATE_REWINDING)
2805 if (Newdemo_at_eof) {
2806 if (Newdemo_game_type == DEMO_GAME_TYPE_D1_SHARE)
2807 PHYSFS_seek(infile, PHYSFS_fileLength(infile) - 2);
2809 PHYSFS_seek(infile, PHYSFS_tell(infile) + 11);
2811 newdemo_back_frames(frames_back);
2813 if (level != Current_level_num)
2814 newdemo_pop_ctrlcen_triggers();
2816 if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) {
2817 if (level != Current_level_num)
2818 newdemo_back_frames(1);
2819 Newdemo_vcr_state = ND_STATE_PAUSED;
2822 else if (Newdemo_vcr_state == ND_STATE_FASTFORWARD) {
2823 if (!Newdemo_at_eof)
2825 for (i = 0; i < 10; i++)
2827 if (newdemo_read_frame_information() == -1)
2830 Newdemo_vcr_state = ND_STATE_PAUSED;
2832 newdemo_stop_playback();
2838 Newdemo_vcr_state = ND_STATE_PAUSED;
2840 else if (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD) {
2841 if (!Newdemo_at_eof) {
2842 level = Current_level_num;
2843 if (newdemo_read_frame_information() == -1) {
2844 if (!Newdemo_at_eof)
2845 newdemo_stop_playback();
2847 if (level != Current_level_num) {
2848 if (newdemo_read_frame_information() == -1) {
2849 if (!Newdemo_at_eof)
2850 newdemo_stop_playback();
2853 Newdemo_vcr_state = ND_STATE_PAUSED;
2855 Newdemo_vcr_state = ND_STATE_PAUSED;
2859 // First, uptate the total playback time to date. Then we check to see
2860 // if we need to change the playback style to interpolate frames or
2861 // skip frames based on where the playback time is relative to the
2864 if (NewdemoFrameCount <= 0)
2865 nd_playback_total = nd_recorded_total; // baseline total playback time
2867 nd_playback_total += FrameTime;
2868 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2869 if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) {
2870 playback_style = INTERPOLATE_PLAYBACK;
2871 nd_playback_total = nd_recorded_total + FrameTime; // baseline playback time
2872 base_interpol_time = nd_recorded_total;
2873 d_recorded = nd_recorded_time; // baseline delta recorded
2875 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2876 if (nd_playback_total > nd_recorded_total)
2877 playback_style = SKIP_PLAYBACK;
2880 if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) {
2883 if (nd_recorded_total - nd_playback_total < FrameTime) {
2884 d_recorded = nd_recorded_total - nd_playback_total;
2886 while (nd_recorded_total - nd_playback_total < FrameTime) {
2888 int i, j, num_objs, level;
2890 num_objs = Highest_object_index;
2891 cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1));
2892 if (cur_objs == NULL) {
2893 Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs);
2896 for (i = 0; i <= num_objs; i++)
2897 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2899 level = Current_level_num;
2900 if (newdemo_read_frame_information() == -1) {
2902 newdemo_stop_playback();
2905 if (level != Current_level_num) {
2907 if (newdemo_read_frame_information() == -1)
2908 newdemo_stop_playback();
2912 // for each new object in the frame just read in, determine if there is
2913 // a corresponding object that we have been interpolating. If so, then
2914 // copy that interpolated object to the new Objects array so that the
2915 // interpolated position and orientation can be preserved.
2917 for (i = 0; i <= num_objs; i++) {
2918 for (j = 0; j <= Highest_object_index; j++) {
2919 if (cur_objs[i].signature == Objects[j].signature) {
2920 memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix));
2921 memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector));
2927 d_recorded += nd_recorded_time;
2928 base_interpol_time = nd_playback_total - FrameTime;
2932 d_play = nd_playback_total - base_interpol_time;
2933 interpolate_frame(d_play, d_recorded);
2937 //mprintf ((0, "*"));
2938 if (newdemo_read_frame_information() == -1) {
2939 newdemo_stop_playback();
2942 if (playback_style == SKIP_PLAYBACK) {
2943 //mprintf ((0, "."));
2944 while (nd_playback_total > nd_recorded_total) {
2945 if (newdemo_read_frame_information() == -1) {
2946 newdemo_stop_playback();
2955 void newdemo_start_recording()
2957 Newdemo_size = (int)PHYSFSX_getFreeDiskSpace();
2958 con_printf(CON_VERBOSE, "Free space = %d\n", Newdemo_size);
2960 Newdemo_size -= 100000;
2962 if ((Newdemo_size+100000) < 2000000000) {
2963 if (((int)(Newdemo_size)) < 500000) {
2965 nm_messagebox(NULL, 1, TXT_OK, TXT_DEMO_NO_SPACE);
2967 nm_messagebox(NULL, 1, TXT_OK, "Not enough space on current\ndrive to start demo recording.");
2973 Newdemo_num_written = 0;
2975 Newdemo_state = ND_STATE_RECORDING;
2976 outfile = PHYSFSX_openWriteBuffered(DEMO_FILENAME);
2978 #if !defined(MACINTOSH) && !defined(_WIN32_WCE)
2979 if (outfile == NULL && errno == ENOENT) //dir doesn't exist?
2981 if (outfile == NULL) //dir doesn't exist and no errno on mac!
2984 PHYSFS_mkdir(DEMO_DIR); //try making directory
2985 outfile = PHYSFSX_openWriteBuffered(DEMO_FILENAME);
2988 if (outfile == NULL)
2990 nm_messagebox(NULL, 1, TXT_OK, "Cannot open demo temp file");
2991 Newdemo_state = ND_STATE_NORMAL;
2994 newdemo_record_start_demo();
2998 char demoname_allowed_chars[] = "azAZ09__--";
2999 void newdemo_stop_recording()
3003 static char filename[15] = "", *s;
3004 static sbyte tmpcnt = 0;
3006 char fullname[15+FILENAME_LEN] = DEMO_DIR;
3007 unsigned short byte_count = 0;
3011 nd_write_byte(ND_EVENT_EOF);
3012 nd_write_short(frame_bytes_written - 1);
3013 if (Game_mode & GM_MULTI) {
3014 for (l = 0; l < N_players; l++) {
3015 if (Players[l].flags & PLAYER_FLAGS_CLOAKED)
3016 cloaked |= (1 << l);
3018 nd_write_byte(cloaked);
3019 nd_write_byte(ND_EVENT_EOF);
3021 nd_write_short(ND_EVENT_EOF);
3023 nd_write_short(ND_EVENT_EOF);
3024 nd_write_int(ND_EVENT_EOF);
3026 byte_count += 10; // from frame_bytes_written
3028 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
3029 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
3030 nd_write_int(Players[Player_num].flags); // be sure players flags are set
3031 nd_write_byte((sbyte)Primary_weapon);
3032 nd_write_byte((sbyte)Secondary_weapon);
3035 for (l = 0; l < MAX_PRIMARY_WEAPONS; l++)
3036 nd_write_short((short)Players[Player_num].primary_ammo[l]);
3038 for (l = 0; l < MAX_SECONDARY_WEAPONS; l++)
3039 nd_write_short((short)Players[Player_num].secondary_ammo[l]);
3040 byte_count += (sizeof(short) * (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS));
3042 nd_write_byte(Players[Player_num].laser_level);
3045 if (Game_mode & GM_MULTI) {
3046 nd_write_byte((sbyte)N_players);
3048 for (l = 0; l < N_players; l++) {
3049 nd_write_string(Players[l].callsign);
3050 byte_count += (strlen(Players[l].callsign) + 2);
3051 nd_write_byte(Players[l].connected);
3052 if (Game_mode & GM_MULTI_COOP) {
3053 nd_write_int(Players[l].score);
3056 nd_write_short((short)Players[l].net_killed_total);
3057 nd_write_short((short)Players[l].net_kills_total);
3062 nd_write_int(Players[Player_num].score);
3065 nd_write_short(byte_count);
3067 nd_write_byte(Current_level_num);
3068 nd_write_byte(ND_EVENT_EOF);
3070 l = (int)PHYSFS_tell(outfile);
3071 PHYSFS_close(outfile);
3073 Newdemo_state = ND_STATE_NORMAL;
3074 gr_palette_load( gr_palette );
3076 if (filename[0] != '\0') {
3077 int num, i = (int)strlen(filename) - 1;
3080 while (isdigit(filename[i])) {
3086 num = atoi(&(filename[i]));
3089 sprintf (newfile, "%s%d", filename, num);
3090 strncpy(filename, newfile, 8);
3097 Newmenu_allowed_chars = demoname_allowed_chars;
3098 if (!Newdemo_no_space) {
3099 m[0].type=NM_TYPE_INPUT; m[0].text_len = 8; m[0].text = filename;
3100 exit = newmenu_do( NULL, TXT_SAVE_DEMO_AS, 1, &(m[0]), NULL );
3101 } else if (Newdemo_no_space == 1) {
3102 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_BAD;
3103 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3104 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3105 } else if (Newdemo_no_space == 2) {
3106 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_NOSPACE;
3107 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3108 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3110 Newmenu_allowed_chars = NULL;
3112 if (exit == -2) { // got bumped out from network menu
3113 char save_file[7+FILENAME_LEN];
3115 if (filename[0] != '\0') {
3116 strcpy(save_file, DEMO_DIR);
3117 strcat(save_file, filename);
3118 strcat(save_file, ".dem");
3120 sprintf (save_file, "%stmp%d.dem", DEMO_DIR, tmpcnt++);
3122 PHYSFSX_rename(DEMO_FILENAME, save_file);
3125 if (exit == -1) { // pressed ESC
3126 PHYSFS_delete(DEMO_FILENAME); // might as well remove the file
3127 return; // return without doing anything
3130 if (filename[0]==0) //null string
3133 //check to make sure name is ok
3134 for (s=filename;*s;s++)
3135 if (!isalnum(*s) && *s!='_') {
3136 nm_messagebox1(NULL, NULL,1,TXT_CONTINUE, TXT_DEMO_USE_LETTERS);
3140 if (Newdemo_no_space)
3141 strcat(fullname, m[1].text);
3143 strcat(fullname, m[0].text);
3144 strcat(fullname, ".dem");
3145 PHYSFS_delete(fullname);
3146 PHYSFSX_rename(DEMO_FILENAME, fullname);
3150 extern char AltHogDir[64];
3151 extern char AltHogdir_initialized;
3153 //returns the number of demo files on the disk
3154 int newdemo_count_demos()
3159 find = PHYSFS_enumerateFiles(DEMO_DIR);
3161 for (i = find; *i != NULL; i++)
3164 PHYSFS_freeList(find);
3169 void newdemo_start_playback(char * filename)
3171 char **find = NULL, **i;
3173 char filename2[PATH_MAX+FILENAME_LEN] = DEMO_DIR;
3176 change_playernum_to(0);
3178 First_time_playback=1;
3179 JasonPlaybackTotal=0;
3182 strcat(filename2, filename);
3185 // Randomly pick a filename
3186 int NumFiles = 0, RandFileNum;
3189 NumFiles = newdemo_count_demos();
3191 if ( NumFiles == 0 ) {
3192 return; // No files found!
3194 RandFileNum = d_rand() % NumFiles;
3197 find = PHYSFS_enumerateFiles(DEMO_DIR);
3199 for (i = find; *i != NULL; i++)
3201 if (NumFiles == RandFileNum)
3203 strcat(filename2, *i);
3209 PHYSFS_freeList(find);
3211 if (NumFiles > RandFileNum)
3215 infile = PHYSFSX_openReadBuffered(filename2);
3218 mprintf( (0, "Error reading '%s'\n", filename ));
3224 change_playernum_to(0); // force playernum to 0
3226 strncpy(nd_save_callsign, Players[Player_num].callsign, CALLSIGN_LEN);
3227 Viewer = ConsoleObject = &Objects[0]; // play properly as if console player
3228 if (newdemo_read_demo_start(rnd_demo)) {
3229 PHYSFS_close(infile);
3233 Game_mode = GM_NORMAL;
3234 Newdemo_state = ND_STATE_PLAYBACK;
3235 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3236 Newdemo_old_cockpit = Cockpit_mode.intval;
3237 Newdemo_size = (unsigned int)PHYSFS_fileLength(infile);
3240 NewdemoFrameCount = 0;
3241 Newdemo_players_cloaked = 0;
3242 playback_style = NORMAL_PLAYBACK;
3243 Function_mode = FMODE_GAME;
3244 cvar_setint(&Cockpit_3d_view[0], CV_NONE); // turn off 3d views on cockpit
3245 cvar_setint(&Cockpit_3d_view[1], CV_NONE); // turn off 3d views on cockpit
3246 newdemo_playback_one_frame(); // this one loads new level
3247 newdemo_playback_one_frame(); // get all of the objects to renderb game
3250 void newdemo_stop_playback()
3252 PHYSFS_close(infile);
3253 Newdemo_state = ND_STATE_NORMAL;
3255 change_playernum_to(0); //this is reality
3257 strncpy(Players[Player_num].callsign, nd_save_callsign, CALLSIGN_LEN);
3258 cvar_setint(&Cockpit_mode, Newdemo_old_cockpit);
3259 Game_mode = GM_GAME_OVER;
3260 Function_mode = FMODE_MENU;
3261 longjmp(LeaveGame,0); // Exit game loop
3267 #define BUF_SIZE 16384
3269 void newdemo_strip_frames(char *outname, int bytes_to_strip)
3271 PHYSFS_file *outfile;
3273 int total_size, bytes_done, read_elems, bytes_back;
3274 int trailer_start, loc1, loc2, stop_loc, bytes_to_read;
3275 short last_frame_length;
3278 total_size = (int)PHYSFS_fileLength(infile);
3279 outfile = PHYSFSX_openWriteBuffered(outname);
3280 if (outfile == NULL) {
3283 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't open output file";
3284 newmenu_do( NULL, NULL, 1, m, NULL );
3285 newdemo_stop_playback();
3288 buf = d_malloc(BUF_SIZE);
3292 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't malloc output buffer";
3293 newmenu_do( NULL, NULL, 1, m, NULL );
3294 PHYSFS_close(outfile);
3295 newdemo_stop_playback();
3299 trailer_start = (int)PHYSFS_tell(infile);
3300 PHYSFS_seek(infile, PHYSFS_tell(infile) + 11);
3302 while (bytes_back < bytes_to_strip) {
3303 loc1 = (int)PHYSFS_tell(infile);
3304 //PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
3305 //nd_read_short(&last_frame_length);
3306 //PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
3307 newdemo_back_frames(1);
3308 loc2 = (int)PHYSFS_tell(infile);
3309 bytes_back += (loc1 - loc2);
3311 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
3312 nd_read_short(&last_frame_length);
3313 PHYSFS_seek(infile, PHYSFS_tell(infile) - 3);
3314 stop_loc = (int)PHYSFS_tell(infile);
3315 PHYSFS_seek(infile, 0);
3316 while (stop_loc > 0) {
3317 if (stop_loc < BUF_SIZE)
3318 bytes_to_read = stop_loc;
3320 bytes_to_read = BUF_SIZE;
3321 read_elems = (int)PHYSFS_read(infile, buf, 1, bytes_to_read);
3322 PHYSFS_write(outfile, buf, 1, read_elems);
3323 stop_loc -= read_elems;
3325 stop_loc = (int)PHYSFS_tell(outfile);
3326 PHYSFS_seek(infile, trailer_start);
3327 while ((read_elems = (int)PHYSFS_read(infile, buf, 1, BUF_SIZE)) != 0)
3328 PHYSFS_write(outfile, buf, 1, read_elems);
3329 PHYSFS_seek(outfile, stop_loc);
3330 PHYSFS_seek(outfile, PHYSFS_tell(infile) + 1);
3331 PHYSFS_write(outfile, &last_frame_length, 2, 1);
3332 PHYSFS_close(outfile);
3333 newdemo_stop_playback();
3339 object DemoRightExtra,DemoLeftExtra;
3340 ubyte DemoDoRight=0,DemoDoLeft=0;
3342 void nd_render_extras (ubyte which,object *obj)
3345 ubyte type=which&15;
3349 Int3(); // how'd we get here?
3350 do_cockpit_window_view(w,NULL,0,WBU_WEAPON,NULL);
3356 memcpy (&DemoRightExtra,obj,sizeof(object)); DemoDoRight=type;
3360 memcpy (&DemoLeftExtra,obj,sizeof(object)); DemoDoLeft=type;
3365 void DoJasonInterpolate (fix recorded_time)
3369 JasonPlaybackTotal+=FrameTime;
3371 if (!First_time_playback)
3373 // get the difference between the recorded time and the playback time
3374 the_delay=(recorded_time - FrameTime);
3375 //mprintf ((0,"The delay=%d\n", f2i(the_delay)));
3376 if (the_delay >= f0_0)
3379 timer_delay(the_delay);
3384 while (JasonPlaybackTotal > nd_recorded_total)
3385 if (newdemo_read_frame_information() == -1)
3387 newdemo_stop_playback();
3391 //the_delay = nd_recorded_total - JasonPlaybackTotal;
3392 //if (the_delay > f0_0)
3393 // timer_delay(the_delay);
3398 First_time_playback=0;
3402 #pragma global_optimizer reset