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.
21 #include <string.h> // for memset
23 #include <ctype.h> /* for isdigit */
86 #include "editor\editor.h"
90 #pragma global_optimizer off // pretty much sucks...need to look into this
93 void DoJasonInterpolate (fix recorded_time);
96 void mkdir (char*); /* no idea */
99 //#include "nocfile.h"
101 //Does demo start automatically?
104 byte WasRecorded [MAX_OBJECTS];
105 byte ViewWasRecorded[MAX_OBJECTS];
106 byte RenderingWasRecorded[32];
108 #define ND_EVENT_EOF 0 // EOF
109 #define ND_EVENT_START_DEMO 1 // Followed by 16 character, NULL terminated filename of .SAV file to use
110 #define ND_EVENT_START_FRAME 2 // Followed by integer frame number, then a fix FrameTime
111 #define ND_EVENT_VIEWER_OBJECT 3 // Followed by an object structure
112 #define ND_EVENT_RENDER_OBJECT 4 // Followed by an object structure
113 #define ND_EVENT_SOUND 5 // Followed by int soundum
114 #define ND_EVENT_SOUND_ONCE 6 // Followed by int soundum
115 #define ND_EVENT_SOUND_3D 7 // Followed by int soundum, int angle, int volume
116 #define ND_EVENT_WALL_HIT_PROCESS 8 // Followed by int segnum, int side, fix damage
117 #define ND_EVENT_TRIGGER 9 // Followed by int segnum, int side, int objnum
118 #define ND_EVENT_HOSTAGE_RESCUED 10 // Followed by int hostage_type
119 #define ND_EVENT_SOUND_3D_ONCE 11 // Followed by int soundum, int angle, int volume
120 #define ND_EVENT_MORPH_FRAME 12 // Followed by ? data
121 #define ND_EVENT_WALL_TOGGLE 13 // Followed by int seg, int side
122 #define ND_EVENT_HUD_MESSAGE 14 // Followed by char size, char * string (+null)
123 #define ND_EVENT_CONTROL_CENTER_DESTROYED 15 // Just a simple flag
124 #define ND_EVENT_PALETTE_EFFECT 16 // Followed by short r,g,b
125 #define ND_EVENT_PLAYER_ENERGY 17 // followed by byte energy
126 #define ND_EVENT_PLAYER_SHIELD 18 // followed by byte shields
127 #define ND_EVENT_PLAYER_FLAGS 19 // followed by player flags
128 #define ND_EVENT_PLAYER_WEAPON 20 // followed by weapon type and weapon number
129 #define ND_EVENT_EFFECT_BLOWUP 21 // followed by segment, side, and pnt
130 #define ND_EVENT_HOMING_DISTANCE 22 // followed by homing distance
131 #define ND_EVENT_LETTERBOX 23 // letterbox mode for death seq.
132 #define ND_EVENT_RESTORE_COCKPIT 24 // restore cockpit after death
133 #define ND_EVENT_REARVIEW 25 // going to rear view mode
134 #define ND_EVENT_WALL_SET_TMAP_NUM1 26 // Wall changed
135 #define ND_EVENT_WALL_SET_TMAP_NUM2 27 // Wall changed
136 #define ND_EVENT_NEW_LEVEL 28 // followed by level number
137 #define ND_EVENT_MULTI_CLOAK 29 // followed by player num
138 #define ND_EVENT_MULTI_DECLOAK 30 // followed by player num
139 #define ND_EVENT_RESTORE_REARVIEW 31 // restore cockpit after rearview mode
140 #define ND_EVENT_MULTI_DEATH 32 // with player number
141 #define ND_EVENT_MULTI_KILL 33 // with player number
142 #define ND_EVENT_MULTI_CONNECT 34 // with player number
143 #define ND_EVENT_MULTI_RECONNECT 35 // with player number
144 #define ND_EVENT_MULTI_DISCONNECT 36 // with player number
145 #define ND_EVENT_MULTI_SCORE 37 // playernum / score
146 #define ND_EVENT_PLAYER_SCORE 38 // followed by score
147 #define ND_EVENT_PRIMARY_AMMO 39 // with old/new ammo count
148 #define ND_EVENT_SECONDARY_AMMO 40 // with old/new ammo count
149 #define ND_EVENT_DOOR_OPENING 41 // with segment/side
150 #define ND_EVENT_LASER_LEVEL 42 // no data
151 #define ND_EVENT_PLAYER_AFTERBURNER 43 // followed by byte old ab, current ab
152 #define ND_EVENT_CLOAKING_WALL 44 // info changing while wall cloaking
153 #define ND_EVENT_CHANGE_COCKPIT 45 // change the cockpit
154 #define ND_EVENT_START_GUIDED 46 // switch to guided view
155 #define ND_EVENT_END_GUIDED 47 // stop guided view/return to ship
156 #define ND_EVENT_SECRET_THINGY 48 // 0/1 = secret exit functional/non-functional
157 #define ND_EVENT_LINK_SOUND_TO_OBJ 49 // record digi_link_sound_to_object3
158 #define ND_EVENT_KILL_SOUND_TO_OBJ 50 // record digi_kill_sound_linked_to_object
161 #define NORMAL_PLAYBACK 0
162 #define SKIP_PLAYBACK 1
163 #define INTERPOLATE_PLAYBACK 2
164 #define INTERPOL_FACTOR (F1_0 + (F1_0/5))
166 #define DEMO_VERSION 15 //last D1 version was 13
167 #define DEMO_GAME_TYPE 3 //1 was shareware, 2 registered
170 #define DEMO_FILENAME "demos/tmpdemo.dem"
171 #define DEMO_DIR "demos/"
173 #define DEMO_FILENAME ":Demos:tmpdemo.dem"
174 #define DEMO_DIR ":Demos:"
177 #define DEMO_MAX_LEVELS 29
180 char nd_save_callsign[CALLSIGN_LEN+1];
181 int Newdemo_state = 0;
182 int Newdemo_vcr_state = 0;
183 int Newdemo_start_frame = -1;
184 unsigned int Newdemo_size;
185 int Newdemo_num_written;
186 int Newdemo_game_mode;
187 int Newdemo_old_cockpit;
188 byte Newdemo_no_space;
190 byte Newdemo_do_interpolate = 0; // 1
191 byte Newdemo_players_cloaked;
192 byte Newdemo_warning_given = 0;
193 byte Newdemo_cntrlcen_destroyed = 0;
194 static byte nd_bad_read;
195 int NewdemoFrameCount;
196 short frame_bytes_written = 0;
197 fix nd_playback_total;
198 fix nd_recorded_total;
199 fix nd_recorded_time;
201 byte First_time_playback=1;
202 fix JasonPlaybackTotal=0;
208 int newdemo_get_percent_done() {
209 if ( Newdemo_state == ND_STATE_PLAYBACK ) {
210 return (ftell(infile)*100)/Newdemo_size;
212 if ( Newdemo_state == ND_STATE_RECORDING ) {
213 return ftell(outfile);
218 #define VEL_PRECISION 12
220 void my_extract_shortpos(object *objp, shortpos *spp)
226 objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
227 objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
228 objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
230 objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
231 objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
232 objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
234 objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
235 objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
236 objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
238 segnum = spp->segment;
239 objp->segnum = segnum;
241 objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x;
242 objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y;
243 objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z;
245 objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION);
246 objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION);
247 objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION);
250 int newdemo_read( void *buffer, int elsize, int nelem )
253 num_read = fread( buffer,elsize,nelem, infile );
254 if (ferror(infile) || feof(infile))
260 int newdemo_find_object( int signature )
265 for (i=0; i<=Highest_object_index; i++, objp++ ) {
266 if ( (objp->type != OBJ_NONE) && (objp->signature == signature))
272 int newdemo_write( void *buffer, int elsize, int nelem )
274 int num_written, total_size;
276 total_size = elsize * nelem;
277 frame_bytes_written += total_size;
278 Newdemo_num_written += total_size;
279 Assert(outfile != NULL);
280 num_written = fwrite( buffer, elsize, nelem, outfile );
281 // if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) {
282 // Newdemo_no_space=1;
283 // newdemo_stop_recording();
286 if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space)
288 if (num_written == nelem && !Newdemo_no_space)
292 newdemo_stop_recording();
297 * The next bunch of files taken from Matt's gamesave.c. We have to modify
298 * these since the demo must save more information about objects that
302 static void nd_write_byte(byte b)
304 newdemo_write(&b, 1, 1);
307 static void nd_write_short(short s)
309 newdemo_write(&s, 2, 1);
312 static void nd_write_int(int i)
314 newdemo_write(&i, 4, 1);
317 static void nd_write_string(char *str)
319 nd_write_byte(strlen(str) + 1);
320 newdemo_write(str, strlen(str) + 1, 1);
323 static void nd_write_fix(fix f)
325 newdemo_write(&f, sizeof(fix), 1);
328 static void nd_write_fixang(fixang f)
330 newdemo_write(&f, sizeof(fixang), 1);
333 static void nd_write_vector(vms_vector *v)
340 static void nd_write_angvec(vms_angvec *v)
342 nd_write_fixang(v->p);
343 nd_write_fixang(v->b);
344 nd_write_fixang(v->h);
347 void nd_write_shortpos(object *obj)
353 create_shortpos(&sp, obj, 0);
355 render_type = obj->render_type;
356 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
357 for (i = 0; i < 9; i++)
358 nd_write_byte(sp.bytemat[i]);
359 for (i = 0; i < 9; i++) {
360 if (sp.bytemat[i] != 0)
364 Int3(); // contact Allender about this.
368 nd_write_short(sp.xo);
369 nd_write_short(sp.yo);
370 nd_write_short(sp.zo);
371 nd_write_short(sp.segment);
372 nd_write_short(sp.velx);
373 nd_write_short(sp.vely);
374 nd_write_short(sp.velz);
377 static void nd_read_byte(byte *b)
379 newdemo_read(b, 1, 1);
382 static void nd_read_short(short *s)
384 newdemo_read(s, 2, 1);
387 static void nd_read_int(int *i)
389 newdemo_read(i, 4, 1);
392 static void nd_read_string(char *str)
397 newdemo_read(str, len, 1);
400 static void nd_read_fix(fix *f)
402 newdemo_read(f, sizeof(fix), 1);
405 static void nd_read_fixang(fixang *f)
407 newdemo_read(f, sizeof(fixang), 1);
410 static void nd_read_vector(vms_vector *v)
412 nd_read_fix(&(v->x));
413 nd_read_fix(&(v->y));
414 nd_read_fix(&(v->z));
417 static void nd_read_angvec(vms_angvec *v)
419 nd_read_fixang(&(v->p));
420 nd_read_fixang(&(v->b));
421 nd_read_fixang(&(v->h));
424 static void nd_read_shortpos(object *obj)
430 render_type = obj->render_type;
431 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
432 for (i = 0; i < 9; i++)
433 nd_read_byte(&(sp.bytemat[i]));
436 nd_read_short(&(sp.xo));
437 nd_read_short(&(sp.yo));
438 nd_read_short(&(sp.zo));
439 nd_read_short(&(sp.segment));
440 nd_read_short(&(sp.velx));
441 nd_read_short(&(sp.vely));
442 nd_read_short(&(sp.velz));
444 my_extract_shortpos(obj, &sp);
445 if ((obj->id == VCLIP_MORPHING_ROBOT) && (render_type == RT_FIREBALL) && (obj->control_type == CT_EXPLOSION))
446 extract_orient_from_segment(&obj->orient,&Segments[obj->segnum]);
450 object *prev_obj=NULL; //ptr to last object read in
452 void nd_read_object(object *obj)
454 memset(obj, 0, sizeof(object));
457 * Do render type first, since with render_type == RT_NONE, we
458 * blow by all other object information
460 nd_read_byte(&(obj->render_type));
461 nd_read_byte(&(obj->type));
462 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
465 nd_read_byte(&(obj->id));
466 nd_read_byte(&(obj->flags));
467 nd_read_short((short *)&(obj->signature));
468 nd_read_shortpos(obj);
470 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
473 obj->attached_obj = -1;
478 obj->control_type = CT_POWERUP;
479 obj->movement_type = MT_NONE;
480 obj->size = HOSTAGE_SIZE;
484 obj->control_type = CT_AI;
485 // (MarkA and MikeK said we should not do the crazy last secret stuff with multiple reactors...
486 // This necessary code is our vindication. --MK, 2/15/96)
487 if (obj->id != SPECIAL_REACTOR_ROBOT)
488 obj->movement_type = MT_PHYSICS;
490 obj->movement_type = MT_NONE;
491 obj->size = Polygon_models[Robot_info[obj->id].model_num].rad;
492 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
493 obj->rtype.pobj_info.subobj_flags = 0;
494 obj->ctype.ai_info.CLOAKED = (Robot_info[obj->id].cloak_type?1:0);
498 obj->control_type = CT_POWERUP;
499 nd_read_byte(&(obj->movement_type)); // might have physics movement
500 obj->size = Powerup_info[obj->id].size;
504 obj->control_type = CT_NONE;
505 obj->movement_type = MT_PHYSICS;
506 obj->size = Polygon_models[Player_ship->model_num].rad;
507 obj->rtype.pobj_info.model_num = Player_ship->model_num;
508 obj->rtype.pobj_info.subobj_flags = 0;
512 obj->control_type = CT_NONE;
513 obj->movement_type = MT_NONE;
514 obj->size = Polygon_models[obj->id].rad;
515 obj->rtype.pobj_info.model_num = obj->id;
516 obj->rtype.pobj_info.subobj_flags = 0;
520 nd_read_byte(&(obj->control_type));
521 nd_read_byte(&(obj->movement_type));
522 nd_read_fix(&(obj->size));
527 nd_read_vector(&(obj->last_pos));
528 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
529 nd_read_fix(&(obj->lifeleft));
534 obj->lifeleft = (fix)b;
535 // MWA old way -- won't work with big endian machines nd_read_byte((ubyte *)&(obj->lifeleft));
536 obj->lifeleft = (fix)((int)obj->lifeleft << 12);
539 if (obj->type == OBJ_ROBOT) {
540 if (Robot_info[obj->id].boss_flag) {
543 nd_read_byte(&cloaked);
544 obj->ctype.ai_info.CLOAKED = cloaked;
548 switch (obj->movement_type) {
551 nd_read_vector(&(obj->mtype.phys_info.velocity));
552 nd_read_vector(&(obj->mtype.phys_info.thrust));
556 nd_read_vector(&(obj->mtype.spin_rate));
566 switch (obj->control_type) {
570 nd_read_fix(&(obj->ctype.expl_info.spawn_time));
571 nd_read_fix(&(obj->ctype.expl_info.delete_time));
572 nd_read_short(&(obj->ctype.expl_info.delete_objnum));
574 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
576 if (obj->flags & OF_ATTACHED) { //attach to previous object
577 Assert(prev_obj!=NULL);
578 if (prev_obj->control_type == CT_EXPLOSION) {
579 if (prev_obj->flags & OF_ATTACHED && prev_obj->ctype.expl_info.attach_parent!=-1)
580 obj_attach(&Objects[prev_obj->ctype.expl_info.attach_parent],obj);
582 obj->flags &= ~OF_ATTACHED;
585 obj_attach(prev_obj,obj);
591 nd_read_fix(&(obj->ctype.light_info.intensity));
613 switch (obj->render_type) {
622 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
623 nd_read_int(&(obj->rtype.pobj_info.model_num));
624 nd_read_int(&(obj->rtype.pobj_info.subobj_flags));
627 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
629 for (i=0;i<MAX_SUBMODELS;i++)
630 nd_read_angvec(&(obj->pobj_info.anim_angles[i]));
632 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
633 nd_read_angvec(&obj->rtype.pobj_info.anim_angles[i]);
638 obj->rtype.pobj_info.tmap_override = tmo;
641 obj->rtype.pobj_info.tmap_override = -1;
643 int xlated_tmo = tmap_xlate_table[tmo];
644 if (xlated_tmo < 0) {
645 // mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->pobj_info.model_num));
649 obj->rtype.pobj_info.tmap_override = xlated_tmo;
657 case RT_WEAPON_VCLIP:
660 nd_read_int(&(obj->rtype.vclip_info.vclip_num));
661 nd_read_fix(&(obj->rtype.vclip_info.frametime));
662 nd_read_byte(&(obj->rtype.vclip_info.framenum));
676 void nd_write_object(object *obj)
680 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
684 * Do render_type first so on read, we can make determination of
685 * what else to read in
687 nd_write_byte(obj->render_type);
688 nd_write_byte(obj->type);
689 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
692 nd_write_byte(obj->id);
693 nd_write_byte(obj->flags);
694 nd_write_short((short)obj->signature);
695 nd_write_shortpos(obj);
697 if ((obj->type != OBJ_HOSTAGE) && (obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_POWERUP) && (obj->type != OBJ_CLUTTER)) {
698 nd_write_byte(obj->control_type);
699 nd_write_byte(obj->movement_type);
700 nd_write_fix(obj->size);
702 if (obj->type == OBJ_POWERUP)
703 nd_write_byte(obj->movement_type);
705 nd_write_vector(&obj->last_pos);
707 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
708 nd_write_fix(obj->lifeleft);
710 life = (int)obj->lifeleft;
714 nd_write_byte((ubyte)life);
717 if (obj->type == OBJ_ROBOT) {
718 if (Robot_info[obj->id].boss_flag) {
719 if ((GameTime > Boss_cloak_start_time) && (GameTime < Boss_cloak_end_time))
726 switch (obj->movement_type) {
729 nd_write_vector(&obj->mtype.phys_info.velocity);
730 nd_write_vector(&obj->mtype.phys_info.thrust);
734 nd_write_vector(&obj->mtype.spin_rate);
744 switch (obj->control_type) {
750 nd_write_fix(obj->ctype.expl_info.spawn_time);
751 nd_write_fix(obj->ctype.expl_info.delete_time);
752 nd_write_short(obj->ctype.expl_info.delete_objnum);
760 nd_write_fix(obj->ctype.light_info.intensity);
767 case CT_SLEW: //the player is generally saved as slew
780 switch (obj->render_type) {
789 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
790 nd_write_int(obj->rtype.pobj_info.model_num);
791 nd_write_int(obj->rtype.pobj_info.subobj_flags);
794 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
796 for (i=0;i<MAX_SUBMODELS;i++)
797 nd_write_angvec(&obj->pobj_info.anim_angles[i]);
799 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
800 nd_write_angvec(&obj->rtype.pobj_info.anim_angles[i]);
802 nd_write_int(obj->rtype.pobj_info.tmap_override);
808 case RT_WEAPON_VCLIP:
811 nd_write_int(obj->rtype.vclip_info.vclip_num);
812 nd_write_fix(obj->rtype.vclip_info.frametime);
813 nd_write_byte(obj->rtype.vclip_info.framenum);
826 int JustStartedRecording=0,JustStartedPlayback=0;
828 void newdemo_record_start_demo()
833 nd_write_byte(ND_EVENT_START_DEMO);
834 nd_write_byte(DEMO_VERSION);
835 nd_write_byte(DEMO_GAME_TYPE);
836 nd_write_fix(GameTime);
839 if (Game_mode & GM_MULTI)
840 nd_write_int(Game_mode | (Player_num << 16));
843 // NOTE LINK TO ABOVE!!!
844 nd_write_int(Game_mode);
847 if (Game_mode & GM_TEAM) {
848 nd_write_byte(Netgame.team_vector);
849 nd_write_string(Netgame.team_name[0]);
850 nd_write_string(Netgame.team_name[1]);
853 if (Game_mode & GM_MULTI) {
854 nd_write_byte((byte)N_players);
855 for (i = 0; i < N_players; i++) {
856 nd_write_string(Players[i].callsign);
857 nd_write_byte(Players[i].connected);
859 if (Game_mode & GM_MULTI_COOP) {
860 nd_write_int(Players[i].score);
862 nd_write_short((short)Players[i].net_killed_total);
863 nd_write_short((short)Players[i].net_kills_total);
868 // NOTE LINK TO ABOVE!!!
869 nd_write_int(Players[Player_num].score);
871 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
872 nd_write_short((short)Players[Player_num].primary_ammo[i]);
874 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
875 nd_write_short((short)Players[Player_num].secondary_ammo[i]);
877 nd_write_byte((byte)Players[Player_num].laser_level);
879 // Support for missions added here
881 nd_write_string(Current_mission_filename);
883 nd_write_byte((byte)(f2ir(Players[Player_num].energy)));
884 nd_write_byte((byte)(f2ir(Players[Player_num].shields)));
885 nd_write_int(Players[Player_num].flags); // be sure players flags are set
886 nd_write_byte((byte)Primary_weapon);
887 nd_write_byte((byte)Secondary_weapon);
888 Newdemo_start_frame = FrameCount;
889 JustStartedRecording=1;
891 newdemo_set_new_level(Current_level_num);
896 void newdemo_record_start_frame(int frame_number, fix frame_time )
900 if (Newdemo_no_space) {
901 newdemo_stop_playback();
908 for (i=0;i<MAX_OBJECTS;i++)
911 ViewWasRecorded[i]=0;
914 RenderingWasRecorded[i]=0;
916 frame_number -= Newdemo_start_frame;
918 Assert(frame_number >= 0 );
920 nd_write_byte(ND_EVENT_START_FRAME);
921 nd_write_short(frame_bytes_written - 1); // from previous frame
922 frame_bytes_written=3;
923 nd_write_int(frame_number);
924 nd_write_int(frame_time);
929 void newdemo_record_render_object(object * obj)
931 if (ViewWasRecorded[obj-Objects])
934 // if (obj==&Objects[Players[Player_num].objnum] && !Player_is_dead)
938 nd_write_byte(ND_EVENT_RENDER_OBJECT);
939 nd_write_object(obj);
943 extern ubyte RenderingType;
945 void newdemo_record_viewer_object(object * obj)
948 if (ViewWasRecorded[obj-Objects] && (ViewWasRecorded[obj-Objects]-1)==RenderingType)
950 //if (WasRecorded[obj-Objects])
952 if (RenderingWasRecorded[RenderingType])
955 ViewWasRecorded[obj-Objects]=RenderingType+1;
956 RenderingWasRecorded[RenderingType]=1;
958 nd_write_byte(ND_EVENT_VIEWER_OBJECT);
959 nd_write_byte(RenderingType);
960 nd_write_object(obj);
964 void newdemo_record_sound( int soundno ) {
966 nd_write_byte(ND_EVENT_SOUND);
967 nd_write_int( soundno );
970 //--unused-- void newdemo_record_sound_once( int soundno ) {
971 //--unused-- stop_time();
972 //--unused-- nd_write_byte( ND_EVENT_SOUND_ONCE );
973 //--unused-- nd_write_int( soundno );
974 //--unused-- start_time();
978 void newdemo_record_cockpit_change (int mode)
981 nd_write_byte (ND_EVENT_CHANGE_COCKPIT);
987 void newdemo_record_sound_3d( int soundno, int angle, int volume ) {
989 nd_write_byte( ND_EVENT_SOUND_3D );
990 nd_write_int( soundno );
991 nd_write_int( angle );
992 nd_write_int( volume );
996 void newdemo_record_sound_3d_once( int soundno, int angle, int volume ) {
998 nd_write_byte( ND_EVENT_SOUND_3D_ONCE );
999 nd_write_int( soundno );
1000 nd_write_int( angle );
1001 nd_write_int( volume );
1006 void newdemo_record_link_sound_to_object3( int soundno, short objnum, fix max_volume, fix max_distance, int loop_start, int loop_end )
1009 nd_write_byte( ND_EVENT_LINK_SOUND_TO_OBJ );
1010 nd_write_int( soundno );
1011 nd_write_int( Objects[objnum].signature );
1012 nd_write_int( max_volume );
1013 nd_write_int( max_distance );
1014 nd_write_int( loop_start );
1015 nd_write_int( loop_end );
1019 void newdemo_record_kill_sound_linked_to_object( int objnum )
1022 nd_write_byte( ND_EVENT_KILL_SOUND_TO_OBJ );
1023 nd_write_int( Objects[objnum].signature );
1028 void newdemo_record_wall_hit_process( int segnum, int side, int damage, int playernum )
1034 // playernum = playernum;
1035 nd_write_byte( ND_EVENT_WALL_HIT_PROCESS );
1036 nd_write_int( segnum );
1037 nd_write_int( side );
1038 nd_write_int( damage );
1039 nd_write_int( playernum );
1043 void newdemo_record_guided_start ()
1045 nd_write_byte (ND_EVENT_START_GUIDED);
1047 void newdemo_record_guided_end ()
1049 nd_write_byte (ND_EVENT_END_GUIDED);
1052 void newdemo_record_secret_exit_blown(int truth)
1055 nd_write_byte( ND_EVENT_SECRET_THINGY );
1056 nd_write_int( truth );
1060 void newdemo_record_trigger( int segnum, int side, int objnum,int shot )
1063 nd_write_byte( ND_EVENT_TRIGGER );
1064 nd_write_int( segnum );
1065 nd_write_int( side );
1066 nd_write_int( objnum );
1071 void newdemo_record_hostage_rescued( int hostage_number ) {
1073 nd_write_byte( ND_EVENT_HOSTAGE_RESCUED );
1074 nd_write_int( hostage_number );
1078 void newdemo_record_morph_frame(morph_data *md) {
1081 nd_write_byte( ND_EVENT_MORPH_FRAME );
1083 newdemo_write( md->morph_vecs, sizeof(md->morph_vecs), 1 );
1084 newdemo_write( md->submodel_active, sizeof(md->submodel_active), 1 );
1085 newdemo_write( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 );
1087 nd_write_object( md->obj );
1091 void newdemo_record_wall_toggle( int segnum, int side ) {
1093 nd_write_byte( ND_EVENT_WALL_TOGGLE );
1094 nd_write_int( segnum );
1095 nd_write_int( side );
1099 void newdemo_record_control_center_destroyed()
1102 nd_write_byte( ND_EVENT_CONTROL_CENTER_DESTROYED );
1103 nd_write_int( Countdown_seconds_left );
1107 void newdemo_record_hud_message( char * message )
1110 nd_write_byte( ND_EVENT_HUD_MESSAGE );
1111 nd_write_string(message);
1115 void newdemo_record_palette_effect(short r, short g, short b )
1118 nd_write_byte( ND_EVENT_PALETTE_EFFECT );
1119 nd_write_short( r );
1120 nd_write_short( g );
1121 nd_write_short( b );
1125 void newdemo_record_player_energy(int old_energy, int energy)
1128 nd_write_byte( ND_EVENT_PLAYER_ENERGY );
1129 nd_write_byte((byte) old_energy);
1130 nd_write_byte((byte) energy);
1134 void newdemo_record_player_afterburner(fix old_afterburner, fix afterburner)
1137 nd_write_byte( ND_EVENT_PLAYER_AFTERBURNER );
1138 nd_write_byte((byte) (old_afterburner>>9));
1139 nd_write_byte((byte) (afterburner>>9));
1143 void newdemo_record_player_shields(int old_shield, int shield)
1146 nd_write_byte( ND_EVENT_PLAYER_SHIELD );
1147 nd_write_byte((byte)old_shield);
1148 nd_write_byte((byte)shield);
1152 void newdemo_record_player_flags(uint oflags, uint flags)
1155 nd_write_byte( ND_EVENT_PLAYER_FLAGS );
1156 nd_write_int(((short)oflags << 16) | (short)flags);
1160 void newdemo_record_player_weapon(int weapon_type, int weapon_num)
1163 nd_write_byte( ND_EVENT_PLAYER_WEAPON );
1164 nd_write_byte((byte)weapon_type);
1165 nd_write_byte((byte)weapon_num);
1167 nd_write_byte((byte)Secondary_weapon);
1169 nd_write_byte((byte)Primary_weapon);
1173 void newdemo_record_effect_blowup(short segment, int side, vms_vector *pnt)
1176 nd_write_byte (ND_EVENT_EFFECT_BLOWUP);
1177 nd_write_short(segment);
1178 nd_write_byte((byte)side);
1179 nd_write_vector(pnt);
1183 void newdemo_record_homing_distance(fix distance)
1186 nd_write_byte(ND_EVENT_HOMING_DISTANCE);
1187 nd_write_short((short)(distance>>16));
1191 void newdemo_record_letterbox(void)
1194 nd_write_byte(ND_EVENT_LETTERBOX);
1198 void newdemo_record_rearview(void)
1201 nd_write_byte(ND_EVENT_REARVIEW);
1205 void newdemo_record_restore_cockpit(void)
1208 nd_write_byte(ND_EVENT_RESTORE_COCKPIT);
1212 void newdemo_record_restore_rearview(void)
1215 nd_write_byte(ND_EVENT_RESTORE_REARVIEW);
1219 void newdemo_record_wall_set_tmap_num1(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1222 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM1);
1223 nd_write_short(seg);
1224 nd_write_byte(side);
1225 nd_write_short(cseg);
1226 nd_write_byte(cside);
1227 nd_write_short(tmap);
1231 void newdemo_record_wall_set_tmap_num2(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1234 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM2);
1235 nd_write_short(seg);
1236 nd_write_byte(side);
1237 nd_write_short(cseg);
1238 nd_write_byte(cside);
1239 nd_write_short(tmap);
1243 void newdemo_record_multi_cloak(int pnum)
1246 nd_write_byte(ND_EVENT_MULTI_CLOAK);
1247 nd_write_byte((byte)pnum);
1251 void newdemo_record_multi_decloak(int pnum)
1254 nd_write_byte(ND_EVENT_MULTI_DECLOAK);
1255 nd_write_byte((byte)pnum);
1259 void newdemo_record_multi_death(int pnum)
1262 nd_write_byte(ND_EVENT_MULTI_DEATH);
1263 nd_write_byte((byte)pnum);
1267 void newdemo_record_multi_kill(int pnum, byte kill)
1270 nd_write_byte(ND_EVENT_MULTI_KILL);
1271 nd_write_byte((byte)pnum);
1272 nd_write_byte(kill);
1276 void newdemo_record_multi_connect(int pnum, int new_player, char *new_callsign)
1279 nd_write_byte(ND_EVENT_MULTI_CONNECT);
1280 nd_write_byte((byte)pnum);
1281 nd_write_byte((byte)new_player);
1283 nd_write_string(Players[pnum].callsign);
1284 nd_write_int(Players[pnum].net_killed_total);
1285 nd_write_int(Players[pnum].net_kills_total);
1287 nd_write_string(new_callsign);
1291 void newdemo_record_multi_reconnect(int pnum)
1294 nd_write_byte(ND_EVENT_MULTI_RECONNECT);
1295 nd_write_byte((byte)pnum);
1299 void newdemo_record_multi_disconnect(int pnum)
1302 nd_write_byte(ND_EVENT_MULTI_DISCONNECT);
1303 nd_write_byte((byte)pnum);
1307 void newdemo_record_player_score(int score)
1310 nd_write_byte(ND_EVENT_PLAYER_SCORE);
1311 nd_write_int(score);
1315 void newdemo_record_multi_score(int pnum, int score)
1318 nd_write_byte(ND_EVENT_MULTI_SCORE);
1319 nd_write_byte((byte)pnum);
1320 nd_write_int(score - Players[pnum].score); // called before score is changed!!!!
1324 void newdemo_record_primary_ammo(int old_ammo, int new_ammo)
1327 nd_write_byte(ND_EVENT_PRIMARY_AMMO);
1329 nd_write_short((short)new_ammo);
1331 nd_write_short((short)old_ammo);
1332 nd_write_short((short)new_ammo);
1336 void newdemo_record_secondary_ammo(int old_ammo, int new_ammo)
1339 nd_write_byte(ND_EVENT_SECONDARY_AMMO);
1341 nd_write_short((short)new_ammo);
1343 nd_write_short((short)old_ammo);
1344 nd_write_short((short)new_ammo);
1348 void newdemo_record_door_opening(int segnum, int side)
1351 nd_write_byte(ND_EVENT_DOOR_OPENING);
1352 nd_write_short((short)segnum);
1353 nd_write_byte((byte)side);
1357 void newdemo_record_laser_level(byte old_level, byte new_level)
1360 nd_write_byte(ND_EVENT_LASER_LEVEL);
1361 nd_write_byte(old_level);
1362 nd_write_byte(new_level);
1366 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)
1368 Assert(front_wall_num <= 255 && back_wall_num <= 255);
1371 nd_write_byte(ND_EVENT_CLOAKING_WALL);
1372 nd_write_byte(front_wall_num);
1373 nd_write_byte(back_wall_num);
1374 nd_write_byte(type);
1375 nd_write_byte(state);
1376 nd_write_byte(cloak_value);
1377 nd_write_short(l0>>8);
1378 nd_write_short(l1>>8);
1379 nd_write_short(l2>>8);
1380 nd_write_short(l3>>8);
1384 void newdemo_set_new_level(int level_num)
1391 nd_write_byte(ND_EVENT_NEW_LEVEL);
1392 nd_write_byte((byte)level_num);
1393 nd_write_byte((byte)Current_level_num);
1395 if (JustStartedRecording==1)
1397 nd_write_int(Num_walls);
1398 for (i=0;i<Num_walls;i++)
1400 nd_write_byte (Walls[i].type);
1401 nd_write_byte (Walls[i].flags);
1402 nd_write_byte (Walls[i].state);
1404 seg = &Segments[Walls[i].segnum];
1405 side = Walls[i].sidenum;
1406 nd_write_short (seg->sides[side].tmap_num);
1407 nd_write_short (seg->sides[side].tmap_num2);
1408 JustStartedRecording=0;
1415 int newdemo_read_demo_start(int rnd_demo)
1417 byte i, version, game_type, laser_level;
1418 char c, energy, shield;
1419 char text[50], current_mission[9];
1422 if ((c != ND_EVENT_START_DEMO) || nd_bad_read) {
1425 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_CORRUPT);
1426 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1427 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1430 nd_read_byte(&version);
1431 nd_read_byte(&game_type);
1432 if (game_type < DEMO_GAME_TYPE) {
1435 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1436 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1437 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Descent: First Strike";
1439 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1442 if (game_type != DEMO_GAME_TYPE) {
1445 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1446 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1447 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Unknown Descent version";
1449 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1452 if (version < DEMO_VERSION) {
1455 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_OLD);
1456 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1457 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1461 nd_read_fix(&GameTime);
1462 Boss_cloak_start_time=Boss_cloak_end_time=GameTime;
1463 JasonPlaybackTotal=0;
1465 nd_read_int(&Newdemo_game_mode);
1468 change_playernum_to((Newdemo_game_mode >> 16) & 0x7);
1469 if (Newdemo_game_mode & GM_TEAM) {
1470 nd_read_byte(&(Netgame.team_vector));
1471 nd_read_string(Netgame.team_name[0]);
1472 nd_read_string(Netgame.team_name[1]);
1474 if (Newdemo_game_mode & GM_MULTI) {
1479 // changed this to above two lines -- breaks on the mac because of
1481 // nd_read_byte((byte *)&N_players);
1482 for (i = 0 ; i < N_players; i++) {
1483 Players[i].cloak_time = 0;
1484 Players[i].invulnerable_time = 0;
1485 nd_read_string(Players[i].callsign);
1486 nd_read_byte(&(Players[i].connected));
1488 if (Newdemo_game_mode & GM_MULTI_COOP) {
1489 nd_read_int(&(Players[i].score));
1491 nd_read_short((short *)&(Players[i].net_killed_total));
1492 nd_read_short((short *)&(Players[i].net_kills_total));
1495 Game_mode = Newdemo_game_mode;
1496 multi_sort_kill_list();
1497 Game_mode = GM_NORMAL;
1500 nd_read_int(&(Players[Player_num].score)); // Note link to above if!
1502 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
1503 nd_read_short((short*)&(Players[Player_num].primary_ammo[i]));
1505 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
1506 nd_read_short((short*)&(Players[Player_num].secondary_ammo[i]));
1508 nd_read_byte(&laser_level);
1509 if (laser_level != Players[Player_num].laser_level) {
1510 Players[Player_num].laser_level = laser_level;
1511 update_laser_weapon_info();
1514 // Support for missions
1516 nd_read_string(current_mission);
1517 if (!load_mission_by_name(current_mission)) {
1521 sprintf(text, TXT_NOMISSION4DEMO, current_mission);
1522 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1523 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1528 nd_recorded_total = 0;
1529 nd_playback_total = 0;
1530 nd_read_byte(&energy);
1531 nd_read_byte(&shield);
1533 nd_read_int((int *)&(Players[Player_num].flags));
1534 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
1535 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1536 Newdemo_players_cloaked |= (1 << Player_num);
1538 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
1539 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1541 nd_read_byte((byte *)&Primary_weapon);
1542 nd_read_byte((byte *)&Secondary_weapon);
1544 // Next bit of code to fix problem that I introduced between 1.0 and 1.1
1545 // check the next byte -- it _will_ be a load_new_level event. If it is
1546 // not, then we must shift all bytes up by one.
1548 Players[Player_num].energy = i2f(energy);
1549 Players[Player_num].shields = i2f(shield);
1550 JustStartedPlayback=1;
1554 void newdemo_pop_ctrlcen_triggers()
1558 segment *seg, *csegp;
1560 for (i = 0; i < ControlCenterTriggers.num_links; i++) {
1561 seg = &Segments[ControlCenterTriggers.seg[i]];
1562 side = ControlCenterTriggers.side[i];
1563 csegp = &Segments[seg->children[side]];
1564 cside = find_connect_side(seg, csegp);
1565 anim_num = Walls[seg->sides[side].wall_num].clip_num;
1566 n = WallAnims[anim_num].num_frames;
1567 if (WallAnims[anim_num].flags & WCF_TMAP1) {
1568 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[n-1];
1570 seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[n-1];
1575 #define N_PLAYER_SHIP_TEXTURES 6
1577 void nd_render_extras (ubyte,object *);
1578 extern void multi_apply_goal_textures ();
1579 ubyte Newdemo_flying_guided=0;
1581 int newdemo_read_frame_information()
1583 int done, segnum, side, objnum, soundno, angle, volume, i,shot;
1585 ubyte c,WhichWindow;
1586 static byte saved_letter_cockpit;
1587 static byte saved_rearview_cockpit;
1589 static char LastReadValue=101;
1594 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1595 for (segnum=0; segnum <= Highest_segment_index; segnum++)
1596 Segments[segnum].objects = -1;
1599 Players[Player_num].homing_object_dist = -F1_0;
1605 if (nd_bad_read) { done = -1; break; }
1609 case ND_EVENT_START_FRAME: { // Followed by an integer frame number, then a fix FrameTime
1610 short last_frame_length;
1613 nd_read_short(&last_frame_length);
1614 nd_read_int(&NewdemoFrameCount);
1615 nd_read_int((int *)&nd_recorded_time);
1616 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1617 nd_recorded_total += nd_recorded_time;
1618 NewdemoFrameCount--;
1620 if (nd_bad_read) { done = -1; break; }
1624 case ND_EVENT_VIEWER_OBJECT: // Followed by an object structure
1625 nd_read_byte (&WhichWindow);
1628 // mprintf ((0,"Reading extra!\n"));
1629 nd_read_object (&extraobj);
1630 if (Newdemo_vcr_state!=ND_STATE_PAUSED)
1632 if (nd_bad_read) { done = -1; break; }
1634 nd_render_extras (WhichWindow,&extraobj);
1639 // mprintf ((0,"Reading viewer!\n"));
1640 //Viewer=&Objects[0];
1641 nd_read_object(Viewer);
1643 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1644 if (nd_bad_read) { done = -1; break; }
1645 segnum = Viewer->segnum;
1646 Viewer->next = Viewer->prev = Viewer->segnum = -1;
1648 // HACK HACK HACK -- since we have multiple level recording, it can be the case
1649 // HACK HACK HACK -- that when rewinding the demo, the viewer is in a segment
1650 // HACK HACK HACK -- that is greater than the highest index of segments. Bash
1651 // HACK HACK HACK -- the viewer to segment 0 for bogus view.
1653 if (segnum > Highest_segment_index)
1655 obj_link(Viewer-Objects,segnum);
1660 case ND_EVENT_RENDER_OBJECT: // Followed by an object structure
1661 objnum = obj_allocate();
1664 obj = &Objects[objnum];
1665 nd_read_object(obj);
1666 if (nd_bad_read) { done = -1; break; }
1667 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1668 segnum = obj->segnum;
1669 obj->next = obj->prev = obj->segnum = -1;
1671 // HACK HACK HACK -- don't render objects is segments greater than Highest_segment_index
1672 // HACK HACK HACK -- (see above)
1674 if (segnum > Highest_segment_index)
1677 obj_link(obj-Objects,segnum);
1679 if ((obj->type == OBJ_PLAYER) && (Newdemo_game_mode & GM_MULTI)) {
1682 if (Newdemo_game_mode & GM_TEAM)
1683 player = get_team(obj->id);
1690 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
1691 multi_player_textures[player][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[obj->rtype.pobj_info.model_num].first_texture+i]];
1693 multi_player_textures[player][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2]];
1694 multi_player_textures[player][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2+1]];
1695 obj->rtype.pobj_info.alt_textures = player+1;
1701 case ND_EVENT_SOUND:
1702 nd_read_int(&soundno);
1703 if (nd_bad_read) {done = -1; break; }
1704 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1705 digi_play_sample( soundno, F1_0 );
1708 //--unused case ND_EVENT_SOUND_ONCE:
1709 //--unused nd_read_int(&soundno);
1710 //--unused if (nd_bad_read) { done = -1; break; }
1711 //--unused if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1712 //--unused digi_play_sample_once( soundno, F1_0 );
1715 case ND_EVENT_SOUND_3D:
1716 nd_read_int(&soundno);
1717 nd_read_int(&angle);
1718 nd_read_int(&volume);
1719 if (nd_bad_read) { done = -1; break; }
1720 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1721 digi_play_sample_3d( soundno, angle, volume, 0 );
1724 case ND_EVENT_SOUND_3D_ONCE:
1725 nd_read_int(&soundno);
1726 nd_read_int(&angle);
1727 nd_read_int(&volume);
1728 if (nd_bad_read) { done = -1; break; }
1729 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1730 digi_play_sample_3d( soundno, angle, volume, 1 );
1733 case ND_EVENT_LINK_SOUND_TO_OBJ:
1735 int soundno, objnum, max_volume, max_distance, loop_start, loop_end;
1737 nd_read_int( &soundno );
1738 nd_read_int( &signature );
1739 nd_read_int( &max_volume );
1740 nd_read_int( &max_distance );
1741 nd_read_int( &loop_start );
1742 nd_read_int( &loop_end );
1743 objnum = newdemo_find_object( signature );
1744 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1745 digi_link_sound_to_object3( soundno, objnum, 1, max_volume, max_distance, loop_start, loop_end );
1750 case ND_EVENT_KILL_SOUND_TO_OBJ:
1752 int objnum, signature;
1753 nd_read_int( &signature );
1754 objnum = newdemo_find_object( signature );
1755 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1756 digi_kill_sound_linked_to_object(objnum);
1761 case ND_EVENT_WALL_HIT_PROCESS: {
1765 nd_read_int(&segnum);
1767 nd_read_fix(&damage);
1768 nd_read_int(&player);
1769 if (nd_bad_read) { done = -1; break; }
1770 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1771 wall_hit_process(&Segments[segnum], side, damage, player, &(Objects[0]) );
1775 case ND_EVENT_TRIGGER:
1776 nd_read_int(&segnum);
1778 nd_read_int(&objnum);
1780 if (nd_bad_read) { done = -1; break; }
1781 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1783 mprintf ((0,"EVENT TRIGGER! shot=%d\n",shot));
1785 if (Triggers[Walls[Segments[segnum].sides[side].wall_num].trigger].type == TT_SECRET_EXIT) {
1789 Assert(c == ND_EVENT_SECRET_THINGY);
1790 nd_read_int(&truth);
1792 check_trigger(&Segments[segnum], side, objnum,shot);
1794 check_trigger(&Segments[segnum], side, objnum,shot);
1798 case ND_EVENT_HOSTAGE_RESCUED: {
1801 nd_read_int(&hostage_number);
1802 if (nd_bad_read) { done = -1; break; }
1803 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1804 hostage_rescue( hostage_number );
1808 case ND_EVENT_MORPH_FRAME: {
1812 md = &morph_objects[0];
1813 if (newdemo_read( md->morph_vecs, sizeof(md->morph_vecs), 1 )!=1) { done=-1; break; }
1814 if (newdemo_read( md->submodel_active, sizeof(md->submodel_active), 1 )!=1) { done=-1; break; }
1815 if (newdemo_read( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 )!=1) { done=-1; break; }
1817 objnum = obj_allocate();
1820 obj = &Objects[objnum];
1821 nd_read_object(obj);
1822 obj->render_type = RT_POLYOBJ;
1823 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1824 if (nd_bad_read) { done = -1; break; }
1825 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1826 segnum = obj->segnum;
1827 obj->next = obj->prev = obj->segnum = -1;
1828 obj_link(obj-Objects,segnum);
1834 case ND_EVENT_WALL_TOGGLE:
1835 nd_read_int(&segnum);
1837 if (nd_bad_read) {done = -1; break; }
1838 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1839 wall_toggle(&Segments[segnum], side);
1842 case ND_EVENT_CONTROL_CENTER_DESTROYED:
1843 nd_read_int(&Countdown_seconds_left);
1844 Control_center_destroyed = 1;
1845 if (nd_bad_read) { done = -1; break; }
1846 if (!Newdemo_cntrlcen_destroyed) {
1847 newdemo_pop_ctrlcen_triggers();
1848 Newdemo_cntrlcen_destroyed = 1;
1849 // do_controlcen_destroyed_stuff(NULL);
1853 case ND_EVENT_HUD_MESSAGE: {
1856 nd_read_string(&(hud_msg[0]));
1857 if (nd_bad_read) { done = -1; break; }
1858 HUD_init_message( hud_msg );
1861 case ND_EVENT_START_GUIDED:
1862 Newdemo_flying_guided=1;
1863 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1864 Newdemo_flying_guided=0;
1866 case ND_EVENT_END_GUIDED:
1867 Newdemo_flying_guided=0;
1868 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1869 Newdemo_flying_guided=1;
1872 case ND_EVENT_PALETTE_EFFECT: {
1878 if (nd_bad_read) { done = -1; break; }
1879 PALETTE_FLASH_SET(r,g,b);
1883 case ND_EVENT_PLAYER_ENERGY: {
1887 nd_read_byte(&old_energy);
1888 nd_read_byte(&energy);
1889 if (nd_bad_read) {done = -1; break; }
1890 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1891 Players[Player_num].energy = i2f(energy);
1892 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1893 if (old_energy != 255)
1894 Players[Player_num].energy = i2f(old_energy);
1899 case ND_EVENT_PLAYER_AFTERBURNER: {
1901 ubyte old_afterburner;
1903 nd_read_byte(&old_afterburner);
1904 nd_read_byte(&afterburner);
1905 if (nd_bad_read) {done = -1; break; }
1906 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1907 Afterburner_charge = afterburner<<9;
1908 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1909 if (old_afterburner != 255)
1910 Afterburner_charge = old_afterburner<<9;
1915 case ND_EVENT_PLAYER_SHIELD: {
1919 nd_read_byte(&old_shield);
1920 nd_read_byte(&shield);
1921 if (nd_bad_read) {done = -1; break; }
1922 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1923 Players[Player_num].shields = i2f(shield);
1924 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1925 if (old_shield != 255)
1926 Players[Player_num].shields = i2f(old_shield);
1931 case ND_EVENT_PLAYER_FLAGS: {
1934 nd_read_int((int *)&(Players[Player_num].flags));
1935 if (nd_bad_read) {done = -1; break; }
1937 oflags = Players[Player_num].flags >> 16;
1938 Players[Player_num].flags &= 0xffff;
1940 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || ((Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) && (oflags != 0xffff)) ) {
1941 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1942 Players[Player_num].cloak_time = 0;
1943 Newdemo_players_cloaked &= ~(1 << Player_num);
1945 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1946 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1947 Newdemo_players_cloaked |= (1 << Player_num);
1949 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1950 Players[Player_num].invulnerable_time = 0;
1951 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1952 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1953 Players[Player_num].flags = oflags;
1954 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1955 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1956 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1957 Newdemo_players_cloaked |= (1 << Player_num);
1959 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1960 Players[Player_num].cloak_time = 0;
1961 Newdemo_players_cloaked &= ~(1 << Player_num);
1963 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1964 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1965 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1966 Players[Player_num].invulnerable_time = 0;
1968 update_laser_weapon_info(); // in case of quad laser change
1972 case ND_EVENT_PLAYER_WEAPON: {
1973 byte weapon_type, weapon_num;
1976 nd_read_byte(&weapon_type);
1977 nd_read_byte(&weapon_num);
1978 nd_read_byte(&old_weapon);
1979 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1980 if (weapon_type == 0)
1981 Primary_weapon = (int)weapon_num;
1983 Secondary_weapon = (int)weapon_num;
1984 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1985 if (weapon_type == 0)
1986 Primary_weapon = (int)old_weapon;
1988 Secondary_weapon = (int)old_weapon;
1993 case ND_EVENT_EFFECT_BLOWUP: {
1999 //create a dummy object which will be the weapon that hits
2000 //the monitor. the blowup code wants to know who the parent of the
2001 //laser is, so create a laser whose parent is the player
2002 dummy.ctype.laser_info.parent_type = OBJ_PLAYER;
2004 nd_read_short(&segnum);
2005 nd_read_byte(&side);
2006 nd_read_vector(&pnt);
2007 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2008 check_effect_blowup(&(Segments[segnum]), side, &pnt, &dummy, 0);
2012 case ND_EVENT_HOMING_DISTANCE: {
2015 nd_read_short(&distance);
2016 Players[Player_num].homing_object_dist = i2f((int)(distance << 16));
2020 case ND_EVENT_LETTERBOX:
2021 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2022 saved_letter_cockpit = Cockpit_mode;
2023 select_cockpit(CM_LETTERBOX);
2024 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2025 select_cockpit(saved_letter_cockpit);
2028 case ND_EVENT_CHANGE_COCKPIT:
2032 nd_read_int (&dummy);
2033 select_cockpit (dummy);
2037 case ND_EVENT_REARVIEW:
2038 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2039 saved_rearview_cockpit = Cockpit_mode;
2040 if (Cockpit_mode == CM_FULL_COCKPIT)
2041 select_cockpit(CM_REAR_VIEW);
2043 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2044 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2045 saved_rearview_cockpit = CM_FULL_COCKPIT;
2046 select_cockpit(saved_rearview_cockpit);
2051 case ND_EVENT_RESTORE_COCKPIT:
2052 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2053 saved_letter_cockpit = Cockpit_mode;
2054 select_cockpit(CM_LETTERBOX);
2055 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2056 select_cockpit(saved_letter_cockpit);
2060 case ND_EVENT_RESTORE_REARVIEW:
2061 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2062 saved_rearview_cockpit = Cockpit_mode;
2063 if (Cockpit_mode == CM_FULL_COCKPIT)
2064 select_cockpit(CM_REAR_VIEW);
2066 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2067 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2068 saved_rearview_cockpit = CM_FULL_COCKPIT;
2069 select_cockpit(saved_rearview_cockpit);
2075 case ND_EVENT_WALL_SET_TMAP_NUM1: {
2076 short seg, cseg, tmap;
2079 nd_read_short(&seg);
2080 nd_read_byte(&side);
2081 nd_read_short(&cseg);
2082 nd_read_byte(&cside);
2083 nd_read_short( &tmap );
2084 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD))
2085 Segments[seg].sides[side].tmap_num = Segments[cseg].sides[cside].tmap_num = tmap;
2089 case ND_EVENT_WALL_SET_TMAP_NUM2: {
2090 short seg, cseg, tmap;
2093 nd_read_short(&seg);
2094 nd_read_byte(&side);
2095 nd_read_short(&cseg);
2096 nd_read_byte(&cside);
2097 nd_read_short( &tmap );
2098 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) {
2099 Assert(tmap!=0 && Segments[seg].sides[side].tmap_num2!=0);
2100 Segments[seg].sides[side].tmap_num2 = Segments[cseg].sides[cside].tmap_num2 = tmap;
2105 case ND_EVENT_MULTI_CLOAK: {
2108 nd_read_byte(&pnum);
2109 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2110 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2111 Players[pnum].cloak_time = 0;
2112 Newdemo_players_cloaked &= ~(1 << pnum);
2113 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2114 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2115 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2116 Newdemo_players_cloaked |= (1 << pnum);
2121 case ND_EVENT_MULTI_DECLOAK: {
2124 nd_read_byte(&pnum);
2126 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2127 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2128 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2129 Newdemo_players_cloaked |= (1 << pnum);
2130 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2131 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2132 Players[pnum].cloak_time = 0;
2133 Newdemo_players_cloaked &= ~(1 << pnum);
2138 case ND_EVENT_MULTI_DEATH: {
2141 nd_read_byte(&pnum);
2142 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2143 Players[pnum].net_killed_total--;
2144 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2145 Players[pnum].net_killed_total++;
2150 case ND_EVENT_MULTI_KILL: {
2153 nd_read_byte(&pnum);
2154 nd_read_byte(&kill);
2155 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2156 Players[pnum].net_kills_total -= kill;
2157 if (Newdemo_game_mode & GM_TEAM)
2158 team_kills[get_team(pnum)] -= kill;
2159 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2160 Players[pnum].net_kills_total += kill;
2161 if (Newdemo_game_mode & GM_TEAM)
2162 team_kills[get_team(pnum)] += kill;
2164 Game_mode = Newdemo_game_mode;
2165 multi_sort_kill_list();
2166 Game_mode = GM_NORMAL;
2170 case ND_EVENT_MULTI_CONNECT: {
2171 byte pnum, new_player;
2172 int killed_total, kills_total;
2173 char new_callsign[CALLSIGN_LEN+1], old_callsign[CALLSIGN_LEN+1];
2175 nd_read_byte(&pnum);
2176 nd_read_byte(&new_player);
2178 nd_read_string(old_callsign);
2179 nd_read_int(&killed_total);
2180 nd_read_int(&kills_total);
2182 nd_read_string(new_callsign);
2183 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2184 Players[pnum].connected = 0;
2186 memcpy(Players[pnum].callsign, old_callsign, CALLSIGN_LEN+1);
2187 Players[pnum].net_killed_total = killed_total;
2188 Players[pnum].net_kills_total = kills_total;
2192 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2193 Players[pnum].connected = 1;
2194 Players[pnum].net_kills_total = 0;
2195 Players[pnum].net_killed_total = 0;
2196 memcpy(Players[pnum].callsign, new_callsign, CALLSIGN_LEN+1);
2203 case ND_EVENT_MULTI_RECONNECT: {
2206 nd_read_byte(&pnum);
2207 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2208 Players[pnum].connected = 0;
2209 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2210 Players[pnum].connected = 1;
2214 case ND_EVENT_MULTI_DISCONNECT: {
2217 nd_read_byte(&pnum);
2218 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2219 Players[pnum].connected = 1;
2220 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2221 Players[pnum].connected = 0;
2225 case ND_EVENT_MULTI_SCORE: {
2229 nd_read_byte(&pnum);
2230 nd_read_int(&score);
2231 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2232 Players[pnum].score -= score;
2233 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2234 Players[pnum].score += score;
2235 Game_mode = Newdemo_game_mode;
2236 multi_sort_kill_list();
2237 Game_mode = GM_NORMAL;
2242 case ND_EVENT_PLAYER_SCORE: {
2245 nd_read_int(&score);
2246 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2247 Players[Player_num].score -= score;
2248 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2249 Players[Player_num].score += score;
2254 case ND_EVENT_PRIMARY_AMMO: {
2255 short old_ammo, new_ammo;
2257 nd_read_short(&old_ammo);
2258 nd_read_short(&new_ammo);
2260 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2261 Players[Player_num].primary_ammo[Primary_weapon] = old_ammo;
2262 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2263 Players[Player_num].primary_ammo[Primary_weapon] = new_ammo;
2267 case ND_EVENT_SECONDARY_AMMO: {
2268 short old_ammo, new_ammo;
2270 nd_read_short(&old_ammo);
2271 nd_read_short(&new_ammo);
2273 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2274 Players[Player_num].secondary_ammo[Secondary_weapon] = old_ammo;
2275 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2276 Players[Player_num].secondary_ammo[Secondary_weapon] = new_ammo;
2280 case ND_EVENT_DOOR_OPENING: {
2284 nd_read_short(&segnum);
2285 nd_read_byte(&side);
2286 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2289 segment *segp, *csegp;
2291 segp = &Segments[segnum];
2292 csegp = &Segments[segp->children[side]];
2293 cside = find_connect_side(segp, csegp);
2294 anim_num = Walls[segp->sides[side].wall_num].clip_num;
2296 if (WallAnims[anim_num].flags & WCF_TMAP1) {
2297 segp->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[0];
2299 segp->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[0];
2305 case ND_EVENT_LASER_LEVEL: {
2306 byte old_level, new_level;
2308 nd_read_byte(&old_level);
2309 nd_read_byte(&new_level);
2310 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2311 Players[Player_num].laser_level = old_level;
2312 update_laser_weapon_info();
2313 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2314 Players[Player_num].laser_level = new_level;
2315 update_laser_weapon_info();
2320 case ND_EVENT_CLOAKING_WALL: {
2321 ubyte back_wall_num,front_wall_num,type,state,cloak_value;
2326 nd_read_byte(&front_wall_num);
2327 nd_read_byte(&back_wall_num);
2328 nd_read_byte(&type);
2329 nd_read_byte(&state);
2330 nd_read_byte(&cloak_value);
2336 Walls[front_wall_num].type = type;
2337 Walls[front_wall_num].state = state;
2338 Walls[front_wall_num].cloak_value = cloak_value;
2339 segp = &Segments[Walls[front_wall_num].segnum];
2340 sidenum = Walls[front_wall_num].sidenum;
2341 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2342 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2343 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2344 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2346 Walls[back_wall_num].type = type;
2347 Walls[back_wall_num].state = state;
2348 Walls[back_wall_num].cloak_value = cloak_value;
2349 segp = &Segments[Walls[back_wall_num].segnum];
2350 sidenum = Walls[back_wall_num].sidenum;
2351 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2352 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2353 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2354 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2359 case ND_EVENT_NEW_LEVEL: {
2360 byte new_level, old_level, loaded_level;
2362 nd_read_byte (&new_level);
2363 nd_read_byte (&old_level);
2364 if (Newdemo_vcr_state == ND_STATE_PAUSED)
2368 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2369 loaded_level = old_level;
2371 loaded_level = new_level;
2372 for (i = 0; i < MAX_PLAYERS; i++) {
2373 Players[i].cloak_time = 0;
2374 Players[i].flags &= ~PLAYER_FLAGS_CLOAKED;
2377 if ((loaded_level < Last_secret_level) || (loaded_level > Last_level)) {
2380 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2381 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2382 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2383 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2387 LoadLevel((int)loaded_level,1);
2388 Newdemo_cntrlcen_destroyed = 0;
2390 if (JustStartedPlayback)
2392 nd_read_int (&Num_walls);
2393 for (i=0;i<Num_walls;i++) // restore the walls
2395 nd_read_byte (&Walls[i].type);
2396 nd_read_byte (&Walls[i].flags);
2397 nd_read_byte (&Walls[i].state);
2399 seg = &Segments[Walls[i].segnum];
2400 side = Walls[i].sidenum;
2401 nd_read_short (&seg->sides[side].tmap_num);
2402 nd_read_short (&seg->sides[side].tmap_num2);
2405 if (Newdemo_game_mode & GM_CAPTURE)
2406 multi_apply_goal_textures ();
2408 JustStartedPlayback=0;
2412 // so says Rob H.!!! if (Newdemo_game_mode & GM_MULTI) {
2413 // so says Rob H.!!! for (i = 0; i < Num_walls; i++) {
2414 // so says Rob H.!!! if (Walls[i].type == WALL_BLASTABLE)
2415 // so says Rob H.!!! {
2416 // so says Rob H.!!! int a, n;
2417 // so says Rob H.!!! int side;
2418 // so says Rob H.!!! segment *seg;
2419 // so says Rob H.!!!
2420 // so says Rob H.!!! seg = &Segments[Walls[i].segnum];
2421 // so says Rob H.!!! side = Walls[i].sidenum;
2422 // so says Rob H.!!! a = Walls[i].clip_num;
2423 // so says Rob H.!!! n = WallAnims[a].num_frames;
2424 // so says Rob H.!!! seg->sides[side].tmap_num = WallAnims[a].frames[n-1];
2425 // so says Rob H.!!! Walls[i].flags |= WALL_BLASTED;
2426 // so says Rob H.!!! }
2427 // so says Rob H.!!! }
2428 // so says Rob H.!!! }
2430 reset_palette_add(); // get palette back to normal
2435 case ND_EVENT_EOF: {
2437 fseek(infile, -1, SEEK_CUR); // get back to the EOF marker
2439 NewdemoFrameCount++;
2453 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_ERR_READING;
2454 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_DEMO_OLD_CORRUPT;
2455 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2461 void newdemo_goto_beginning()
2463 // if (NewdemoFrameCount == 0)
2465 fseek(infile, 0, SEEK_SET);
2466 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2467 if (newdemo_read_demo_start(0))
2468 newdemo_stop_playback();
2469 if (newdemo_read_frame_information() == -1)
2470 newdemo_stop_playback();
2471 if (newdemo_read_frame_information() == -1)
2472 newdemo_stop_playback();
2473 Newdemo_vcr_state = ND_STATE_PAUSED;
2477 void newdemo_goto_end()
2479 short frame_length, byte_count, bshort;
2480 byte level, bbyte, laser_level;
2481 ubyte energy, shield, c;
2484 fseek(infile, -2, SEEK_END);
2485 nd_read_byte(&level);
2487 if ((level < Last_secret_level) || (level > Last_level)) {
2490 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2491 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2492 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2493 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2494 newdemo_stop_playback();
2497 if (level != Current_level_num)
2500 fseek(infile, -4, SEEK_END);
2501 nd_read_short(&byte_count);
2502 fseek(infile, -2 - byte_count, SEEK_CUR);
2504 nd_read_short(&frame_length);
2505 loc = ftell(infile);
2506 if (Newdemo_game_mode & GM_MULTI)
2507 nd_read_byte(&Newdemo_players_cloaked);
2509 nd_read_byte(&bbyte);
2510 nd_read_byte(&bbyte);
2511 nd_read_short(&bshort);
2514 nd_read_byte(&energy);
2515 nd_read_byte(&shield);
2516 Players[Player_num].energy = i2f(energy);
2517 Players[Player_num].shields = i2f(shield);
2518 nd_read_int((int *)&(Players[Player_num].flags));
2519 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
2520 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2521 Newdemo_players_cloaked |= (1 << Player_num);
2523 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2524 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2525 nd_read_byte((byte *)&Primary_weapon);
2526 nd_read_byte((byte *)&Secondary_weapon);
2527 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
2528 nd_read_short((short *)&(Players[Player_num].primary_ammo[i]));
2529 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
2530 nd_read_short((short *)&(Players[Player_num].secondary_ammo[i]));
2531 nd_read_byte(&laser_level);
2532 if (laser_level != Players[Player_num].laser_level) {
2533 Players[Player_num].laser_level = laser_level;
2534 update_laser_weapon_info();
2537 if (Newdemo_game_mode & GM_MULTI) {
2540 // see newdemo_read_start_demo for explanation of
2541 // why this is commented out
2542 // nd_read_byte((byte *)&N_players);
2543 for (i = 0; i < N_players; i++) {
2544 nd_read_string(Players[i].callsign);
2545 nd_read_byte(&(Players[i].connected));
2546 if (Newdemo_game_mode & GM_MULTI_COOP) {
2547 nd_read_int(&(Players[i].score));
2549 nd_read_short((short *)&(Players[i].net_killed_total));
2550 nd_read_short((short *)&(Players[i].net_kills_total));
2554 nd_read_int(&(Players[Player_num].score));
2557 fseek(infile, loc, SEEK_SET);
2558 fseek(infile, -frame_length, SEEK_CUR);
2559 nd_read_int(&NewdemoFrameCount); // get the frame count
2560 NewdemoFrameCount--;
2561 fseek(infile, 4, SEEK_CUR);
2562 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2563 newdemo_read_frame_information(); // then the frame information
2564 Newdemo_vcr_state = ND_STATE_PAUSED;
2568 void newdemo_back_frames(int frames)
2570 short last_frame_length;
2573 for (i = 0; i < frames; i++)
2575 fseek(infile, -10, SEEK_CUR);
2576 nd_read_short(&last_frame_length);
2577 fseek(infile, 8 - last_frame_length, SEEK_CUR);
2579 if (!Newdemo_at_eof && newdemo_read_frame_information() == -1) {
2580 newdemo_stop_playback();
2586 fseek(infile, -10, SEEK_CUR);
2587 nd_read_short(&last_frame_length);
2588 fseek(infile, 8 - last_frame_length, SEEK_CUR);
2594 * routine to interpolate the viewer position. the current position is
2595 * stored in the Viewer object. Save this position, and read the next
2596 * frame to get all objects read in. Calculate the delta playback and
2597 * the delta recording frame times between the two frames, then intepolate
2598 * the viewers position accordingly. nd_recorded_time is the time that it
2599 * took the recording to render the frame that we are currently looking
2603 void interpolate_frame(fix d_play, fix d_recorded)
2605 int i, j, num_cur_objs;
2609 factor = fixdiv(d_play, d_recorded);
2613 num_cur_objs = Highest_object_index;
2614 cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1));
2615 if (cur_objs == NULL) {
2616 mprintf((0,"Couldn't get %d bytes for cur_objs in interpolate_frame\n", sizeof(object) * num_cur_objs));
2620 for (i = 0; i <= num_cur_objs; i++)
2621 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2623 Newdemo_vcr_state = ND_STATE_PAUSED;
2624 if (newdemo_read_frame_information() == -1) {
2626 newdemo_stop_playback();
2630 for (i = 0; i <= num_cur_objs; i++) {
2631 for (j = 0; j <= Highest_object_index; j++) {
2632 if (cur_objs[i].signature == Objects[j].signature) {
2633 ubyte render_type = cur_objs[i].render_type;
2634 // fix delta_p, delta_h, delta_b;
2635 fix delta_x, delta_y, delta_z;
2636 // vms_angvec cur_angles, dest_angles;
2638 // Extract the angles from the object orientation matrix.
2639 // Some of this code taken from ai_turn_towards_vector
2640 // Don't do the interpolation on certain render types which don't use an orientation matrix
2642 if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) {
2644 vms_vector fvec1, fvec2, rvec1, rvec2;
2647 fvec1 = cur_objs[i].orient.fvec;
2648 vm_vec_scale(&fvec1, F1_0-factor);
2649 fvec2 = Objects[j].orient.fvec;
2650 vm_vec_scale(&fvec2, factor);
2651 vm_vec_add2(&fvec1, &fvec2);
2652 mag1 = vm_vec_normalize_quick(&fvec1);
2653 if (mag1 > F1_0/256) {
2654 rvec1 = cur_objs[i].orient.rvec;
2655 vm_vec_scale(&rvec1, F1_0-factor);
2656 rvec2 = Objects[j].orient.rvec;
2657 vm_vec_scale(&rvec2, factor);
2658 vm_vec_add2(&rvec1, &rvec2);
2659 vm_vec_normalize_quick(&rvec1); // Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1
2660 vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2663 //--old new way -- vms_vector fvec1, fvec2, rvec1, rvec2;
2665 //--old new way -- fvec1 = cur_objs[i].orient.fvec;
2666 //--old new way -- vm_vec_scale(&fvec1, F1_0-factor);
2667 //--old new way -- fvec2 = Objects[j].orient.fvec;
2668 //--old new way -- vm_vec_scale(&fvec2, factor);
2669 //--old new way -- vm_vec_add2(&fvec1, &fvec2);
2670 //--old new way -- vm_vec_normalize_quick(&fvec1);
2672 //--old new way -- rvec1 = cur_objs[i].orient.rvec;
2673 //--old new way -- vm_vec_scale(&rvec1, F1_0-factor);
2674 //--old new way -- rvec2 = Objects[j].orient.rvec;
2675 //--old new way -- vm_vec_scale(&rvec2, factor);
2676 //--old new way -- vm_vec_add2(&rvec1, &rvec2);
2677 //--old new way -- vm_vec_normalize_quick(&rvec1);
2679 //--old new way -- vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2681 // -- old fashioned way -- vm_extract_angles_matrix(&cur_angles, &(cur_objs[i].orient));
2682 // -- old fashioned way -- vm_extract_angles_matrix(&dest_angles, &(Objects[j].orient));
2683 // -- old fashioned way --
2684 // -- old fashioned way -- delta_p = (dest_angles.p - cur_angles.p);
2685 // -- old fashioned way -- delta_h = (dest_angles.h - cur_angles.h);
2686 // -- old fashioned way -- delta_b = (dest_angles.b - cur_angles.b);
2687 // -- old fashioned way --
2688 // -- old fashioned way -- if (delta_p != 0) {
2689 // -- old fashioned way -- if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
2690 // -- old fashioned way -- if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
2691 // -- old fashioned way -- delta_p = fixmul(delta_p, factor);
2692 // -- old fashioned way -- cur_angles.p += delta_p;
2693 // -- old fashioned way -- }
2694 // -- old fashioned way -- if (delta_h != 0) {
2695 // -- old fashioned way -- if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
2696 // -- old fashioned way -- if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
2697 // -- old fashioned way -- delta_h = fixmul(delta_h, factor);
2698 // -- old fashioned way -- cur_angles.h += delta_h;
2699 // -- old fashioned way -- }
2700 // -- old fashioned way -- if (delta_b != 0) {
2701 // -- old fashioned way -- if (delta_b > F1_0/2) delta_b = dest_angles.b - cur_angles.b - F1_0;
2702 // -- old fashioned way -- if (delta_b < -F1_0/2) delta_b = dest_angles.b - cur_angles.b + F1_0;
2703 // -- old fashioned way -- delta_b = fixmul(delta_b, factor);
2704 // -- old fashioned way -- cur_angles.b += delta_b;
2705 // -- old fashioned way -- }
2708 // Interpolate the object position. This is just straight linear
2711 delta_x = Objects[j].pos.x - cur_objs[i].pos.x;
2712 delta_y = Objects[j].pos.y - cur_objs[i].pos.y;
2713 delta_z = Objects[j].pos.z - cur_objs[i].pos.z;
2715 delta_x = fixmul(delta_x, factor);
2716 delta_y = fixmul(delta_y, factor);
2717 delta_z = fixmul(delta_z, factor);
2719 cur_objs[i].pos.x += delta_x;
2720 cur_objs[i].pos.y += delta_y;
2721 cur_objs[i].pos.z += delta_z;
2723 // -- old fashioned way --// stuff the new angles back into the object structure
2724 // -- old fashioned way -- vm_angles_2_matrix(&(cur_objs[i].orient), &cur_angles);
2729 // get back to original position in the demo file. Reread the current
2730 // frame information again to reset all of the object stuff not covered
2731 // with Highest_object_index and the object array (previously rendered
2732 // objects, etc....)
2734 newdemo_back_frames(1);
2735 newdemo_back_frames(1);
2736 if (newdemo_read_frame_information() == -1)
2737 newdemo_stop_playback();
2738 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2740 for (i = 0; i <= num_cur_objs; i++)
2741 memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object));
2742 Highest_object_index = num_cur_objs;
2746 void newdemo_playback_one_frame()
2748 int frames_back, i, level;
2749 static fix base_interpol_time = 0;
2750 static fix d_recorded = 0;
2752 for (i = 0; i < MAX_PLAYERS; i++)
2753 if (Newdemo_players_cloaked & (1 << i))
2754 Players[i].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2756 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2757 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2759 if (Newdemo_vcr_state == ND_STATE_PAUSED) // render a frame or not
2762 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2763 DoJasonInterpolate(nd_recorded_time);
2765 Control_center_destroyed = 0;
2766 Countdown_seconds_left = -1;
2767 PALETTE_FLASH_SET(0,0,0); //clear flash
2769 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2771 level = Current_level_num;
2772 if (NewdemoFrameCount == 0)
2774 else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (NewdemoFrameCount < 10)) {
2775 newdemo_goto_beginning();
2778 if (Newdemo_vcr_state == ND_STATE_REWINDING)
2782 if (Newdemo_at_eof) {
2783 fseek(infile, 11, SEEK_CUR);
2785 newdemo_back_frames(frames_back);
2787 if (level != Current_level_num)
2788 newdemo_pop_ctrlcen_triggers();
2790 if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) {
2791 if (level != Current_level_num)
2792 newdemo_back_frames(1);
2793 Newdemo_vcr_state = ND_STATE_PAUSED;
2796 else if (Newdemo_vcr_state == ND_STATE_FASTFORWARD) {
2797 if (!Newdemo_at_eof)
2799 for (i = 0; i < 10; i++)
2801 if (newdemo_read_frame_information() == -1)
2804 Newdemo_vcr_state = ND_STATE_PAUSED;
2806 newdemo_stop_playback();
2812 Newdemo_vcr_state = ND_STATE_PAUSED;
2814 else if (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD) {
2815 if (!Newdemo_at_eof) {
2816 level = Current_level_num;
2817 if (newdemo_read_frame_information() == -1) {
2818 if (!Newdemo_at_eof)
2819 newdemo_stop_playback();
2821 if (level != Current_level_num) {
2822 if (newdemo_read_frame_information() == -1) {
2823 if (!Newdemo_at_eof)
2824 newdemo_stop_playback();
2827 Newdemo_vcr_state = ND_STATE_PAUSED;
2829 Newdemo_vcr_state = ND_STATE_PAUSED;
2833 // First, uptate the total playback time to date. Then we check to see
2834 // if we need to change the playback style to interpolate frames or
2835 // skip frames based on where the playback time is relative to the
2838 if (NewdemoFrameCount <= 0)
2839 nd_playback_total = nd_recorded_total; // baseline total playback time
2841 nd_playback_total += FrameTime;
2842 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2843 if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) {
2844 playback_style = INTERPOLATE_PLAYBACK;
2845 nd_playback_total = nd_recorded_total + FrameTime; // baseline playback time
2846 base_interpol_time = nd_recorded_total;
2847 d_recorded = nd_recorded_time; // baseline delta recorded
2849 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2850 if (nd_playback_total > nd_recorded_total)
2851 playback_style = SKIP_PLAYBACK;
2854 if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) {
2857 if (nd_recorded_total - nd_playback_total < FrameTime) {
2858 d_recorded = nd_recorded_total - nd_playback_total;
2860 while (nd_recorded_total - nd_playback_total < FrameTime) {
2862 int i, j, num_objs, level;
2864 num_objs = Highest_object_index;
2865 cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1));
2866 if (cur_objs == NULL) {
2867 Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs);
2870 for (i = 0; i <= num_objs; i++)
2871 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2873 level = Current_level_num;
2874 if (newdemo_read_frame_information() == -1) {
2876 newdemo_stop_playback();
2879 if (level != Current_level_num) {
2881 if (newdemo_read_frame_information() == -1)
2882 newdemo_stop_playback();
2886 // for each new object in the frame just read in, determine if there is
2887 // a corresponding object that we have been interpolating. If so, then
2888 // copy that interpolated object to the new Objects array so that the
2889 // interpolated position and orientation can be preserved.
2891 for (i = 0; i <= num_objs; i++) {
2892 for (j = 0; j <= Highest_object_index; j++) {
2893 if (cur_objs[i].signature == Objects[j].signature) {
2894 memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix));
2895 memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector));
2901 d_recorded += nd_recorded_time;
2902 base_interpol_time = nd_playback_total - FrameTime;
2906 d_play = nd_playback_total - base_interpol_time;
2907 interpolate_frame(d_play, d_recorded);
2911 // mprintf ((0, "*"));
2912 if (newdemo_read_frame_information() == -1) {
2913 newdemo_stop_playback();
2916 if (playback_style == SKIP_PLAYBACK) {
2917 // mprintf ((0, "."));
2918 while (nd_playback_total > nd_recorded_total) {
2919 if (newdemo_read_frame_information() == -1) {
2920 newdemo_stop_playback();
2929 void newdemo_start_recording()
2932 Newdemo_size=GetFreeDiskSpace();
2933 mprintf((0, "Free space = %d\n", Newdemo_size));
2935 Newdemo_size = GetDiskFree();
2938 Newdemo_size -= 100000;
2940 if ((Newdemo_size+100000) < 2000000000) {
2941 if (((int)(Newdemo_size)) < 500000) {
2943 nm_messagebox(NULL, 1, TXT_OK, TXT_DEMO_NO_SPACE);
2945 nm_messagebox(NULL, 1, TXT_OK, "Not enough space on current\ndrive to start demo recording.");
2951 Newdemo_num_written = 0;
2953 Newdemo_state = ND_STATE_RECORDING;
2954 outfile = fopen( DEMO_FILENAME, "wb" );
2957 if (outfile == NULL && errno == ENOENT) { //dir doesn't exist?
2959 if (outfile == NULL) { //dir doesn't exist and no errno on mac!
2961 mkdir(DEMO_DIR); //try making directory
2962 outfile = fopen( DEMO_FILENAME, "wb" );
2965 if (outfile == NULL)
2967 nm_messagebox(NULL, 1, TXT_OK, "Cannot open demo temp file");
2968 Newdemo_state = ND_STATE_NORMAL;
2971 newdemo_record_start_demo();
2975 char demoname_allowed_chars[] = "azAZ09__--";
2976 void newdemo_stop_recording()
2980 static char filename[15] = "", *s;
2981 static ubyte tmpcnt = 0;
2983 char fullname[15+FILENAME_LEN] = DEMO_DIR;
2984 unsigned short byte_count = 0;
2988 nd_write_byte(ND_EVENT_EOF);
2989 nd_write_short(frame_bytes_written - 1);
2990 if (Game_mode & GM_MULTI) {
2991 for (l = 0; l < N_players; l++) {
2992 if (Players[l].flags & PLAYER_FLAGS_CLOAKED)
2993 cloaked |= (1 << l);
2995 nd_write_byte(cloaked);
2996 nd_write_byte(ND_EVENT_EOF);
2998 nd_write_short(ND_EVENT_EOF);
3000 nd_write_short(ND_EVENT_EOF);
3001 nd_write_int(ND_EVENT_EOF);
3003 byte_count += 10; // from frame_bytes_written
3005 nd_write_byte((byte)(f2ir(Players[Player_num].energy)));
3006 nd_write_byte((byte)(f2ir(Players[Player_num].shields)));
3007 nd_write_int(Players[Player_num].flags); // be sure players flags are set
3008 nd_write_byte((byte)Primary_weapon);
3009 nd_write_byte((byte)Secondary_weapon);
3012 for (l = 0; l < MAX_PRIMARY_WEAPONS; l++)
3013 nd_write_short((short)Players[Player_num].primary_ammo[l]);
3015 for (l = 0; l < MAX_SECONDARY_WEAPONS; l++)
3016 nd_write_short((short)Players[Player_num].secondary_ammo[l]);
3017 byte_count += (sizeof(short) * (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS));
3019 nd_write_byte(Players[Player_num].laser_level);
3022 if (Game_mode & GM_MULTI) {
3023 nd_write_byte((byte)N_players);
3025 for (l = 0; l < N_players; l++) {
3026 nd_write_string(Players[l].callsign);
3027 byte_count += (strlen(Players[l].callsign) + 2);
3028 nd_write_byte(Players[l].connected);
3029 if (Game_mode & GM_MULTI_COOP) {
3030 nd_write_int(Players[l].score);
3033 nd_write_short((short)Players[l].net_killed_total);
3034 nd_write_short((short)Players[l].net_kills_total);
3039 nd_write_int(Players[Player_num].score);
3042 nd_write_short(byte_count);
3044 nd_write_byte(Current_level_num);
3045 nd_write_byte(ND_EVENT_EOF);
3050 Newdemo_state = ND_STATE_NORMAL;
3051 gr_palette_load( gr_palette );
3053 if (filename[0] != '\0') {
3054 int num, i = strlen(filename) - 1;
3057 while (isdigit(filename[i])) {
3063 num = atoi(&(filename[i]));
3066 sprintf (newfile, "%s%d", filename, num);
3067 strncpy(filename, newfile, 8);
3074 Newmenu_allowed_chars = demoname_allowed_chars;
3075 if (!Newdemo_no_space) {
3076 m[0].type=NM_TYPE_INPUT; m[0].text_len = 8; m[0].text = filename;
3077 exit = newmenu_do( NULL, TXT_SAVE_DEMO_AS, 1, &(m[0]), NULL );
3078 } else if (Newdemo_no_space == 1) {
3079 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_BAD;
3080 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3081 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3082 } else if (Newdemo_no_space == 2) {
3083 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_NOSPACE;
3084 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3085 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3087 Newmenu_allowed_chars = NULL;
3089 if (exit == -2) { // got bumped out from network menu
3090 char save_file[7+FILENAME_LEN];
3092 if (filename[0] != '\0') {
3093 strcpy(save_file, DEMO_DIR);
3094 strcat(save_file, filename);
3095 strcat(save_file, ".dem");
3097 sprintf (save_file, "%stmp%d.dem", DEMO_DIR, tmpcnt++);
3099 rename(DEMO_FILENAME, save_file);
3102 if (exit == -1) { // pressed ESC
3103 remove(DEMO_FILENAME); // might as well remove the file
3104 return; // return without doing anything
3107 if (filename[0]==0) //null string
3110 //check to make sure name is ok
3111 for (s=filename;*s;s++)
3112 if (!isalnum(*s) && *s!='_') {
3113 nm_messagebox1(NULL, NULL,1,TXT_CONTINUE, TXT_DEMO_USE_LETTERS);
3117 if (Newdemo_no_space)
3118 strcat(fullname, m[1].text);
3120 strcat(fullname, m[0].text);
3121 strcat(fullname, ".dem");
3123 rename(DEMO_FILENAME, fullname);
3126 //returns the number of demo files on the disk
3127 int newdemo_count_demos()
3129 FILEFINDSTRUCT find;
3132 if( !FileFindFirst("demos/*.dem", &find ) ) {
3135 } while( !FileFindNext( &find ) );
3142 void newdemo_start_playback(char * filename)
3144 FILEFINDSTRUCT find;
3146 char filename2[7+FILENAME_LEN] = DEMO_DIR;
3149 change_playernum_to(0);
3151 First_time_playback=1;
3152 JasonPlaybackTotal=0;
3154 if (filename==NULL) {
3155 // Randomly pick a filename
3156 int NumFiles = 0, RandFileNum;
3159 NumFiles = newdemo_count_demos();
3161 if ( NumFiles == 0 ) {
3162 return; // No files found!
3164 RandFileNum = d_rand() % NumFiles;
3166 if( !FileFindFirst( "demos/*.dem", &find ) ) {
3168 if ( NumFiles==RandFileNum ) {
3169 filename = (char *)&find.name;
3173 } while( !FileFindNext( &find ) );
3176 if ( filename==NULL) return;
3182 strcat(filename2,filename);
3184 infile = fopen( filename2, "rb" );
3186 mprintf( (0, "Error reading '%s'\n", filename ));
3192 change_playernum_to(0); // force playernum to 0
3194 strncpy(nd_save_callsign, Players[Player_num].callsign, CALLSIGN_LEN);
3195 Viewer = ConsoleObject = &Objects[0]; // play properly as if console player
3196 if (newdemo_read_demo_start(rnd_demo)) {
3201 Game_mode = GM_NORMAL;
3202 Newdemo_state = ND_STATE_PLAYBACK;
3203 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3204 Newdemo_old_cockpit = Cockpit_mode;
3205 Newdemo_size = filelength(fileno(infile));
3208 NewdemoFrameCount = 0;
3209 Newdemo_players_cloaked = 0;
3210 playback_style = NORMAL_PLAYBACK;
3211 Function_mode = FMODE_GAME;
3212 Cockpit_3d_view[0] = CV_NONE; //turn off 3d views on cockpit
3213 Cockpit_3d_view[1] = CV_NONE; //turn off 3d views on cockpit
3214 newdemo_playback_one_frame(); // this one loads new level
3215 newdemo_playback_one_frame(); // get all of the objects to renderb game
3218 void newdemo_stop_playback()
3221 Newdemo_state = ND_STATE_NORMAL;
3223 change_playernum_to(0); //this is reality
3225 strncpy(Players[Player_num].callsign, nd_save_callsign, CALLSIGN_LEN);
3226 Cockpit_mode = Newdemo_old_cockpit;
3227 Game_mode = GM_GAME_OVER;
3228 Function_mode = FMODE_MENU;
3229 longjmp(LeaveGame,0); // Exit game loop
3235 #define BUF_SIZE 16384
3237 void newdemo_strip_frames(char *outname, int bytes_to_strip)
3241 int total_size, bytes_done, read_elems, bytes_back;
3242 int trailer_start, loc1, loc2, stop_loc, bytes_to_read;
3243 short last_frame_length;
3246 total_size = filelength(fileno(infile));
3247 outfile = fopen(outname, "wb");
3248 if (outfile == NULL) {
3251 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't open output file";
3252 newmenu_do( NULL, NULL, 1, m, NULL );
3253 newdemo_stop_playback();
3256 buf = d_malloc(BUF_SIZE);
3260 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't malloc output buffer";
3261 newmenu_do( NULL, NULL, 1, m, NULL );
3263 newdemo_stop_playback();
3267 trailer_start = ftell(infile);
3268 fseek(infile, 11, SEEK_CUR);
3270 while (bytes_back < bytes_to_strip) {
3271 loc1 = ftell(infile);
3272 // fseek(infile, -10, SEEK_CUR);
3273 // nd_read_short(&last_frame_length);
3274 // fseek(infile, 8 - last_frame_length, SEEK_CUR);
3275 newdemo_back_frames(1);
3276 loc2 = ftell(infile);
3277 bytes_back += (loc1 - loc2);
3279 fseek(infile, -10, SEEK_CUR);
3280 nd_read_short(&last_frame_length);
3281 fseek(infile, -3, SEEK_CUR);
3282 stop_loc = ftell(infile);
3283 fseek(infile, 0, SEEK_SET);
3284 while (stop_loc > 0) {
3285 if (stop_loc < BUF_SIZE)
3286 bytes_to_read = stop_loc;
3288 bytes_to_read = BUF_SIZE;
3289 read_elems = fread(buf, 1, bytes_to_read, infile);
3290 fwrite(buf, 1, read_elems, outfile);
3291 stop_loc -= read_elems;
3293 stop_loc = ftell(outfile);
3294 fseek(infile, trailer_start, SEEK_SET);
3295 while ((read_elems = fread(buf, 1, BUF_SIZE, infile)) != 0)
3296 fwrite(buf, 1, read_elems, outfile);
3297 fseek(outfile, stop_loc, SEEK_SET);
3298 fseek(outfile, 1, SEEK_CUR);
3299 fwrite(&last_frame_length, 2, 1, outfile);
3301 newdemo_stop_playback();
3307 object DemoRightExtra,DemoLeftExtra;
3308 ubyte DemoDoRight=0,DemoDoLeft=0;
3310 void nd_render_extras (ubyte which,object *obj)
3313 ubyte type=which&15;
3317 Int3(); // how'd we get here?
3318 do_cockpit_window_view(w,NULL,0,WBU_WEAPON,NULL);
3323 { memcpy (&DemoRightExtra,obj,sizeof(object)); DemoDoRight=type; }
3325 { memcpy (&DemoLeftExtra,obj,sizeof(object)); DemoDoLeft=type;}
3329 void DoJasonInterpolate (fix recorded_time)
3332 float MyRecFrameTime,ThisFrameTime;
3334 JasonPlaybackTotal+=FrameTime;
3337 if (!First_time_playback)
3339 // get the difference between the recorded time and the playback time
3341 MyRecFrameTime=f2fl(recorded_time);
3342 ThisFrameTime=f2fl(FrameTime);
3344 the_delay=((MyRecFrameTime-ThisFrameTime)*1000.0);
3345 //mprintf ((0,"The delay=%d\n",the_delay));
3349 d_delay (the_delay);
3354 while (JasonPlaybackTotal > nd_recorded_total)
3355 if (newdemo_read_frame_information() == -1)
3357 newdemo_stop_playback();
3361 // the_delay=(f2fl(nd_recorded_total-JasonPlaybackTotal))*1000.0;
3363 // delay (the_delay);
3368 First_time_playback=0;
3372 #pragma global_optimizer reset