1 /* $Id: newdemo.c,v 1.17 2004-08-28 23:17:45 schaffner Exp $ */
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
17 * Code to make a complete demo playback system.
29 #include <string.h> // for memset
33 #include <ctype.h> /* for isdigit */
37 #include <sys/types.h>
100 #include "findfile.h"
103 #include "editor/editor.h"
107 #pragma global_optimizer off // pretty much sucks...need to look into this
110 void DoJasonInterpolate (fix recorded_time);
112 //#include "nocfile.h"
114 //Does demo start automatically?
117 sbyte WasRecorded [MAX_OBJECTS];
118 sbyte ViewWasRecorded[MAX_OBJECTS];
119 sbyte RenderingWasRecorded[32];
121 #define ND_EVENT_EOF 0 // EOF
122 #define ND_EVENT_START_DEMO 1 // Followed by 16 character, NULL terminated filename of .SAV file to use
123 #define ND_EVENT_START_FRAME 2 // Followed by integer frame number, then a fix FrameTime
124 #define ND_EVENT_VIEWER_OBJECT 3 // Followed by an object structure
125 #define ND_EVENT_RENDER_OBJECT 4 // Followed by an object structure
126 #define ND_EVENT_SOUND 5 // Followed by int soundum
127 #define ND_EVENT_SOUND_ONCE 6 // Followed by int soundum
128 #define ND_EVENT_SOUND_3D 7 // Followed by int soundum, int angle, int volume
129 #define ND_EVENT_WALL_HIT_PROCESS 8 // Followed by int segnum, int side, fix damage
130 #define ND_EVENT_TRIGGER 9 // Followed by int segnum, int side, int objnum
131 #define ND_EVENT_HOSTAGE_RESCUED 10 // Followed by int hostage_type
132 #define ND_EVENT_SOUND_3D_ONCE 11 // Followed by int soundum, int angle, int volume
133 #define ND_EVENT_MORPH_FRAME 12 // Followed by ? data
134 #define ND_EVENT_WALL_TOGGLE 13 // Followed by int seg, int side
135 #define ND_EVENT_HUD_MESSAGE 14 // Followed by char size, char * string (+null)
136 #define ND_EVENT_CONTROL_CENTER_DESTROYED 15 // Just a simple flag
137 #define ND_EVENT_PALETTE_EFFECT 16 // Followed by short r,g,b
138 #define ND_EVENT_PLAYER_ENERGY 17 // followed by byte energy
139 #define ND_EVENT_PLAYER_SHIELD 18 // followed by byte shields
140 #define ND_EVENT_PLAYER_FLAGS 19 // followed by player flags
141 #define ND_EVENT_PLAYER_WEAPON 20 // followed by weapon type and weapon number
142 #define ND_EVENT_EFFECT_BLOWUP 21 // followed by segment, side, and pnt
143 #define ND_EVENT_HOMING_DISTANCE 22 // followed by homing distance
144 #define ND_EVENT_LETTERBOX 23 // letterbox mode for death seq.
145 #define ND_EVENT_RESTORE_COCKPIT 24 // restore cockpit after death
146 #define ND_EVENT_REARVIEW 25 // going to rear view mode
147 #define ND_EVENT_WALL_SET_TMAP_NUM1 26 // Wall changed
148 #define ND_EVENT_WALL_SET_TMAP_NUM2 27 // Wall changed
149 #define ND_EVENT_NEW_LEVEL 28 // followed by level number
150 #define ND_EVENT_MULTI_CLOAK 29 // followed by player num
151 #define ND_EVENT_MULTI_DECLOAK 30 // followed by player num
152 #define ND_EVENT_RESTORE_REARVIEW 31 // restore cockpit after rearview mode
153 #define ND_EVENT_MULTI_DEATH 32 // with player number
154 #define ND_EVENT_MULTI_KILL 33 // with player number
155 #define ND_EVENT_MULTI_CONNECT 34 // with player number
156 #define ND_EVENT_MULTI_RECONNECT 35 // with player number
157 #define ND_EVENT_MULTI_DISCONNECT 36 // with player number
158 #define ND_EVENT_MULTI_SCORE 37 // playernum / score
159 #define ND_EVENT_PLAYER_SCORE 38 // followed by score
160 #define ND_EVENT_PRIMARY_AMMO 39 // with old/new ammo count
161 #define ND_EVENT_SECONDARY_AMMO 40 // with old/new ammo count
162 #define ND_EVENT_DOOR_OPENING 41 // with segment/side
163 #define ND_EVENT_LASER_LEVEL 42 // no data
164 #define ND_EVENT_PLAYER_AFTERBURNER 43 // followed by byte old ab, current ab
165 #define ND_EVENT_CLOAKING_WALL 44 // info changing while wall cloaking
166 #define ND_EVENT_CHANGE_COCKPIT 45 // change the cockpit
167 #define ND_EVENT_START_GUIDED 46 // switch to guided view
168 #define ND_EVENT_END_GUIDED 47 // stop guided view/return to ship
169 #define ND_EVENT_SECRET_THINGY 48 // 0/1 = secret exit functional/non-functional
170 #define ND_EVENT_LINK_SOUND_TO_OBJ 49 // record digi_link_sound_to_object3
171 #define ND_EVENT_KILL_SOUND_TO_OBJ 50 // record digi_kill_sound_linked_to_object
174 #define NORMAL_PLAYBACK 0
175 #define SKIP_PLAYBACK 1
176 #define INTERPOLATE_PLAYBACK 2
177 #define INTERPOL_FACTOR (F1_0 + (F1_0/5))
179 #define DEMO_VERSION 15 // last D1 version was 13
180 #define DEMO_GAME_TYPE 3 // 1 was shareware, 2 registered
182 #define DEMO_FILENAME DEMO_DIR "tmpdemo.dem"
184 #define DEMO_MAX_LEVELS 29
187 char nd_save_callsign[CALLSIGN_LEN+1];
188 int Newdemo_state = 0;
189 int Newdemo_vcr_state = 0;
190 int Newdemo_start_frame = -1;
191 unsigned int Newdemo_size;
192 int Newdemo_num_written;
193 int Newdemo_game_mode;
194 int Newdemo_old_cockpit;
195 sbyte Newdemo_no_space;
196 sbyte Newdemo_at_eof;
197 sbyte Newdemo_do_interpolate = 0; // 1
198 sbyte Newdemo_players_cloaked;
199 sbyte Newdemo_warning_given = 0;
200 sbyte Newdemo_cntrlcen_destroyed = 0;
201 static sbyte nd_bad_read;
202 int NewdemoFrameCount;
203 short frame_bytes_written = 0;
204 fix nd_playback_total;
205 fix nd_recorded_total;
206 fix nd_recorded_time;
207 sbyte playback_style;
208 sbyte First_time_playback=1;
209 fix JasonPlaybackTotal=0;
213 CFILE *outfile = NULL;
215 int newdemo_get_percent_done() {
216 if ( Newdemo_state == ND_STATE_PLAYBACK ) {
217 return (cftell(infile) * 100) / Newdemo_size;
219 if ( Newdemo_state == ND_STATE_RECORDING ) {
220 return cftell(outfile);
225 #define VEL_PRECISION 12
227 void my_extract_shortpos(object *objp, shortpos *spp)
233 objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
234 objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
235 objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
237 objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
238 objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
239 objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
241 objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
242 objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
243 objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
245 segnum = spp->segment;
246 objp->segnum = segnum;
248 objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x;
249 objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y;
250 objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z;
252 objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION);
253 objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION);
254 objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION);
257 int newdemo_read( void *buffer, int elsize, int nelem )
260 num_read = cfread(buffer, elsize, nelem, infile);
261 if (cferror(infile) || cfeof(infile))
267 int newdemo_find_object( int signature )
272 for (i=0; i<=Highest_object_index; i++, objp++ ) {
273 if ( (objp->type != OBJ_NONE) && (objp->signature == signature))
279 int newdemo_write( void *buffer, int elsize, int nelem )
281 int num_written, total_size;
283 total_size = elsize * nelem;
284 frame_bytes_written += total_size;
285 Newdemo_num_written += total_size;
286 Assert(outfile != NULL);
287 num_written = cfwrite(buffer, elsize, nelem, outfile);
288 //if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) {
289 // Newdemo_no_space=1;
290 // newdemo_stop_recording();
293 if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space)
295 if (num_written == nelem && !Newdemo_no_space)
299 newdemo_stop_recording();
304 * The next bunch of files taken from Matt's gamesave.c. We have to modify
305 * these since the demo must save more information about objects that
309 static void nd_write_byte(sbyte b)
311 newdemo_write(&b, 1, 1);
314 static void nd_write_short(short s)
316 newdemo_write(&s, 2, 1);
319 static void nd_write_int(int i)
321 newdemo_write(&i, 4, 1);
324 static void nd_write_string(char *str)
326 nd_write_byte(strlen(str) + 1);
327 newdemo_write(str, strlen(str) + 1, 1);
330 static void nd_write_fix(fix f)
332 newdemo_write(&f, sizeof(fix), 1);
335 static void nd_write_fixang(fixang f)
337 newdemo_write(&f, sizeof(fixang), 1);
340 static void nd_write_vector(vms_vector *v)
347 static void nd_write_angvec(vms_angvec *v)
349 nd_write_fixang(v->p);
350 nd_write_fixang(v->b);
351 nd_write_fixang(v->h);
354 void nd_write_shortpos(object *obj)
360 create_shortpos(&sp, obj, 0);
362 render_type = obj->render_type;
363 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
364 for (i = 0; i < 9; i++)
365 nd_write_byte(sp.bytemat[i]);
366 for (i = 0; i < 9; i++) {
367 if (sp.bytemat[i] != 0)
371 Int3(); // contact Allender about this.
375 nd_write_short(sp.xo);
376 nd_write_short(sp.yo);
377 nd_write_short(sp.zo);
378 nd_write_short(sp.segment);
379 nd_write_short(sp.velx);
380 nd_write_short(sp.vely);
381 nd_write_short(sp.velz);
384 static void nd_read_byte(sbyte *b)
386 newdemo_read(b, 1, 1);
389 static void nd_read_short(short *s)
391 newdemo_read(s, 2, 1);
394 static void nd_read_int(int *i)
396 newdemo_read(i, 4, 1);
399 static void nd_read_string(char *str)
404 newdemo_read(str, len, 1);
407 static void nd_read_fix(fix *f)
409 newdemo_read(f, sizeof(fix), 1);
412 static void nd_read_fixang(fixang *f)
414 newdemo_read(f, sizeof(fixang), 1);
417 static void nd_read_vector(vms_vector *v)
419 nd_read_fix(&(v->x));
420 nd_read_fix(&(v->y));
421 nd_read_fix(&(v->z));
424 static void nd_read_angvec(vms_angvec *v)
426 nd_read_fixang(&(v->p));
427 nd_read_fixang(&(v->b));
428 nd_read_fixang(&(v->h));
431 static void nd_read_shortpos(object *obj)
437 render_type = obj->render_type;
438 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
439 for (i = 0; i < 9; i++)
440 nd_read_byte(&(sp.bytemat[i]));
443 nd_read_short(&(sp.xo));
444 nd_read_short(&(sp.yo));
445 nd_read_short(&(sp.zo));
446 nd_read_short(&(sp.segment));
447 nd_read_short(&(sp.velx));
448 nd_read_short(&(sp.vely));
449 nd_read_short(&(sp.velz));
451 my_extract_shortpos(obj, &sp);
452 if ((obj->id == VCLIP_MORPHING_ROBOT) && (render_type == RT_FIREBALL) && (obj->control_type == CT_EXPLOSION))
453 extract_orient_from_segment(&obj->orient,&Segments[obj->segnum]);
457 object *prev_obj=NULL; //ptr to last object read in
459 void nd_read_object(object *obj)
461 memset(obj, 0, sizeof(object));
464 * Do render type first, since with render_type == RT_NONE, we
465 * blow by all other object information
467 nd_read_byte((sbyte *) &(obj->render_type));
468 nd_read_byte((sbyte *) &(obj->type));
469 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
472 nd_read_byte((sbyte *) &(obj->id));
473 nd_read_byte((sbyte *) &(obj->flags));
474 nd_read_short((short *)&(obj->signature));
475 nd_read_shortpos(obj);
477 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
480 obj->attached_obj = -1;
485 obj->control_type = CT_POWERUP;
486 obj->movement_type = MT_NONE;
487 obj->size = HOSTAGE_SIZE;
491 obj->control_type = CT_AI;
492 // (MarkA and MikeK said we should not do the crazy last secret stuff with multiple reactors...
493 // This necessary code is our vindication. --MK, 2/15/96)
494 if (obj->id != SPECIAL_REACTOR_ROBOT)
495 obj->movement_type = MT_PHYSICS;
497 obj->movement_type = MT_NONE;
498 obj->size = Polygon_models[Robot_info[obj->id].model_num].rad;
499 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
500 obj->rtype.pobj_info.subobj_flags = 0;
501 obj->ctype.ai_info.CLOAKED = (Robot_info[obj->id].cloak_type?1:0);
505 obj->control_type = CT_POWERUP;
506 nd_read_byte((sbyte *) &(obj->movement_type)); // might have physics movement
507 obj->size = Powerup_info[obj->id].size;
511 obj->control_type = CT_NONE;
512 obj->movement_type = MT_PHYSICS;
513 obj->size = Polygon_models[Player_ship->model_num].rad;
514 obj->rtype.pobj_info.model_num = Player_ship->model_num;
515 obj->rtype.pobj_info.subobj_flags = 0;
519 obj->control_type = CT_NONE;
520 obj->movement_type = MT_NONE;
521 obj->size = Polygon_models[obj->id].rad;
522 obj->rtype.pobj_info.model_num = obj->id;
523 obj->rtype.pobj_info.subobj_flags = 0;
527 nd_read_byte((sbyte *) &(obj->control_type));
528 nd_read_byte((sbyte *) &(obj->movement_type));
529 nd_read_fix(&(obj->size));
534 nd_read_vector(&(obj->last_pos));
535 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
536 nd_read_fix(&(obj->lifeleft));
541 obj->lifeleft = (fix)b;
542 // MWA old way -- won't work with big endian machines nd_read_byte((ubyte *)&(obj->lifeleft));
543 obj->lifeleft = (fix)((int)obj->lifeleft << 12);
546 if (obj->type == OBJ_ROBOT) {
547 if (Robot_info[obj->id].boss_flag) {
550 nd_read_byte(&cloaked);
551 obj->ctype.ai_info.CLOAKED = cloaked;
555 switch (obj->movement_type) {
558 nd_read_vector(&(obj->mtype.phys_info.velocity));
559 nd_read_vector(&(obj->mtype.phys_info.thrust));
563 nd_read_vector(&(obj->mtype.spin_rate));
573 switch (obj->control_type) {
577 nd_read_fix(&(obj->ctype.expl_info.spawn_time));
578 nd_read_fix(&(obj->ctype.expl_info.delete_time));
579 nd_read_short(&(obj->ctype.expl_info.delete_objnum));
581 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
583 if (obj->flags & OF_ATTACHED) { //attach to previous object
584 Assert(prev_obj!=NULL);
585 if (prev_obj->control_type == CT_EXPLOSION) {
586 if (prev_obj->flags & OF_ATTACHED && prev_obj->ctype.expl_info.attach_parent!=-1)
587 obj_attach(&Objects[prev_obj->ctype.expl_info.attach_parent],obj);
589 obj->flags &= ~OF_ATTACHED;
592 obj_attach(prev_obj,obj);
598 nd_read_fix(&(obj->ctype.light_info.intensity));
620 switch (obj->render_type) {
629 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
630 nd_read_int(&(obj->rtype.pobj_info.model_num));
631 nd_read_int(&(obj->rtype.pobj_info.subobj_flags));
634 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
636 for (i=0;i<MAX_SUBMODELS;i++)
637 nd_read_angvec(&(obj->pobj_info.anim_angles[i]));
639 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
640 nd_read_angvec(&obj->rtype.pobj_info.anim_angles[i]);
645 obj->rtype.pobj_info.tmap_override = tmo;
648 obj->rtype.pobj_info.tmap_override = -1;
650 int xlated_tmo = tmap_xlate_table[tmo];
651 if (xlated_tmo < 0) {
652 //mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->pobj_info.model_num));
656 obj->rtype.pobj_info.tmap_override = xlated_tmo;
664 case RT_WEAPON_VCLIP:
667 nd_read_int(&(obj->rtype.vclip_info.vclip_num));
668 nd_read_fix(&(obj->rtype.vclip_info.frametime));
669 nd_read_byte(&(obj->rtype.vclip_info.framenum));
683 void nd_write_object(object *obj)
687 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
691 * Do render_type first so on read, we can make determination of
692 * what else to read in
694 nd_write_byte(obj->render_type);
695 nd_write_byte(obj->type);
696 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
699 nd_write_byte(obj->id);
700 nd_write_byte(obj->flags);
701 nd_write_short((short)obj->signature);
702 nd_write_shortpos(obj);
704 if ((obj->type != OBJ_HOSTAGE) && (obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_POWERUP) && (obj->type != OBJ_CLUTTER)) {
705 nd_write_byte(obj->control_type);
706 nd_write_byte(obj->movement_type);
707 nd_write_fix(obj->size);
709 if (obj->type == OBJ_POWERUP)
710 nd_write_byte(obj->movement_type);
712 nd_write_vector(&obj->last_pos);
714 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
715 nd_write_fix(obj->lifeleft);
717 life = (int)obj->lifeleft;
721 nd_write_byte((ubyte)life);
724 if (obj->type == OBJ_ROBOT) {
725 if (Robot_info[obj->id].boss_flag) {
726 if ((GameTime > Boss_cloak_start_time) && (GameTime < Boss_cloak_end_time))
733 switch (obj->movement_type) {
736 nd_write_vector(&obj->mtype.phys_info.velocity);
737 nd_write_vector(&obj->mtype.phys_info.thrust);
741 nd_write_vector(&obj->mtype.spin_rate);
751 switch (obj->control_type) {
757 nd_write_fix(obj->ctype.expl_info.spawn_time);
758 nd_write_fix(obj->ctype.expl_info.delete_time);
759 nd_write_short(obj->ctype.expl_info.delete_objnum);
767 nd_write_fix(obj->ctype.light_info.intensity);
774 case CT_SLEW: //the player is generally saved as slew
787 switch (obj->render_type) {
796 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
797 nd_write_int(obj->rtype.pobj_info.model_num);
798 nd_write_int(obj->rtype.pobj_info.subobj_flags);
801 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
803 for (i=0;i<MAX_SUBMODELS;i++)
804 nd_write_angvec(&obj->pobj_info.anim_angles[i]);
806 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
807 nd_write_angvec(&obj->rtype.pobj_info.anim_angles[i]);
809 nd_write_int(obj->rtype.pobj_info.tmap_override);
815 case RT_WEAPON_VCLIP:
818 nd_write_int(obj->rtype.vclip_info.vclip_num);
819 nd_write_fix(obj->rtype.vclip_info.frametime);
820 nd_write_byte(obj->rtype.vclip_info.framenum);
833 int JustStartedRecording=0,JustStartedPlayback=0;
835 void newdemo_record_start_demo()
840 nd_write_byte(ND_EVENT_START_DEMO);
841 nd_write_byte(DEMO_VERSION);
842 nd_write_byte(DEMO_GAME_TYPE);
843 nd_write_fix(GameTime);
846 if (Game_mode & GM_MULTI)
847 nd_write_int(Game_mode | (Player_num << 16));
850 // NOTE LINK TO ABOVE!!!
851 nd_write_int(Game_mode);
854 if (Game_mode & GM_TEAM) {
855 nd_write_byte(Netgame.team_vector);
856 nd_write_string(Netgame.team_name[0]);
857 nd_write_string(Netgame.team_name[1]);
860 if (Game_mode & GM_MULTI) {
861 nd_write_byte((sbyte)N_players);
862 for (i = 0; i < N_players; i++) {
863 nd_write_string(Players[i].callsign);
864 nd_write_byte(Players[i].connected);
866 if (Game_mode & GM_MULTI_COOP) {
867 nd_write_int(Players[i].score);
869 nd_write_short((short)Players[i].net_killed_total);
870 nd_write_short((short)Players[i].net_kills_total);
875 // NOTE LINK TO ABOVE!!!
876 nd_write_int(Players[Player_num].score);
878 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
879 nd_write_short((short)Players[Player_num].primary_ammo[i]);
881 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
882 nd_write_short((short)Players[Player_num].secondary_ammo[i]);
884 nd_write_byte((sbyte)Players[Player_num].laser_level);
886 // Support for missions added here
888 nd_write_string(Current_mission_filename);
890 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
891 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
892 nd_write_int(Players[Player_num].flags); // be sure players flags are set
893 nd_write_byte((sbyte)Primary_weapon);
894 nd_write_byte((sbyte)Secondary_weapon);
895 Newdemo_start_frame = FrameCount;
896 JustStartedRecording=1;
898 newdemo_set_new_level(Current_level_num);
903 void newdemo_record_start_frame(int frame_number, fix frame_time )
907 if (Newdemo_no_space) {
908 newdemo_stop_playback();
914 for (i=0;i<MAX_OBJECTS;i++)
917 ViewWasRecorded[i]=0;
920 RenderingWasRecorded[i]=0;
922 frame_number -= Newdemo_start_frame;
924 Assert(frame_number >= 0 );
926 nd_write_byte(ND_EVENT_START_FRAME);
927 nd_write_short(frame_bytes_written - 1); // from previous frame
928 frame_bytes_written=3;
929 nd_write_int(frame_number);
930 nd_write_int(frame_time);
935 void newdemo_record_render_object(object * obj)
937 if (ViewWasRecorded[obj-Objects])
940 //if (obj==&Objects[Players[Player_num].objnum] && !Player_is_dead)
944 nd_write_byte(ND_EVENT_RENDER_OBJECT);
945 nd_write_object(obj);
949 extern ubyte RenderingType;
951 void newdemo_record_viewer_object(object * obj)
954 if (ViewWasRecorded[obj-Objects] && (ViewWasRecorded[obj-Objects]-1)==RenderingType)
956 //if (WasRecorded[obj-Objects])
958 if (RenderingWasRecorded[RenderingType])
961 ViewWasRecorded[obj-Objects]=RenderingType+1;
962 RenderingWasRecorded[RenderingType]=1;
964 nd_write_byte(ND_EVENT_VIEWER_OBJECT);
965 nd_write_byte(RenderingType);
966 nd_write_object(obj);
970 void newdemo_record_sound( int soundno )
973 nd_write_byte(ND_EVENT_SOUND);
974 nd_write_int( soundno );
978 //--unused-- void newdemo_record_sound_once( int soundno ) {
979 //--unused-- stop_time();
980 //--unused-- nd_write_byte( ND_EVENT_SOUND_ONCE );
981 //--unused-- nd_write_int( soundno );
982 //--unused-- start_time();
986 void newdemo_record_cockpit_change (int mode)
989 nd_write_byte (ND_EVENT_CHANGE_COCKPIT);
995 void newdemo_record_sound_3d( int soundno, int angle, int volume )
998 nd_write_byte( ND_EVENT_SOUND_3D );
999 nd_write_int( soundno );
1000 nd_write_int( angle );
1001 nd_write_int( volume );
1005 void newdemo_record_sound_3d_once( int soundno, int angle, int volume )
1008 nd_write_byte( ND_EVENT_SOUND_3D_ONCE );
1009 nd_write_int( soundno );
1010 nd_write_int( angle );
1011 nd_write_int( volume );
1016 void newdemo_record_link_sound_to_object3( int soundno, short objnum, fix max_volume, fix max_distance, int loop_start, int loop_end )
1019 nd_write_byte( ND_EVENT_LINK_SOUND_TO_OBJ );
1020 nd_write_int( soundno );
1021 nd_write_int( Objects[objnum].signature );
1022 nd_write_int( max_volume );
1023 nd_write_int( max_distance );
1024 nd_write_int( loop_start );
1025 nd_write_int( loop_end );
1029 void newdemo_record_kill_sound_linked_to_object( int objnum )
1032 nd_write_byte( ND_EVENT_KILL_SOUND_TO_OBJ );
1033 nd_write_int( Objects[objnum].signature );
1038 void newdemo_record_wall_hit_process( int segnum, int side, int damage, int playernum )
1044 //playernum = playernum;
1045 nd_write_byte( ND_EVENT_WALL_HIT_PROCESS );
1046 nd_write_int( segnum );
1047 nd_write_int( side );
1048 nd_write_int( damage );
1049 nd_write_int( playernum );
1053 void newdemo_record_guided_start ()
1055 nd_write_byte (ND_EVENT_START_GUIDED);
1058 void newdemo_record_guided_end ()
1060 nd_write_byte (ND_EVENT_END_GUIDED);
1063 void newdemo_record_secret_exit_blown(int truth)
1066 nd_write_byte( ND_EVENT_SECRET_THINGY );
1067 nd_write_int( truth );
1071 void newdemo_record_trigger( int segnum, int side, int objnum,int shot )
1074 nd_write_byte( ND_EVENT_TRIGGER );
1075 nd_write_int( segnum );
1076 nd_write_int( side );
1077 nd_write_int( objnum );
1082 void newdemo_record_hostage_rescued( int hostage_number ) {
1084 nd_write_byte( ND_EVENT_HOSTAGE_RESCUED );
1085 nd_write_int( hostage_number );
1089 void newdemo_record_morph_frame(morph_data *md)
1093 nd_write_byte( ND_EVENT_MORPH_FRAME );
1095 newdemo_write( md->morph_vecs, sizeof(md->morph_vecs), 1 );
1096 newdemo_write( md->submodel_active, sizeof(md->submodel_active), 1 );
1097 newdemo_write( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 );
1099 nd_write_object( md->obj );
1103 void newdemo_record_wall_toggle( int segnum, int side )
1106 nd_write_byte( ND_EVENT_WALL_TOGGLE );
1107 nd_write_int( segnum );
1108 nd_write_int( side );
1112 void newdemo_record_control_center_destroyed()
1115 nd_write_byte( ND_EVENT_CONTROL_CENTER_DESTROYED );
1116 nd_write_int( Countdown_seconds_left );
1120 void newdemo_record_hud_message( char * message )
1123 nd_write_byte( ND_EVENT_HUD_MESSAGE );
1124 nd_write_string(message);
1128 void newdemo_record_palette_effect(short r, short g, short b )
1131 nd_write_byte( ND_EVENT_PALETTE_EFFECT );
1132 nd_write_short( r );
1133 nd_write_short( g );
1134 nd_write_short( b );
1138 void newdemo_record_player_energy(int old_energy, int energy)
1141 nd_write_byte( ND_EVENT_PLAYER_ENERGY );
1142 nd_write_byte((sbyte) old_energy);
1143 nd_write_byte((sbyte) energy);
1147 void newdemo_record_player_afterburner(fix old_afterburner, fix afterburner)
1150 nd_write_byte( ND_EVENT_PLAYER_AFTERBURNER );
1151 nd_write_byte((sbyte) (old_afterburner>>9));
1152 nd_write_byte((sbyte) (afterburner>>9));
1156 void newdemo_record_player_shields(int old_shield, int shield)
1159 nd_write_byte( ND_EVENT_PLAYER_SHIELD );
1160 nd_write_byte((sbyte)old_shield);
1161 nd_write_byte((sbyte)shield);
1165 void newdemo_record_player_flags(uint oflags, uint flags)
1168 nd_write_byte( ND_EVENT_PLAYER_FLAGS );
1169 nd_write_int(((short)oflags << 16) | (short)flags);
1173 void newdemo_record_player_weapon(int weapon_type, int weapon_num)
1176 nd_write_byte( ND_EVENT_PLAYER_WEAPON );
1177 nd_write_byte((sbyte)weapon_type);
1178 nd_write_byte((sbyte)weapon_num);
1180 nd_write_byte((sbyte)Secondary_weapon);
1182 nd_write_byte((sbyte)Primary_weapon);
1186 void newdemo_record_effect_blowup(short segment, int side, vms_vector *pnt)
1189 nd_write_byte (ND_EVENT_EFFECT_BLOWUP);
1190 nd_write_short(segment);
1191 nd_write_byte((sbyte)side);
1192 nd_write_vector(pnt);
1196 void newdemo_record_homing_distance(fix distance)
1199 nd_write_byte(ND_EVENT_HOMING_DISTANCE);
1200 nd_write_short((short)(distance>>16));
1204 void newdemo_record_letterbox(void)
1207 nd_write_byte(ND_EVENT_LETTERBOX);
1211 void newdemo_record_rearview(void)
1214 nd_write_byte(ND_EVENT_REARVIEW);
1218 void newdemo_record_restore_cockpit(void)
1221 nd_write_byte(ND_EVENT_RESTORE_COCKPIT);
1225 void newdemo_record_restore_rearview(void)
1228 nd_write_byte(ND_EVENT_RESTORE_REARVIEW);
1232 void newdemo_record_wall_set_tmap_num1(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1235 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM1);
1236 nd_write_short(seg);
1237 nd_write_byte(side);
1238 nd_write_short(cseg);
1239 nd_write_byte(cside);
1240 nd_write_short(tmap);
1244 void newdemo_record_wall_set_tmap_num2(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1247 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM2);
1248 nd_write_short(seg);
1249 nd_write_byte(side);
1250 nd_write_short(cseg);
1251 nd_write_byte(cside);
1252 nd_write_short(tmap);
1256 void newdemo_record_multi_cloak(int pnum)
1259 nd_write_byte(ND_EVENT_MULTI_CLOAK);
1260 nd_write_byte((sbyte)pnum);
1264 void newdemo_record_multi_decloak(int pnum)
1267 nd_write_byte(ND_EVENT_MULTI_DECLOAK);
1268 nd_write_byte((sbyte)pnum);
1272 void newdemo_record_multi_death(int pnum)
1275 nd_write_byte(ND_EVENT_MULTI_DEATH);
1276 nd_write_byte((sbyte)pnum);
1280 void newdemo_record_multi_kill(int pnum, sbyte kill)
1283 nd_write_byte(ND_EVENT_MULTI_KILL);
1284 nd_write_byte((sbyte)pnum);
1285 nd_write_byte(kill);
1289 void newdemo_record_multi_connect(int pnum, int new_player, char *new_callsign)
1292 nd_write_byte(ND_EVENT_MULTI_CONNECT);
1293 nd_write_byte((sbyte)pnum);
1294 nd_write_byte((sbyte)new_player);
1296 nd_write_string(Players[pnum].callsign);
1297 nd_write_int(Players[pnum].net_killed_total);
1298 nd_write_int(Players[pnum].net_kills_total);
1300 nd_write_string(new_callsign);
1304 void newdemo_record_multi_reconnect(int pnum)
1307 nd_write_byte(ND_EVENT_MULTI_RECONNECT);
1308 nd_write_byte((sbyte)pnum);
1312 void newdemo_record_multi_disconnect(int pnum)
1315 nd_write_byte(ND_EVENT_MULTI_DISCONNECT);
1316 nd_write_byte((sbyte)pnum);
1320 void newdemo_record_player_score(int score)
1323 nd_write_byte(ND_EVENT_PLAYER_SCORE);
1324 nd_write_int(score);
1328 void newdemo_record_multi_score(int pnum, int score)
1331 nd_write_byte(ND_EVENT_MULTI_SCORE);
1332 nd_write_byte((sbyte)pnum);
1333 nd_write_int(score - Players[pnum].score); // called before score is changed!!!!
1337 void newdemo_record_primary_ammo(int old_ammo, int new_ammo)
1340 nd_write_byte(ND_EVENT_PRIMARY_AMMO);
1342 nd_write_short((short)new_ammo);
1344 nd_write_short((short)old_ammo);
1345 nd_write_short((short)new_ammo);
1349 void newdemo_record_secondary_ammo(int old_ammo, int new_ammo)
1352 nd_write_byte(ND_EVENT_SECONDARY_AMMO);
1354 nd_write_short((short)new_ammo);
1356 nd_write_short((short)old_ammo);
1357 nd_write_short((short)new_ammo);
1361 void newdemo_record_door_opening(int segnum, int side)
1364 nd_write_byte(ND_EVENT_DOOR_OPENING);
1365 nd_write_short((short)segnum);
1366 nd_write_byte((sbyte)side);
1370 void newdemo_record_laser_level(sbyte old_level, sbyte new_level)
1373 nd_write_byte(ND_EVENT_LASER_LEVEL);
1374 nd_write_byte(old_level);
1375 nd_write_byte(new_level);
1379 void newdemo_record_cloaking_wall(int front_wall_num, int back_wall_num, ubyte type, ubyte state, fix cloak_value, fix l0, fix l1, fix l2, fix l3)
1381 Assert(front_wall_num <= 255 && back_wall_num <= 255);
1384 nd_write_byte(ND_EVENT_CLOAKING_WALL);
1385 nd_write_byte(front_wall_num);
1386 nd_write_byte(back_wall_num);
1387 nd_write_byte(type);
1388 nd_write_byte(state);
1389 nd_write_byte(cloak_value);
1390 nd_write_short(l0>>8);
1391 nd_write_short(l1>>8);
1392 nd_write_short(l2>>8);
1393 nd_write_short(l3>>8);
1397 void newdemo_set_new_level(int level_num)
1404 nd_write_byte(ND_EVENT_NEW_LEVEL);
1405 nd_write_byte((sbyte)level_num);
1406 nd_write_byte((sbyte)Current_level_num);
1408 if (JustStartedRecording==1)
1410 nd_write_int(Num_walls);
1411 for (i=0;i<Num_walls;i++)
1413 nd_write_byte (Walls[i].type);
1414 nd_write_byte (Walls[i].flags);
1415 nd_write_byte (Walls[i].state);
1417 seg = &Segments[Walls[i].segnum];
1418 side = Walls[i].sidenum;
1419 nd_write_short (seg->sides[side].tmap_num);
1420 nd_write_short (seg->sides[side].tmap_num2);
1421 JustStartedRecording=0;
1428 int newdemo_read_demo_start(int rnd_demo)
1430 sbyte i, version, game_type, laser_level;
1431 sbyte c, energy, shield;
1432 char text[128], current_mission[9];
1435 if ((c != ND_EVENT_START_DEMO) || nd_bad_read) {
1438 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_CORRUPT);
1439 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1440 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1443 nd_read_byte(&version);
1444 nd_read_byte(&game_type);
1445 if (game_type < DEMO_GAME_TYPE) {
1448 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1449 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1450 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Descent: First Strike";
1452 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1455 if (game_type != DEMO_GAME_TYPE) {
1458 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1459 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1460 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Unknown Descent version";
1462 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1465 if (version < DEMO_VERSION) {
1468 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_OLD);
1469 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1470 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1474 nd_read_fix(&GameTime);
1475 Boss_cloak_start_time=Boss_cloak_end_time=GameTime;
1476 JasonPlaybackTotal=0;
1478 nd_read_int(&Newdemo_game_mode);
1481 change_playernum_to((Newdemo_game_mode >> 16) & 0x7);
1482 if (Newdemo_game_mode & GM_TEAM) {
1483 nd_read_byte(&(Netgame.team_vector));
1484 nd_read_string(Netgame.team_name[0]);
1485 nd_read_string(Netgame.team_name[1]);
1487 if (Newdemo_game_mode & GM_MULTI) {
1492 // changed this to above two lines -- breaks on the mac because of
1494 // nd_read_byte((sbyte *)&N_players);
1495 for (i = 0 ; i < N_players; i++) {
1496 Players[i].cloak_time = 0;
1497 Players[i].invulnerable_time = 0;
1498 nd_read_string(Players[i].callsign);
1499 nd_read_byte(&(Players[i].connected));
1501 if (Newdemo_game_mode & GM_MULTI_COOP) {
1502 nd_read_int(&(Players[i].score));
1504 nd_read_short((short *)&(Players[i].net_killed_total));
1505 nd_read_short((short *)&(Players[i].net_kills_total));
1508 Game_mode = Newdemo_game_mode;
1509 multi_sort_kill_list();
1510 Game_mode = GM_NORMAL;
1513 nd_read_int(&(Players[Player_num].score)); // Note link to above if!
1515 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
1516 nd_read_short((short*)&(Players[Player_num].primary_ammo[i]));
1518 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
1519 nd_read_short((short*)&(Players[Player_num].secondary_ammo[i]));
1521 nd_read_byte(&laser_level);
1522 if (laser_level != Players[Player_num].laser_level) {
1523 Players[Player_num].laser_level = laser_level;
1524 update_laser_weapon_info();
1527 // Support for missions
1529 nd_read_string(current_mission);
1530 if (!load_mission_by_name(current_mission)) {
1534 sprintf(text, TXT_NOMISSION4DEMO, current_mission);
1535 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1536 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1541 nd_recorded_total = 0;
1542 nd_playback_total = 0;
1543 nd_read_byte(&energy);
1544 nd_read_byte(&shield);
1546 nd_read_int((int *)&(Players[Player_num].flags));
1547 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
1548 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1549 Newdemo_players_cloaked |= (1 << Player_num);
1551 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
1552 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1554 nd_read_byte((sbyte *)&Primary_weapon);
1555 nd_read_byte((sbyte *)&Secondary_weapon);
1557 // Next bit of code to fix problem that I introduced between 1.0 and 1.1
1558 // check the next byte -- it _will_ be a load_new_level event. If it is
1559 // not, then we must shift all bytes up by one.
1561 Players[Player_num].energy = i2f(energy);
1562 Players[Player_num].shields = i2f(shield);
1563 JustStartedPlayback=1;
1567 void newdemo_pop_ctrlcen_triggers()
1571 segment *seg, *csegp;
1573 for (i = 0; i < ControlCenterTriggers.num_links; i++) {
1574 seg = &Segments[ControlCenterTriggers.seg[i]];
1575 side = ControlCenterTriggers.side[i];
1576 csegp = &Segments[seg->children[side]];
1577 cside = find_connect_side(seg, csegp);
1578 anim_num = Walls[seg->sides[side].wall_num].clip_num;
1579 n = WallAnims[anim_num].num_frames;
1580 if (WallAnims[anim_num].flags & WCF_TMAP1) {
1581 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[n-1];
1583 seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[n-1];
1588 #define N_PLAYER_SHIP_TEXTURES 6
1590 void nd_render_extras (ubyte,object *);
1591 extern void multi_apply_goal_textures ();
1592 ubyte Newdemo_flying_guided=0;
1594 int newdemo_read_frame_information()
1596 int done, segnum, side, objnum, soundno, angle, volume, i,shot;
1598 sbyte c,WhichWindow;
1599 static sbyte saved_letter_cockpit;
1600 static sbyte saved_rearview_cockpit;
1602 static char LastReadValue=101;
1607 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1608 for (segnum=0; segnum <= Highest_segment_index; segnum++)
1609 Segments[segnum].objects = -1;
1612 Players[Player_num].homing_object_dist = -F1_0;
1618 if (nd_bad_read) { done = -1; break; }
1622 case ND_EVENT_START_FRAME: { // Followed by an integer frame number, then a fix FrameTime
1623 short last_frame_length;
1626 nd_read_short(&last_frame_length);
1627 nd_read_int(&NewdemoFrameCount);
1628 nd_read_int((int *)&nd_recorded_time);
1629 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1630 nd_recorded_total += nd_recorded_time;
1631 NewdemoFrameCount--;
1633 if (nd_bad_read) { done = -1; break; }
1637 case ND_EVENT_VIEWER_OBJECT: // Followed by an object structure
1638 nd_read_byte (&WhichWindow);
1641 //mprintf ((0,"Reading extra!\n"));
1642 nd_read_object (&extraobj);
1643 if (Newdemo_vcr_state!=ND_STATE_PAUSED)
1645 if (nd_bad_read) { done = -1; break; }
1647 nd_render_extras (WhichWindow,&extraobj);
1652 //mprintf ((0,"Reading viewer!\n"));
1653 //Viewer=&Objects[0];
1654 nd_read_object(Viewer);
1656 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1657 if (nd_bad_read) { done = -1; break; }
1658 segnum = Viewer->segnum;
1659 Viewer->next = Viewer->prev = Viewer->segnum = -1;
1661 // HACK HACK HACK -- since we have multiple level recording, it can be the case
1662 // HACK HACK HACK -- that when rewinding the demo, the viewer is in a segment
1663 // HACK HACK HACK -- that is greater than the highest index of segments. Bash
1664 // HACK HACK HACK -- the viewer to segment 0 for bogus view.
1666 if (segnum > Highest_segment_index)
1668 obj_link(Viewer-Objects,segnum);
1673 case ND_EVENT_RENDER_OBJECT: // Followed by an object structure
1674 objnum = obj_allocate();
1677 obj = &Objects[objnum];
1678 nd_read_object(obj);
1679 if (nd_bad_read) { done = -1; break; }
1680 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1681 segnum = obj->segnum;
1682 obj->next = obj->prev = obj->segnum = -1;
1684 // HACK HACK HACK -- don't render objects is segments greater than Highest_segment_index
1685 // HACK HACK HACK -- (see above)
1687 if (segnum > Highest_segment_index)
1690 obj_link(obj-Objects,segnum);
1692 if ((obj->type == OBJ_PLAYER) && (Newdemo_game_mode & GM_MULTI)) {
1695 if (Newdemo_game_mode & GM_TEAM)
1696 player = get_team(obj->id);
1703 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
1704 multi_player_textures[player][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[obj->rtype.pobj_info.model_num].first_texture+i]];
1706 multi_player_textures[player][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2]];
1707 multi_player_textures[player][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2+1]];
1708 obj->rtype.pobj_info.alt_textures = player+1;
1714 case ND_EVENT_SOUND:
1715 nd_read_int(&soundno);
1716 if (nd_bad_read) {done = -1; break; }
1717 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1718 digi_play_sample( soundno, F1_0 );
1721 //--unused case ND_EVENT_SOUND_ONCE:
1722 //--unused nd_read_int(&soundno);
1723 //--unused if (nd_bad_read) { done = -1; break; }
1724 //--unused if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1725 //--unused digi_play_sample_once( soundno, F1_0 );
1728 case ND_EVENT_SOUND_3D:
1729 nd_read_int(&soundno);
1730 nd_read_int(&angle);
1731 nd_read_int(&volume);
1732 if (nd_bad_read) { done = -1; break; }
1733 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1734 digi_play_sample_3d( soundno, angle, volume, 0 );
1737 case ND_EVENT_SOUND_3D_ONCE:
1738 nd_read_int(&soundno);
1739 nd_read_int(&angle);
1740 nd_read_int(&volume);
1741 if (nd_bad_read) { done = -1; break; }
1742 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1743 digi_play_sample_3d( soundno, angle, volume, 1 );
1746 case ND_EVENT_LINK_SOUND_TO_OBJ:
1748 int soundno, objnum, max_volume, max_distance, loop_start, loop_end;
1750 nd_read_int( &soundno );
1751 nd_read_int( &signature );
1752 nd_read_int( &max_volume );
1753 nd_read_int( &max_distance );
1754 nd_read_int( &loop_start );
1755 nd_read_int( &loop_end );
1756 objnum = newdemo_find_object( signature );
1757 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1758 digi_link_sound_to_object3( soundno, objnum, 1, max_volume, max_distance, loop_start, loop_end );
1763 case ND_EVENT_KILL_SOUND_TO_OBJ:
1765 int objnum, signature;
1766 nd_read_int( &signature );
1767 objnum = newdemo_find_object( signature );
1768 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1769 digi_kill_sound_linked_to_object(objnum);
1774 case ND_EVENT_WALL_HIT_PROCESS: {
1778 nd_read_int(&segnum);
1780 nd_read_fix(&damage);
1781 nd_read_int(&player);
1782 if (nd_bad_read) { done = -1; break; }
1783 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1784 wall_hit_process(&Segments[segnum], side, damage, player, &(Objects[0]) );
1788 case ND_EVENT_TRIGGER:
1789 nd_read_int(&segnum);
1791 nd_read_int(&objnum);
1793 if (nd_bad_read) { done = -1; break; }
1794 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1796 mprintf ((0,"EVENT TRIGGER! shot=%d\n",shot));
1798 if (Triggers[Walls[Segments[segnum].sides[side].wall_num].trigger].type == TT_SECRET_EXIT) {
1802 Assert(c == ND_EVENT_SECRET_THINGY);
1803 nd_read_int(&truth);
1805 check_trigger(&Segments[segnum], side, objnum,shot);
1807 check_trigger(&Segments[segnum], side, objnum,shot);
1811 case ND_EVENT_HOSTAGE_RESCUED: {
1814 nd_read_int(&hostage_number);
1815 if (nd_bad_read) { done = -1; break; }
1816 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1817 hostage_rescue( hostage_number );
1821 case ND_EVENT_MORPH_FRAME: {
1825 md = &morph_objects[0];
1826 if (newdemo_read( md->morph_vecs, sizeof(md->morph_vecs), 1 )!=1) { done=-1; break; }
1827 if (newdemo_read( md->submodel_active, sizeof(md->submodel_active), 1 )!=1) { done=-1; break; }
1828 if (newdemo_read( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 )!=1) { done=-1; break; }
1830 objnum = obj_allocate();
1833 obj = &Objects[objnum];
1834 nd_read_object(obj);
1835 obj->render_type = RT_POLYOBJ;
1836 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1837 if (nd_bad_read) { done = -1; break; }
1838 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1839 segnum = obj->segnum;
1840 obj->next = obj->prev = obj->segnum = -1;
1841 obj_link(obj-Objects,segnum);
1847 case ND_EVENT_WALL_TOGGLE:
1848 nd_read_int(&segnum);
1850 if (nd_bad_read) {done = -1; break; }
1851 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1852 wall_toggle(&Segments[segnum], side);
1855 case ND_EVENT_CONTROL_CENTER_DESTROYED:
1856 nd_read_int(&Countdown_seconds_left);
1857 Control_center_destroyed = 1;
1858 if (nd_bad_read) { done = -1; break; }
1859 if (!Newdemo_cntrlcen_destroyed) {
1860 newdemo_pop_ctrlcen_triggers();
1861 Newdemo_cntrlcen_destroyed = 1;
1862 //do_controlcen_destroyed_stuff(NULL);
1866 case ND_EVENT_HUD_MESSAGE: {
1869 nd_read_string(&(hud_msg[0]));
1870 if (nd_bad_read) { done = -1; break; }
1871 HUD_init_message( hud_msg );
1874 case ND_EVENT_START_GUIDED:
1875 Newdemo_flying_guided=1;
1876 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1877 Newdemo_flying_guided=0;
1879 case ND_EVENT_END_GUIDED:
1880 Newdemo_flying_guided=0;
1881 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1882 Newdemo_flying_guided=1;
1885 case ND_EVENT_PALETTE_EFFECT: {
1891 if (nd_bad_read) { done = -1; break; }
1892 PALETTE_FLASH_SET(r,g,b);
1896 case ND_EVENT_PLAYER_ENERGY: {
1900 nd_read_byte(&old_energy);
1901 nd_read_byte(&energy);
1902 if (nd_bad_read) {done = -1; break; }
1903 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1904 Players[Player_num].energy = i2f(energy);
1905 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1906 if (old_energy != -128)
1907 Players[Player_num].energy = i2f(old_energy);
1912 case ND_EVENT_PLAYER_AFTERBURNER: {
1914 sbyte old_afterburner;
1916 nd_read_byte(&old_afterburner);
1917 nd_read_byte(&afterburner);
1918 if (nd_bad_read) {done = -1; break; }
1919 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1920 Afterburner_charge = afterburner<<9;
1921 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1922 if (old_afterburner != -128)
1923 Afterburner_charge = old_afterburner<<9;
1928 case ND_EVENT_PLAYER_SHIELD: {
1932 nd_read_byte(&old_shield);
1933 nd_read_byte(&shield);
1934 if (nd_bad_read) {done = -1; break; }
1935 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1936 Players[Player_num].shields = i2f(shield);
1937 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1938 if (old_shield != -128)
1939 Players[Player_num].shields = i2f(old_shield);
1944 case ND_EVENT_PLAYER_FLAGS: {
1947 nd_read_int((int *)&(Players[Player_num].flags));
1948 if (nd_bad_read) {done = -1; break; }
1950 oflags = Players[Player_num].flags >> 16;
1951 Players[Player_num].flags &= 0xffff;
1953 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || ((Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) && (oflags != 0xffff)) ) {
1954 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1955 Players[Player_num].cloak_time = 0;
1956 Newdemo_players_cloaked &= ~(1 << Player_num);
1958 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1959 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1960 Newdemo_players_cloaked |= (1 << Player_num);
1962 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1963 Players[Player_num].invulnerable_time = 0;
1964 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1965 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1966 Players[Player_num].flags = oflags;
1967 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1968 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1969 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1970 Newdemo_players_cloaked |= (1 << Player_num);
1972 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1973 Players[Player_num].cloak_time = 0;
1974 Newdemo_players_cloaked &= ~(1 << Player_num);
1976 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1977 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1978 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1979 Players[Player_num].invulnerable_time = 0;
1981 update_laser_weapon_info(); // in case of quad laser change
1985 case ND_EVENT_PLAYER_WEAPON: {
1986 sbyte weapon_type, weapon_num;
1989 nd_read_byte(&weapon_type);
1990 nd_read_byte(&weapon_num);
1991 nd_read_byte(&old_weapon);
1992 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1993 if (weapon_type == 0)
1994 Primary_weapon = (int)weapon_num;
1996 Secondary_weapon = (int)weapon_num;
1997 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1998 if (weapon_type == 0)
1999 Primary_weapon = (int)old_weapon;
2001 Secondary_weapon = (int)old_weapon;
2006 case ND_EVENT_EFFECT_BLOWUP: {
2012 //create a dummy object which will be the weapon that hits
2013 //the monitor. the blowup code wants to know who the parent of the
2014 //laser is, so create a laser whose parent is the player
2015 dummy.ctype.laser_info.parent_type = OBJ_PLAYER;
2017 nd_read_short(&segnum);
2018 nd_read_byte(&side);
2019 nd_read_vector(&pnt);
2020 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2021 check_effect_blowup(&(Segments[segnum]), side, &pnt, &dummy, 0);
2025 case ND_EVENT_HOMING_DISTANCE: {
2028 nd_read_short(&distance);
2029 Players[Player_num].homing_object_dist = i2f((int)(distance << 16));
2033 case ND_EVENT_LETTERBOX:
2034 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2035 saved_letter_cockpit = Cockpit_mode;
2036 select_cockpit(CM_LETTERBOX);
2037 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2038 select_cockpit(saved_letter_cockpit);
2041 case ND_EVENT_CHANGE_COCKPIT: {
2044 nd_read_int (&dummy);
2045 select_cockpit (dummy);
2049 case ND_EVENT_REARVIEW:
2050 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2051 saved_rearview_cockpit = Cockpit_mode;
2052 if (Cockpit_mode == CM_FULL_COCKPIT)
2053 select_cockpit(CM_REAR_VIEW);
2055 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2056 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2057 saved_rearview_cockpit = CM_FULL_COCKPIT;
2058 select_cockpit(saved_rearview_cockpit);
2063 case ND_EVENT_RESTORE_COCKPIT:
2064 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2065 saved_letter_cockpit = Cockpit_mode;
2066 select_cockpit(CM_LETTERBOX);
2067 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2068 select_cockpit(saved_letter_cockpit);
2072 case ND_EVENT_RESTORE_REARVIEW:
2073 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2074 saved_rearview_cockpit = Cockpit_mode;
2075 if (Cockpit_mode == CM_FULL_COCKPIT)
2076 select_cockpit(CM_REAR_VIEW);
2078 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2079 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2080 saved_rearview_cockpit = CM_FULL_COCKPIT;
2081 select_cockpit(saved_rearview_cockpit);
2087 case ND_EVENT_WALL_SET_TMAP_NUM1: {
2088 short seg, cseg, tmap;
2091 nd_read_short(&seg);
2092 nd_read_byte(&side);
2093 nd_read_short(&cseg);
2094 nd_read_byte(&cside);
2095 nd_read_short( &tmap );
2096 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD))
2097 Segments[seg].sides[side].tmap_num = Segments[cseg].sides[cside].tmap_num = tmap;
2101 case ND_EVENT_WALL_SET_TMAP_NUM2: {
2102 short seg, cseg, tmap;
2105 nd_read_short(&seg);
2106 nd_read_byte(&side);
2107 nd_read_short(&cseg);
2108 nd_read_byte(&cside);
2109 nd_read_short( &tmap );
2110 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) {
2111 Assert(tmap!=0 && Segments[seg].sides[side].tmap_num2!=0);
2112 Segments[seg].sides[side].tmap_num2 = Segments[cseg].sides[cside].tmap_num2 = tmap;
2117 case ND_EVENT_MULTI_CLOAK: {
2120 nd_read_byte(&pnum);
2121 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2122 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2123 Players[pnum].cloak_time = 0;
2124 Newdemo_players_cloaked &= ~(1 << pnum);
2125 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2126 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2127 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2128 Newdemo_players_cloaked |= (1 << pnum);
2133 case ND_EVENT_MULTI_DECLOAK: {
2136 nd_read_byte(&pnum);
2138 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2139 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2140 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2141 Newdemo_players_cloaked |= (1 << pnum);
2142 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2143 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2144 Players[pnum].cloak_time = 0;
2145 Newdemo_players_cloaked &= ~(1 << pnum);
2150 case ND_EVENT_MULTI_DEATH: {
2153 nd_read_byte(&pnum);
2154 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2155 Players[pnum].net_killed_total--;
2156 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2157 Players[pnum].net_killed_total++;
2162 case ND_EVENT_MULTI_KILL: {
2165 nd_read_byte(&pnum);
2166 nd_read_byte(&kill);
2167 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2168 Players[pnum].net_kills_total -= kill;
2169 if (Newdemo_game_mode & GM_TEAM)
2170 team_kills[get_team(pnum)] -= kill;
2171 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2172 Players[pnum].net_kills_total += kill;
2173 if (Newdemo_game_mode & GM_TEAM)
2174 team_kills[get_team(pnum)] += kill;
2176 Game_mode = Newdemo_game_mode;
2177 multi_sort_kill_list();
2178 Game_mode = GM_NORMAL;
2182 case ND_EVENT_MULTI_CONNECT: {
2183 sbyte pnum, new_player;
2184 int killed_total, kills_total;
2185 char new_callsign[CALLSIGN_LEN+1], old_callsign[CALLSIGN_LEN+1];
2187 nd_read_byte(&pnum);
2188 nd_read_byte(&new_player);
2190 nd_read_string(old_callsign);
2191 nd_read_int(&killed_total);
2192 nd_read_int(&kills_total);
2194 nd_read_string(new_callsign);
2195 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2196 Players[pnum].connected = 0;
2198 memcpy(Players[pnum].callsign, old_callsign, CALLSIGN_LEN+1);
2199 Players[pnum].net_killed_total = killed_total;
2200 Players[pnum].net_kills_total = kills_total;
2204 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2205 Players[pnum].connected = 1;
2206 Players[pnum].net_kills_total = 0;
2207 Players[pnum].net_killed_total = 0;
2208 memcpy(Players[pnum].callsign, new_callsign, CALLSIGN_LEN+1);
2215 case ND_EVENT_MULTI_RECONNECT: {
2218 nd_read_byte(&pnum);
2219 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2220 Players[pnum].connected = 0;
2221 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2222 Players[pnum].connected = 1;
2226 case ND_EVENT_MULTI_DISCONNECT: {
2229 nd_read_byte(&pnum);
2230 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2231 Players[pnum].connected = 1;
2232 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2233 Players[pnum].connected = 0;
2237 case ND_EVENT_MULTI_SCORE: {
2241 nd_read_byte(&pnum);
2242 nd_read_int(&score);
2243 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2244 Players[pnum].score -= score;
2245 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2246 Players[pnum].score += score;
2247 Game_mode = Newdemo_game_mode;
2248 multi_sort_kill_list();
2249 Game_mode = GM_NORMAL;
2254 case ND_EVENT_PLAYER_SCORE: {
2257 nd_read_int(&score);
2258 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2259 Players[Player_num].score -= score;
2260 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2261 Players[Player_num].score += score;
2266 case ND_EVENT_PRIMARY_AMMO: {
2267 short old_ammo, new_ammo;
2269 nd_read_short(&old_ammo);
2270 nd_read_short(&new_ammo);
2272 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2273 Players[Player_num].primary_ammo[Primary_weapon] = old_ammo;
2274 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2275 Players[Player_num].primary_ammo[Primary_weapon] = new_ammo;
2279 case ND_EVENT_SECONDARY_AMMO: {
2280 short old_ammo, new_ammo;
2282 nd_read_short(&old_ammo);
2283 nd_read_short(&new_ammo);
2285 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2286 Players[Player_num].secondary_ammo[Secondary_weapon] = old_ammo;
2287 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2288 Players[Player_num].secondary_ammo[Secondary_weapon] = new_ammo;
2292 case ND_EVENT_DOOR_OPENING: {
2296 nd_read_short(&segnum);
2297 nd_read_byte(&side);
2298 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2301 segment *segp, *csegp;
2303 segp = &Segments[segnum];
2304 csegp = &Segments[segp->children[side]];
2305 cside = find_connect_side(segp, csegp);
2306 anim_num = Walls[segp->sides[side].wall_num].clip_num;
2308 if (WallAnims[anim_num].flags & WCF_TMAP1) {
2309 segp->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[0];
2311 segp->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[0];
2317 case ND_EVENT_LASER_LEVEL: {
2318 sbyte old_level, new_level;
2320 nd_read_byte(&old_level);
2321 nd_read_byte(&new_level);
2322 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2323 Players[Player_num].laser_level = old_level;
2324 update_laser_weapon_info();
2325 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2326 Players[Player_num].laser_level = new_level;
2327 update_laser_weapon_info();
2332 case ND_EVENT_CLOAKING_WALL: {
2333 sbyte back_wall_num,front_wall_num,type,state,cloak_value;
2338 nd_read_byte(&front_wall_num);
2339 nd_read_byte(&back_wall_num);
2340 nd_read_byte(&type);
2341 nd_read_byte(&state);
2342 nd_read_byte(&cloak_value);
2348 Walls[front_wall_num].type = type;
2349 Walls[front_wall_num].state = state;
2350 Walls[front_wall_num].cloak_value = cloak_value;
2351 segp = &Segments[Walls[front_wall_num].segnum];
2352 sidenum = Walls[front_wall_num].sidenum;
2353 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2354 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2355 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2356 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2358 Walls[back_wall_num].type = type;
2359 Walls[back_wall_num].state = state;
2360 Walls[back_wall_num].cloak_value = cloak_value;
2361 segp = &Segments[Walls[back_wall_num].segnum];
2362 sidenum = Walls[back_wall_num].sidenum;
2363 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2364 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2365 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2366 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2371 case ND_EVENT_NEW_LEVEL: {
2372 sbyte new_level, old_level, loaded_level;
2374 nd_read_byte (&new_level);
2375 nd_read_byte (&old_level);
2376 if (Newdemo_vcr_state == ND_STATE_PAUSED)
2380 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2381 loaded_level = old_level;
2383 loaded_level = new_level;
2384 for (i = 0; i < MAX_PLAYERS; i++) {
2385 Players[i].cloak_time = 0;
2386 Players[i].flags &= ~PLAYER_FLAGS_CLOAKED;
2389 if ((loaded_level < Last_secret_level) || (loaded_level > Last_level)) {
2392 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2393 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2394 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2395 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2399 LoadLevel((int)loaded_level,1);
2400 Newdemo_cntrlcen_destroyed = 0;
2402 if (JustStartedPlayback)
2404 nd_read_int (&Num_walls);
2405 for (i=0;i<Num_walls;i++) // restore the walls
2407 nd_read_byte ((signed char *)&Walls[i].type);
2408 nd_read_byte ((signed char *)&Walls[i].flags);
2409 nd_read_byte ((signed char *)&Walls[i].state);
2411 seg = &Segments[Walls[i].segnum];
2412 side = Walls[i].sidenum;
2413 nd_read_short (&seg->sides[side].tmap_num);
2414 nd_read_short (&seg->sides[side].tmap_num2);
2417 if (Newdemo_game_mode & GM_CAPTURE)
2418 multi_apply_goal_textures ();
2420 JustStartedPlayback=0;
2424 // so says Rob H.!!! if (Newdemo_game_mode & GM_MULTI) {
2425 // so says Rob H.!!! for (i = 0; i < Num_walls; i++) {
2426 // so says Rob H.!!! if (Walls[i].type == WALL_BLASTABLE)
2427 // so says Rob H.!!! {
2428 // so says Rob H.!!! int a, n;
2429 // so says Rob H.!!! int side;
2430 // so says Rob H.!!! segment *seg;
2431 // so says Rob H.!!!
2432 // so says Rob H.!!! seg = &Segments[Walls[i].segnum];
2433 // so says Rob H.!!! side = Walls[i].sidenum;
2434 // so says Rob H.!!! a = Walls[i].clip_num;
2435 // so says Rob H.!!! n = WallAnims[a].num_frames;
2436 // so says Rob H.!!! seg->sides[side].tmap_num = WallAnims[a].frames[n-1];
2437 // so says Rob H.!!! Walls[i].flags |= WALL_BLASTED;
2438 // so says Rob H.!!! }
2439 // so says Rob H.!!! }
2440 // so says Rob H.!!! }
2442 reset_palette_add(); // get palette back to normal
2447 case ND_EVENT_EOF: {
2449 cfseek(infile, -1, SEEK_CUR); // get back to the EOF marker
2451 NewdemoFrameCount++;
2465 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_ERR_READING;
2466 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_DEMO_OLD_CORRUPT;
2467 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2473 void newdemo_goto_beginning()
2475 //if (NewdemoFrameCount == 0)
2477 cfseek(infile, 0, SEEK_SET);
2478 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2479 if (newdemo_read_demo_start(0))
2480 newdemo_stop_playback();
2481 if (newdemo_read_frame_information() == -1)
2482 newdemo_stop_playback();
2483 if (newdemo_read_frame_information() == -1)
2484 newdemo_stop_playback();
2485 Newdemo_vcr_state = ND_STATE_PAUSED;
2489 void newdemo_goto_end()
2491 short frame_length, byte_count, bshort;
2492 sbyte level, bbyte, laser_level;
2493 sbyte energy, shield, c;
2496 cfseek(infile, -2, SEEK_END);
2497 nd_read_byte(&level);
2499 if ((level < Last_secret_level) || (level > Last_level)) {
2502 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2503 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2504 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2505 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2506 newdemo_stop_playback();
2509 if (level != Current_level_num)
2512 cfseek(infile, -4, SEEK_END);
2513 nd_read_short(&byte_count);
2514 cfseek(infile, -2 - byte_count, SEEK_CUR);
2516 nd_read_short(&frame_length);
2517 loc = cftell(infile);
2518 if (Newdemo_game_mode & GM_MULTI)
2519 nd_read_byte(&Newdemo_players_cloaked);
2521 nd_read_byte(&bbyte);
2522 nd_read_byte(&bbyte);
2523 nd_read_short(&bshort);
2526 nd_read_byte(&energy);
2527 nd_read_byte(&shield);
2528 Players[Player_num].energy = i2f(energy);
2529 Players[Player_num].shields = i2f(shield);
2530 nd_read_int((int *)&(Players[Player_num].flags));
2531 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
2532 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2533 Newdemo_players_cloaked |= (1 << Player_num);
2535 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2536 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2537 nd_read_byte((sbyte *)&Primary_weapon);
2538 nd_read_byte((sbyte *)&Secondary_weapon);
2539 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
2540 nd_read_short((short *)&(Players[Player_num].primary_ammo[i]));
2541 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
2542 nd_read_short((short *)&(Players[Player_num].secondary_ammo[i]));
2543 nd_read_byte(&laser_level);
2544 if (laser_level != Players[Player_num].laser_level) {
2545 Players[Player_num].laser_level = laser_level;
2546 update_laser_weapon_info();
2549 if (Newdemo_game_mode & GM_MULTI) {
2552 // see newdemo_read_start_demo for explanation of
2553 // why this is commented out
2554 // nd_read_byte((sbyte *)&N_players);
2555 for (i = 0; i < N_players; i++) {
2556 nd_read_string(Players[i].callsign);
2557 nd_read_byte(&(Players[i].connected));
2558 if (Newdemo_game_mode & GM_MULTI_COOP) {
2559 nd_read_int(&(Players[i].score));
2561 nd_read_short((short *)&(Players[i].net_killed_total));
2562 nd_read_short((short *)&(Players[i].net_kills_total));
2566 nd_read_int(&(Players[Player_num].score));
2569 cfseek(infile, loc, SEEK_SET);
2570 cfseek(infile, -frame_length, SEEK_CUR);
2571 nd_read_int(&NewdemoFrameCount); // get the frame count
2572 NewdemoFrameCount--;
2573 cfseek(infile, 4, SEEK_CUR);
2574 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2575 newdemo_read_frame_information(); // then the frame information
2576 Newdemo_vcr_state = ND_STATE_PAUSED;
2580 void newdemo_back_frames(int frames)
2582 short last_frame_length;
2585 for (i = 0; i < frames; i++)
2587 cfseek(infile, -10, SEEK_CUR);
2588 nd_read_short(&last_frame_length);
2589 cfseek(infile, 8 - last_frame_length, SEEK_CUR);
2591 if (!Newdemo_at_eof && newdemo_read_frame_information() == -1) {
2592 newdemo_stop_playback();
2598 cfseek(infile, -10, SEEK_CUR);
2599 nd_read_short(&last_frame_length);
2600 cfseek(infile, 8 - last_frame_length, SEEK_CUR);
2606 * routine to interpolate the viewer position. the current position is
2607 * stored in the Viewer object. Save this position, and read the next
2608 * frame to get all objects read in. Calculate the delta playback and
2609 * the delta recording frame times between the two frames, then intepolate
2610 * the viewers position accordingly. nd_recorded_time is the time that it
2611 * took the recording to render the frame that we are currently looking
2615 void interpolate_frame(fix d_play, fix d_recorded)
2617 int i, j, num_cur_objs;
2621 factor = fixdiv(d_play, d_recorded);
2625 num_cur_objs = Highest_object_index;
2626 cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1));
2627 if (cur_objs == NULL) {
2628 mprintf((0,"Couldn't get %d bytes for cur_objs in interpolate_frame\n", sizeof(object) * num_cur_objs));
2632 for (i = 0; i <= num_cur_objs; i++)
2633 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2635 Newdemo_vcr_state = ND_STATE_PAUSED;
2636 if (newdemo_read_frame_information() == -1) {
2638 newdemo_stop_playback();
2642 for (i = 0; i <= num_cur_objs; i++) {
2643 for (j = 0; j <= Highest_object_index; j++) {
2644 if (cur_objs[i].signature == Objects[j].signature) {
2645 sbyte render_type = cur_objs[i].render_type;
2646 //fix delta_p, delta_h, delta_b;
2647 fix delta_x, delta_y, delta_z;
2648 //vms_angvec cur_angles, dest_angles;
2650 // Extract the angles from the object orientation matrix.
2651 // Some of this code taken from ai_turn_towards_vector
2652 // Don't do the interpolation on certain render types which don't use an orientation matrix
2654 if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) {
2656 vms_vector fvec1, fvec2, rvec1, rvec2;
2659 fvec1 = cur_objs[i].orient.fvec;
2660 vm_vec_scale(&fvec1, F1_0-factor);
2661 fvec2 = Objects[j].orient.fvec;
2662 vm_vec_scale(&fvec2, factor);
2663 vm_vec_add2(&fvec1, &fvec2);
2664 mag1 = vm_vec_normalize_quick(&fvec1);
2665 if (mag1 > F1_0/256) {
2666 rvec1 = cur_objs[i].orient.rvec;
2667 vm_vec_scale(&rvec1, F1_0-factor);
2668 rvec2 = Objects[j].orient.rvec;
2669 vm_vec_scale(&rvec2, factor);
2670 vm_vec_add2(&rvec1, &rvec2);
2671 vm_vec_normalize_quick(&rvec1); // Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1
2672 vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2675 //--old new way -- vms_vector fvec1, fvec2, rvec1, rvec2;
2677 //--old new way -- fvec1 = cur_objs[i].orient.fvec;
2678 //--old new way -- vm_vec_scale(&fvec1, F1_0-factor);
2679 //--old new way -- fvec2 = Objects[j].orient.fvec;
2680 //--old new way -- vm_vec_scale(&fvec2, factor);
2681 //--old new way -- vm_vec_add2(&fvec1, &fvec2);
2682 //--old new way -- vm_vec_normalize_quick(&fvec1);
2684 //--old new way -- rvec1 = cur_objs[i].orient.rvec;
2685 //--old new way -- vm_vec_scale(&rvec1, F1_0-factor);
2686 //--old new way -- rvec2 = Objects[j].orient.rvec;
2687 //--old new way -- vm_vec_scale(&rvec2, factor);
2688 //--old new way -- vm_vec_add2(&rvec1, &rvec2);
2689 //--old new way -- vm_vec_normalize_quick(&rvec1);
2691 //--old new way -- vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2693 // -- old fashioned way -- vm_extract_angles_matrix(&cur_angles, &(cur_objs[i].orient));
2694 // -- old fashioned way -- vm_extract_angles_matrix(&dest_angles, &(Objects[j].orient));
2695 // -- old fashioned way --
2696 // -- old fashioned way -- delta_p = (dest_angles.p - cur_angles.p);
2697 // -- old fashioned way -- delta_h = (dest_angles.h - cur_angles.h);
2698 // -- old fashioned way -- delta_b = (dest_angles.b - cur_angles.b);
2699 // -- old fashioned way --
2700 // -- old fashioned way -- if (delta_p != 0) {
2701 // -- old fashioned way -- if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
2702 // -- old fashioned way -- if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
2703 // -- old fashioned way -- delta_p = fixmul(delta_p, factor);
2704 // -- old fashioned way -- cur_angles.p += delta_p;
2705 // -- old fashioned way -- }
2706 // -- old fashioned way -- if (delta_h != 0) {
2707 // -- old fashioned way -- if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
2708 // -- old fashioned way -- if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
2709 // -- old fashioned way -- delta_h = fixmul(delta_h, factor);
2710 // -- old fashioned way -- cur_angles.h += delta_h;
2711 // -- old fashioned way -- }
2712 // -- old fashioned way -- if (delta_b != 0) {
2713 // -- old fashioned way -- if (delta_b > F1_0/2) delta_b = dest_angles.b - cur_angles.b - F1_0;
2714 // -- old fashioned way -- if (delta_b < -F1_0/2) delta_b = dest_angles.b - cur_angles.b + F1_0;
2715 // -- old fashioned way -- delta_b = fixmul(delta_b, factor);
2716 // -- old fashioned way -- cur_angles.b += delta_b;
2717 // -- old fashioned way -- }
2720 // Interpolate the object position. This is just straight linear
2723 delta_x = Objects[j].pos.x - cur_objs[i].pos.x;
2724 delta_y = Objects[j].pos.y - cur_objs[i].pos.y;
2725 delta_z = Objects[j].pos.z - cur_objs[i].pos.z;
2727 delta_x = fixmul(delta_x, factor);
2728 delta_y = fixmul(delta_y, factor);
2729 delta_z = fixmul(delta_z, factor);
2731 cur_objs[i].pos.x += delta_x;
2732 cur_objs[i].pos.y += delta_y;
2733 cur_objs[i].pos.z += delta_z;
2735 // -- old fashioned way --// stuff the new angles back into the object structure
2736 // -- old fashioned way -- vm_angles_2_matrix(&(cur_objs[i].orient), &cur_angles);
2741 // get back to original position in the demo file. Reread the current
2742 // frame information again to reset all of the object stuff not covered
2743 // with Highest_object_index and the object array (previously rendered
2744 // objects, etc....)
2746 newdemo_back_frames(1);
2747 newdemo_back_frames(1);
2748 if (newdemo_read_frame_information() == -1)
2749 newdemo_stop_playback();
2750 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2752 for (i = 0; i <= num_cur_objs; i++)
2753 memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object));
2754 Highest_object_index = num_cur_objs;
2758 void newdemo_playback_one_frame()
2760 int frames_back, i, level;
2761 static fix base_interpol_time = 0;
2762 static fix d_recorded = 0;
2764 for (i = 0; i < MAX_PLAYERS; i++)
2765 if (Newdemo_players_cloaked & (1 << i))
2766 Players[i].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2768 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2769 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2771 if (Newdemo_vcr_state == ND_STATE_PAUSED) // render a frame or not
2774 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2775 DoJasonInterpolate(nd_recorded_time);
2777 Control_center_destroyed = 0;
2778 Countdown_seconds_left = -1;
2779 PALETTE_FLASH_SET(0,0,0); //clear flash
2781 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2783 level = Current_level_num;
2784 if (NewdemoFrameCount == 0)
2786 else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (NewdemoFrameCount < 10)) {
2787 newdemo_goto_beginning();
2790 if (Newdemo_vcr_state == ND_STATE_REWINDING)
2794 if (Newdemo_at_eof) {
2795 cfseek(infile, 11, SEEK_CUR);
2797 newdemo_back_frames(frames_back);
2799 if (level != Current_level_num)
2800 newdemo_pop_ctrlcen_triggers();
2802 if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) {
2803 if (level != Current_level_num)
2804 newdemo_back_frames(1);
2805 Newdemo_vcr_state = ND_STATE_PAUSED;
2808 else if (Newdemo_vcr_state == ND_STATE_FASTFORWARD) {
2809 if (!Newdemo_at_eof)
2811 for (i = 0; i < 10; i++)
2813 if (newdemo_read_frame_information() == -1)
2816 Newdemo_vcr_state = ND_STATE_PAUSED;
2818 newdemo_stop_playback();
2824 Newdemo_vcr_state = ND_STATE_PAUSED;
2826 else if (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD) {
2827 if (!Newdemo_at_eof) {
2828 level = Current_level_num;
2829 if (newdemo_read_frame_information() == -1) {
2830 if (!Newdemo_at_eof)
2831 newdemo_stop_playback();
2833 if (level != Current_level_num) {
2834 if (newdemo_read_frame_information() == -1) {
2835 if (!Newdemo_at_eof)
2836 newdemo_stop_playback();
2839 Newdemo_vcr_state = ND_STATE_PAUSED;
2841 Newdemo_vcr_state = ND_STATE_PAUSED;
2845 // First, uptate the total playback time to date. Then we check to see
2846 // if we need to change the playback style to interpolate frames or
2847 // skip frames based on where the playback time is relative to the
2850 if (NewdemoFrameCount <= 0)
2851 nd_playback_total = nd_recorded_total; // baseline total playback time
2853 nd_playback_total += FrameTime;
2854 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2855 if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) {
2856 playback_style = INTERPOLATE_PLAYBACK;
2857 nd_playback_total = nd_recorded_total + FrameTime; // baseline playback time
2858 base_interpol_time = nd_recorded_total;
2859 d_recorded = nd_recorded_time; // baseline delta recorded
2861 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2862 if (nd_playback_total > nd_recorded_total)
2863 playback_style = SKIP_PLAYBACK;
2866 if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) {
2869 if (nd_recorded_total - nd_playback_total < FrameTime) {
2870 d_recorded = nd_recorded_total - nd_playback_total;
2872 while (nd_recorded_total - nd_playback_total < FrameTime) {
2874 int i, j, num_objs, level;
2876 num_objs = Highest_object_index;
2877 cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1));
2878 if (cur_objs == NULL) {
2879 Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs);
2882 for (i = 0; i <= num_objs; i++)
2883 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2885 level = Current_level_num;
2886 if (newdemo_read_frame_information() == -1) {
2888 newdemo_stop_playback();
2891 if (level != Current_level_num) {
2893 if (newdemo_read_frame_information() == -1)
2894 newdemo_stop_playback();
2898 // for each new object in the frame just read in, determine if there is
2899 // a corresponding object that we have been interpolating. If so, then
2900 // copy that interpolated object to the new Objects array so that the
2901 // interpolated position and orientation can be preserved.
2903 for (i = 0; i <= num_objs; i++) {
2904 for (j = 0; j <= Highest_object_index; j++) {
2905 if (cur_objs[i].signature == Objects[j].signature) {
2906 memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix));
2907 memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector));
2913 d_recorded += nd_recorded_time;
2914 base_interpol_time = nd_playback_total - FrameTime;
2918 d_play = nd_playback_total - base_interpol_time;
2919 interpolate_frame(d_play, d_recorded);
2923 //mprintf ((0, "*"));
2924 if (newdemo_read_frame_information() == -1) {
2925 newdemo_stop_playback();
2928 if (playback_style == SKIP_PLAYBACK) {
2929 //mprintf ((0, "."));
2930 while (nd_playback_total > nd_recorded_total) {
2931 if (newdemo_read_frame_information() == -1) {
2932 newdemo_stop_playback();
2941 void newdemo_start_recording()
2944 Newdemo_size=GetFreeDiskSpace();
2945 mprintf((0, "Free space = %d\n", Newdemo_size));
2947 Newdemo_size = GetDiskFree();
2950 Newdemo_size -= 100000;
2952 if ((Newdemo_size+100000) < 2000000000) {
2953 if (((int)(Newdemo_size)) < 500000) {
2955 nm_messagebox(NULL, 1, TXT_OK, TXT_DEMO_NO_SPACE);
2957 nm_messagebox(NULL, 1, TXT_OK, "Not enough space on current\ndrive to start demo recording.");
2963 Newdemo_num_written = 0;
2965 Newdemo_state = ND_STATE_RECORDING;
2966 outfile = cfopen(DEMO_FILENAME, "wb");
2968 #if !defined(MACINTOSH) && !defined(_WIN32_WCE)
2969 if (outfile == NULL && errno == ENOENT) { //dir doesn't exist?
2971 if (outfile == NULL) { //dir doesn't exist and no errno on mac!
2973 cfile_mkdir(DEMO_DIR); //try making directory
2974 outfile = cfopen(DEMO_FILENAME, "wb");
2977 if (outfile == NULL)
2979 nm_messagebox(NULL, 1, TXT_OK, "Cannot open demo temp file");
2980 Newdemo_state = ND_STATE_NORMAL;
2983 newdemo_record_start_demo();
2987 char demoname_allowed_chars[] = "azAZ09__--";
2988 void newdemo_stop_recording()
2992 static char filename[15] = "", *s;
2993 static sbyte tmpcnt = 0;
2995 char fullname[15+FILENAME_LEN] = DEMO_DIR;
2996 unsigned short byte_count = 0;
3000 nd_write_byte(ND_EVENT_EOF);
3001 nd_write_short(frame_bytes_written - 1);
3002 if (Game_mode & GM_MULTI) {
3003 for (l = 0; l < N_players; l++) {
3004 if (Players[l].flags & PLAYER_FLAGS_CLOAKED)
3005 cloaked |= (1 << l);
3007 nd_write_byte(cloaked);
3008 nd_write_byte(ND_EVENT_EOF);
3010 nd_write_short(ND_EVENT_EOF);
3012 nd_write_short(ND_EVENT_EOF);
3013 nd_write_int(ND_EVENT_EOF);
3015 byte_count += 10; // from frame_bytes_written
3017 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
3018 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
3019 nd_write_int(Players[Player_num].flags); // be sure players flags are set
3020 nd_write_byte((sbyte)Primary_weapon);
3021 nd_write_byte((sbyte)Secondary_weapon);
3024 for (l = 0; l < MAX_PRIMARY_WEAPONS; l++)
3025 nd_write_short((short)Players[Player_num].primary_ammo[l]);
3027 for (l = 0; l < MAX_SECONDARY_WEAPONS; l++)
3028 nd_write_short((short)Players[Player_num].secondary_ammo[l]);
3029 byte_count += (sizeof(short) * (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS));
3031 nd_write_byte(Players[Player_num].laser_level);
3034 if (Game_mode & GM_MULTI) {
3035 nd_write_byte((sbyte)N_players);
3037 for (l = 0; l < N_players; l++) {
3038 nd_write_string(Players[l].callsign);
3039 byte_count += (strlen(Players[l].callsign) + 2);
3040 nd_write_byte(Players[l].connected);
3041 if (Game_mode & GM_MULTI_COOP) {
3042 nd_write_int(Players[l].score);
3045 nd_write_short((short)Players[l].net_killed_total);
3046 nd_write_short((short)Players[l].net_kills_total);
3051 nd_write_int(Players[Player_num].score);
3054 nd_write_short(byte_count);
3056 nd_write_byte(Current_level_num);
3057 nd_write_byte(ND_EVENT_EOF);
3059 l = cftell(outfile);
3062 Newdemo_state = ND_STATE_NORMAL;
3063 gr_palette_load( gr_palette );
3065 if (filename[0] != '\0') {
3066 int num, i = strlen(filename) - 1;
3069 while (isdigit(filename[i])) {
3075 num = atoi(&(filename[i]));
3078 sprintf (newfile, "%s%d", filename, num);
3079 strncpy(filename, newfile, 8);
3086 Newmenu_allowed_chars = demoname_allowed_chars;
3087 if (!Newdemo_no_space) {
3088 m[0].type=NM_TYPE_INPUT; m[0].text_len = 8; m[0].text = filename;
3089 exit = newmenu_do( NULL, TXT_SAVE_DEMO_AS, 1, &(m[0]), NULL );
3090 } else if (Newdemo_no_space == 1) {
3091 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_BAD;
3092 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3093 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3094 } else if (Newdemo_no_space == 2) {
3095 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_NOSPACE;
3096 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3097 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3099 Newmenu_allowed_chars = NULL;
3101 if (exit == -2) { // got bumped out from network menu
3102 char save_file[7+FILENAME_LEN];
3104 if (filename[0] != '\0') {
3105 strcpy(save_file, DEMO_DIR);
3106 strcat(save_file, filename);
3107 strcat(save_file, ".dem");
3109 sprintf (save_file, "%stmp%d.dem", DEMO_DIR, tmpcnt++);
3110 cfile_delete(save_file);
3111 cfile_rename(DEMO_FILENAME, save_file);
3114 if (exit == -1) { // pressed ESC
3115 cfile_delete(DEMO_FILENAME); // might as well remove the file
3116 return; // return without doing anything
3119 if (filename[0]==0) //null string
3122 //check to make sure name is ok
3123 for (s=filename;*s;s++)
3124 if (!isalnum(*s) && *s!='_') {
3125 nm_messagebox1(NULL, NULL,1,TXT_CONTINUE, TXT_DEMO_USE_LETTERS);
3129 if (Newdemo_no_space)
3130 strcat(fullname, m[1].text);
3132 strcat(fullname, m[0].text);
3133 strcat(fullname, ".dem");
3134 cfile_delete(fullname);
3135 cfile_rename(DEMO_FILENAME, fullname);
3139 extern char AltHogDir[64];
3140 extern char AltHogdir_initialized;
3142 //returns the number of demo files on the disk
3143 int newdemo_count_demos()
3145 FILEFINDSTRUCT find;
3148 if( !FileFindFirst( DEMO_DIR "*.dem", &find ) ) {
3151 } while( !FileFindNext( &find ) );
3155 if ( AltHogdir_initialized ) {
3156 char search_name[PATH_MAX + 5];
3157 strcpy(search_name, AltHogDir);
3158 strcat(search_name, "/" DEMO_DIR "*.dem");
3159 if( !FileFindFirst( search_name, &find ) ) {
3162 } while( !FileFindNext( &find ) );
3170 void newdemo_start_playback(char * filename)
3172 FILEFINDSTRUCT find;
3174 char filename2[PATH_MAX+FILENAME_LEN] = DEMO_DIR;
3177 change_playernum_to(0);
3179 First_time_playback=1;
3180 JasonPlaybackTotal=0;
3182 if (filename==NULL) {
3183 // Randomly pick a filename
3184 int NumFiles = 0, RandFileNum;
3187 NumFiles = newdemo_count_demos();
3189 if ( NumFiles == 0 ) {
3190 return; // No files found!
3192 RandFileNum = d_rand() % NumFiles;
3194 if( !FileFindFirst( DEMO_DIR "*.dem", &find ) ) {
3196 if ( NumFiles==RandFileNum ) {
3197 filename = (char *)&find.name;
3201 } while( !FileFindNext( &find ) );
3205 if ( filename == NULL && AltHogdir_initialized ) {
3206 char search_name[PATH_MAX + 5];
3207 strcpy(search_name, AltHogDir);
3208 strcat(search_name, "/" DEMO_DIR "*.dem");
3209 if( !FileFindFirst( search_name, &find ) ) {
3211 if ( NumFiles==RandFileNum ) {
3212 filename = (char *)&find.name;
3216 } while( !FileFindNext( &find ) );
3221 if ( filename==NULL) return;
3227 strcat(filename2,filename);
3229 infile = cfopen(filename2, "rb");
3232 mprintf( (0, "Error reading '%s'\n", filename ));
3238 change_playernum_to(0); // force playernum to 0
3240 strncpy(nd_save_callsign, Players[Player_num].callsign, CALLSIGN_LEN);
3241 Viewer = ConsoleObject = &Objects[0]; // play properly as if console player
3242 if (newdemo_read_demo_start(rnd_demo)) {
3247 Game_mode = GM_NORMAL;
3248 Newdemo_state = ND_STATE_PLAYBACK;
3249 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3250 Newdemo_old_cockpit = Cockpit_mode;
3251 Newdemo_size = cfilelength(infile);
3254 NewdemoFrameCount = 0;
3255 Newdemo_players_cloaked = 0;
3256 playback_style = NORMAL_PLAYBACK;
3257 Function_mode = FMODE_GAME;
3258 Cockpit_3d_view[0] = CV_NONE; //turn off 3d views on cockpit
3259 Cockpit_3d_view[1] = CV_NONE; //turn off 3d views on cockpit
3260 newdemo_playback_one_frame(); // this one loads new level
3261 newdemo_playback_one_frame(); // get all of the objects to renderb game
3264 void newdemo_stop_playback()
3267 Newdemo_state = ND_STATE_NORMAL;
3269 change_playernum_to(0); //this is reality
3271 strncpy(Players[Player_num].callsign, nd_save_callsign, CALLSIGN_LEN);
3272 Cockpit_mode = Newdemo_old_cockpit;
3273 Game_mode = GM_GAME_OVER;
3274 Function_mode = FMODE_MENU;
3275 longjmp(LeaveGame,0); // Exit game loop
3281 #define BUF_SIZE 16384
3283 void newdemo_strip_frames(char *outname, int bytes_to_strip)
3287 int total_size, bytes_done, read_elems, bytes_back;
3288 int trailer_start, loc1, loc2, stop_loc, bytes_to_read;
3289 short last_frame_length;
3292 total_size = cfilelength(infile);
3293 outfile = cfopen(outname, "wb");
3294 if (outfile == NULL) {
3297 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't open output file";
3298 newmenu_do( NULL, NULL, 1, m, NULL );
3299 newdemo_stop_playback();
3302 buf = d_malloc(BUF_SIZE);
3306 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't malloc output buffer";
3307 newmenu_do( NULL, NULL, 1, m, NULL );
3309 newdemo_stop_playback();
3313 trailer_start = cftell(infile);
3314 cfseek(infile, 11, SEEK_CUR);
3316 while (bytes_back < bytes_to_strip) {
3317 loc1 = cftell(infile);
3318 //cfseek(infile, -10, SEEK_CUR);
3319 //nd_read_short(&last_frame_length);
3320 //cfseek(infile, 8 - last_frame_length, SEEK_CUR);
3321 newdemo_back_frames(1);
3322 loc2 = cftell(infile);
3323 bytes_back += (loc1 - loc2);
3325 cfseek(infile, -10, SEEK_CUR);
3326 nd_read_short(&last_frame_length);
3327 cfseek(infile, -3, SEEK_CUR);
3328 stop_loc = cftell(infile);
3329 cfseek(infile, 0, SEEK_SET);
3330 while (stop_loc > 0) {
3331 if (stop_loc < BUF_SIZE)
3332 bytes_to_read = stop_loc;
3334 bytes_to_read = BUF_SIZE;
3335 read_elems = cfread(buf, 1, bytes_to_read, infile);
3336 cfwrite(buf, 1, read_elems, outfile);
3337 stop_loc -= read_elems;
3339 stop_loc = cftell(outfile);
3340 cfseek(infile, trailer_start, SEEK_SET);
3341 while ((read_elems = cfread(buf, 1, BUF_SIZE, infile)) != 0)
3342 cfwrite(buf, 1, read_elems, outfile);
3343 cfseek(outfile, stop_loc, SEEK_SET);
3344 cfseek(outfile, 1, SEEK_CUR);
3345 cfwrite(&last_frame_length, 2, 1, outfile);
3347 newdemo_stop_playback();
3353 object DemoRightExtra,DemoLeftExtra;
3354 ubyte DemoDoRight=0,DemoDoLeft=0;
3356 void nd_render_extras (ubyte which,object *obj)
3359 ubyte type=which&15;
3363 Int3(); // how'd we get here?
3364 do_cockpit_window_view(w,NULL,0,WBU_WEAPON,NULL);
3370 memcpy (&DemoRightExtra,obj,sizeof(object)); DemoDoRight=type;
3374 memcpy (&DemoLeftExtra,obj,sizeof(object)); DemoDoLeft=type;
3379 void DoJasonInterpolate (fix recorded_time)
3383 JasonPlaybackTotal+=FrameTime;
3385 if (!First_time_playback)
3387 // get the difference between the recorded time and the playback time
3388 the_delay=(recorded_time - FrameTime);
3389 //mprintf ((0,"The delay=%d\n", f2i(the_delay)));
3390 if (the_delay >= f0_0)
3393 timer_delay(the_delay);
3398 while (JasonPlaybackTotal > nd_recorded_total)
3399 if (newdemo_read_frame_information() == -1)
3401 newdemo_stop_playback();
3405 //the_delay = nd_recorded_total - JasonPlaybackTotal;
3406 //if (the_delay > f0_0)
3407 // timer_delay(the_delay);
3412 First_time_playback=0;
3416 #pragma global_optimizer reset