2 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
3 SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO
4 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
5 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
6 IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
7 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
8 FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
9 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS
10 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
11 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED.
16 * Code to make a complete demo playback system.
28 #include <string.h> // for memset
32 #include <ctype.h> /* for isdigit */
36 #include <sys/types.h>
55 #include "editor/editor.h"
60 #pragma global_optimizer off // pretty much sucks...need to look into this
63 void DoJasonInterpolate (fix recorded_time);
65 //#include "nocfile.h"
67 //Does demo start automatically?
70 sbyte WasRecorded [MAX_OBJECTS];
71 sbyte ViewWasRecorded[MAX_OBJECTS];
72 sbyte RenderingWasRecorded[32];
74 #define ND_EVENT_EOF 0 // EOF
75 #define ND_EVENT_START_DEMO 1 // Followed by 16 character, NULL terminated filename of .SAV file to use
76 #define ND_EVENT_START_FRAME 2 // Followed by integer frame number, then a fix FrameTime
77 #define ND_EVENT_VIEWER_OBJECT 3 // Followed by an object structure
78 #define ND_EVENT_RENDER_OBJECT 4 // Followed by an object structure
79 #define ND_EVENT_SOUND 5 // Followed by int soundum
80 #define ND_EVENT_SOUND_ONCE 6 // Followed by int soundum
81 #define ND_EVENT_SOUND_3D 7 // Followed by int soundum, int angle, int volume
82 #define ND_EVENT_WALL_HIT_PROCESS 8 // Followed by int segnum, int side, fix damage
83 #define ND_EVENT_TRIGGER 9 // Followed by int segnum, int side, int objnum
84 #define ND_EVENT_HOSTAGE_RESCUED 10 // Followed by int hostage_type
85 #define ND_EVENT_SOUND_3D_ONCE 11 // Followed by int soundum, int angle, int volume
86 #define ND_EVENT_MORPH_FRAME 12 // Followed by ? data
87 #define ND_EVENT_WALL_TOGGLE 13 // Followed by int seg, int side
88 #define ND_EVENT_HUD_MESSAGE 14 // Followed by char size, char * string (+null)
89 #define ND_EVENT_CONTROL_CENTER_DESTROYED 15 // Just a simple flag
90 #define ND_EVENT_PALETTE_EFFECT 16 // Followed by short r,g,b
91 #define ND_EVENT_PLAYER_ENERGY 17 // followed by byte energy
92 #define ND_EVENT_PLAYER_SHIELD 18 // followed by byte shields
93 #define ND_EVENT_PLAYER_FLAGS 19 // followed by player flags
94 #define ND_EVENT_PLAYER_WEAPON 20 // followed by weapon type and weapon number
95 #define ND_EVENT_EFFECT_BLOWUP 21 // followed by segment, side, and pnt
96 #define ND_EVENT_HOMING_DISTANCE 22 // followed by homing distance
97 #define ND_EVENT_LETTERBOX 23 // letterbox mode for death seq.
98 #define ND_EVENT_RESTORE_COCKPIT 24 // restore cockpit after death
99 #define ND_EVENT_REARVIEW 25 // going to rear view mode
100 #define ND_EVENT_WALL_SET_TMAP_NUM1 26 // Wall changed
101 #define ND_EVENT_WALL_SET_TMAP_NUM2 27 // Wall changed
102 #define ND_EVENT_NEW_LEVEL 28 // followed by level number
103 #define ND_EVENT_MULTI_CLOAK 29 // followed by player num
104 #define ND_EVENT_MULTI_DECLOAK 30 // followed by player num
105 #define ND_EVENT_RESTORE_REARVIEW 31 // restore cockpit after rearview mode
106 #define ND_EVENT_MULTI_DEATH 32 // with player number
107 #define ND_EVENT_MULTI_KILL 33 // with player number
108 #define ND_EVENT_MULTI_CONNECT 34 // with player number
109 #define ND_EVENT_MULTI_RECONNECT 35 // with player number
110 #define ND_EVENT_MULTI_DISCONNECT 36 // with player number
111 #define ND_EVENT_MULTI_SCORE 37 // playernum / score
112 #define ND_EVENT_PLAYER_SCORE 38 // followed by score
113 #define ND_EVENT_PRIMARY_AMMO 39 // with old/new ammo count
114 #define ND_EVENT_SECONDARY_AMMO 40 // with old/new ammo count
115 #define ND_EVENT_DOOR_OPENING 41 // with segment/side
116 #define ND_EVENT_LASER_LEVEL 42 // no data
117 #define ND_EVENT_PLAYER_AFTERBURNER 43 // followed by byte old ab, current ab
118 #define ND_EVENT_CLOAKING_WALL 44 // info changing while wall cloaking
119 #define ND_EVENT_CHANGE_COCKPIT 45 // change the cockpit
120 #define ND_EVENT_START_GUIDED 46 // switch to guided view
121 #define ND_EVENT_END_GUIDED 47 // stop guided view/return to ship
122 #define ND_EVENT_SECRET_THINGY 48 // 0/1 = secret exit functional/non-functional
123 #define ND_EVENT_LINK_SOUND_TO_OBJ 49 // record digi_link_sound_to_object3
124 #define ND_EVENT_KILL_SOUND_TO_OBJ 50 // record digi_kill_sound_linked_to_object
127 #define NORMAL_PLAYBACK 0
128 #define SKIP_PLAYBACK 1
129 #define INTERPOLATE_PLAYBACK 2
130 #define INTERPOL_FACTOR (F1_0 + (F1_0/5))
132 #define DEMO_VERSION_D1_SHARE 5
133 #define DEMO_VERSION_D1 13
134 #define DEMO_VERSION_D2 15
135 #define DEMO_VERSION DEMO_VERSION_D2
137 #define DEMO_GAME_TYPE_D1_SHARE 1
138 #define DEMO_GAME_TYPE_D1 2
139 #define DEMO_GAME_TYPE_D2 3
140 #define DEMO_GAME_TYPE DEMO_GAME_TYPE_D2
142 #define DEMO_FILENAME DEMO_DIR "tmpdemo.dem"
144 #define DEMO_MAX_LEVELS 29
147 char nd_save_callsign[CALLSIGN_LEN+1];
148 int Newdemo_state = 0;
149 int Newdemo_vcr_state = 0;
150 int Newdemo_start_frame = -1;
151 unsigned int Newdemo_size;
152 int Newdemo_num_written;
153 int Newdemo_game_mode;
154 sbyte Newdemo_game_type;
155 int Newdemo_old_cockpit;
156 int Newdemo_is_d2demo = 0;
157 sbyte Newdemo_no_space;
158 sbyte Newdemo_at_eof;
159 sbyte Newdemo_do_interpolate = 0; // 1
160 sbyte Newdemo_players_cloaked;
161 sbyte Newdemo_warning_given = 0;
162 sbyte Newdemo_cntrlcen_destroyed = 0;
163 static sbyte nd_bad_read;
164 int NewdemoFrameCount;
165 short frame_bytes_written = 0;
166 fix nd_playback_total;
167 fix nd_recorded_total;
168 fix nd_recorded_time;
169 sbyte playback_style;
170 sbyte First_time_playback=1;
171 fix JasonPlaybackTotal=0;
175 PHYSFS_file *outfile = NULL;
177 int newdemo_get_percent_done() {
178 if ( Newdemo_state == ND_STATE_PLAYBACK ) {
179 return ((unsigned int)PHYSFS_tell(infile) * 100) / Newdemo_size;
181 if ( Newdemo_state == ND_STATE_RECORDING ) {
182 return (int)PHYSFS_tell(outfile);
187 #define VEL_PRECISION 12
189 void my_extract_shortpos(object *objp, shortpos *spp)
195 objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
196 objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
197 objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
199 objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
200 objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
201 objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
203 objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
204 objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
205 objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
207 segnum = spp->segment;
208 objp->segnum = segnum;
210 objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x;
211 objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y;
212 objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z;
214 objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION);
215 objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION);
216 objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION);
219 int newdemo_read( void *buffer, int elsize, int nelem )
222 num_read = (int)PHYSFS_read(infile, buffer, elsize, nelem);
223 if (num_read < nelem || PHYSFS_eof(infile))
229 int newdemo_find_object( int signature )
234 for (i=0; i<=Highest_object_index; i++, objp++ ) {
235 if ( (objp->type != OBJ_NONE) && (objp->signature == signature))
241 int newdemo_write( void *buffer, int elsize, int nelem )
243 int num_written, total_size;
245 total_size = elsize * nelem;
246 frame_bytes_written += total_size;
247 Newdemo_num_written += total_size;
248 Assert(outfile != NULL);
249 num_written = (int)PHYSFS_write(outfile, buffer, elsize, nelem);
250 //if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) {
251 // Newdemo_no_space=1;
252 // newdemo_stop_recording();
255 if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space)
257 if (num_written == nelem && !Newdemo_no_space)
261 newdemo_stop_recording();
266 * The next bunch of files taken from Matt's gamesave.c. We have to modify
267 * these since the demo must save more information about objects that
271 static void nd_write_byte(sbyte b)
273 newdemo_write(&b, 1, 1);
276 static void nd_write_short(short s)
278 newdemo_write(&s, 2, 1);
281 static void nd_write_int(int i)
283 newdemo_write(&i, 4, 1);
286 static void nd_write_string(char *str)
288 nd_write_byte(strlen(str) + 1);
289 newdemo_write(str, (int)strlen(str) + 1, 1);
292 static void nd_write_fix(fix f)
294 newdemo_write(&f, sizeof(fix), 1);
297 static void nd_write_fixang(fixang f)
299 newdemo_write(&f, sizeof(fixang), 1);
302 static void nd_write_vector(vms_vector *v)
309 static void nd_write_angvec(vms_angvec *v)
311 nd_write_fixang(v->p);
312 nd_write_fixang(v->b);
313 nd_write_fixang(v->h);
316 void nd_write_shortpos(object *obj)
322 create_shortpos(&sp, obj, 0);
324 render_type = obj->render_type;
325 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
326 for (i = 0; i < 9; i++)
327 nd_write_byte(sp.bytemat[i]);
328 for (i = 0; i < 9; i++) {
329 if (sp.bytemat[i] != 0)
333 Int3(); // contact Allender about this.
337 nd_write_short(sp.xo);
338 nd_write_short(sp.yo);
339 nd_write_short(sp.zo);
340 nd_write_short(sp.segment);
341 nd_write_short(sp.velx);
342 nd_write_short(sp.vely);
343 nd_write_short(sp.velz);
346 static void nd_read_byte(sbyte *b)
348 newdemo_read(b, 1, 1);
351 static void nd_read_short(short *s)
353 newdemo_read(s, 2, 1);
356 static void nd_read_int(int *i)
358 newdemo_read(i, 4, 1);
361 static void nd_read_string(char *str)
366 newdemo_read(str, len, 1);
369 static void nd_read_fix(fix *f)
371 newdemo_read(f, sizeof(fix), 1);
374 static void nd_read_fixang(fixang *f)
376 newdemo_read(f, sizeof(fixang), 1);
379 static void nd_read_vector(vms_vector *v)
381 nd_read_fix(&(v->x));
382 nd_read_fix(&(v->y));
383 nd_read_fix(&(v->z));
386 static void nd_read_angvec(vms_angvec *v)
388 nd_read_fixang(&(v->p));
389 nd_read_fixang(&(v->b));
390 nd_read_fixang(&(v->h));
393 static void nd_read_shortpos(object *obj)
399 render_type = obj->render_type;
400 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
401 for (i = 0; i < 9; i++)
402 nd_read_byte(&(sp.bytemat[i]));
405 nd_read_short(&(sp.xo));
406 nd_read_short(&(sp.yo));
407 nd_read_short(&(sp.zo));
408 nd_read_short(&(sp.segment));
409 nd_read_short(&(sp.velx));
410 nd_read_short(&(sp.vely));
411 nd_read_short(&(sp.velz));
413 my_extract_shortpos(obj, &sp);
414 if ((obj->id == VCLIP_MORPHING_ROBOT) && (render_type == RT_FIREBALL) && (obj->control_type == CT_EXPLOSION))
415 extract_orient_from_segment(&obj->orient,&Segments[obj->segnum]);
419 object *prev_obj=NULL; //ptr to last object read in
421 void nd_read_object(object *obj)
423 int* sig = &(obj->signature);
425 memset(obj, 0, sizeof(object));
428 * Do render type first, since with render_type == RT_NONE, we
429 * blow by all other object information
431 nd_read_byte((sbyte *) &(obj->render_type));
432 nd_read_byte((sbyte *) &(obj->type));
433 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
436 nd_read_byte((sbyte *) &(obj->id));
437 nd_read_byte((sbyte *) &(obj->flags));
438 nd_read_short((short *)sig);
439 nd_read_shortpos(obj);
441 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
444 obj->attached_obj = -1;
449 obj->control_type = CT_POWERUP;
450 obj->movement_type = MT_NONE;
451 obj->size = HOSTAGE_SIZE;
455 obj->control_type = CT_AI;
456 // (MarkA and MikeK said we should not do the crazy last secret stuff with multiple reactors...
457 // This necessary code is our vindication. --MK, 2/15/96)
458 if (obj->id != SPECIAL_REACTOR_ROBOT)
459 obj->movement_type = MT_PHYSICS;
461 obj->movement_type = MT_NONE;
462 obj->size = Polygon_models[Robot_info[obj->id].model_num].rad;
463 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
464 obj->rtype.pobj_info.subobj_flags = 0;
465 obj->ctype.ai_info.CLOAKED = (Robot_info[obj->id].cloak_type?1:0);
469 obj->control_type = CT_POWERUP;
470 nd_read_byte((sbyte *) &(obj->movement_type)); // might have physics movement
471 obj->size = Powerup_info[obj->id].size;
475 obj->control_type = CT_NONE;
476 obj->movement_type = MT_PHYSICS;
477 obj->size = Polygon_models[Player_ship->model_num].rad;
478 obj->rtype.pobj_info.model_num = Player_ship->model_num;
479 obj->rtype.pobj_info.subobj_flags = 0;
483 obj->control_type = CT_NONE;
484 obj->movement_type = MT_NONE;
485 obj->size = Polygon_models[obj->id].rad;
486 obj->rtype.pobj_info.model_num = obj->id;
487 obj->rtype.pobj_info.subobj_flags = 0;
491 nd_read_byte((sbyte *) &(obj->control_type));
492 nd_read_byte((sbyte *) &(obj->movement_type));
493 nd_read_fix(&(obj->size));
498 nd_read_vector(&(obj->last_pos));
499 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
500 nd_read_fix(&(obj->lifeleft));
505 obj->lifeleft = (fix)b;
506 // MWA old way -- won't work with big endian machines nd_read_byte((ubyte *)&(obj->lifeleft));
507 obj->lifeleft = (fix)((int)obj->lifeleft << 12);
510 if (obj->type == OBJ_ROBOT) {
511 if (Robot_info[obj->id].boss_flag) {
514 nd_read_byte(&cloaked);
515 obj->ctype.ai_info.CLOAKED = cloaked;
519 switch (obj->movement_type) {
522 nd_read_vector(&(obj->mtype.phys_info.velocity));
523 nd_read_vector(&(obj->mtype.phys_info.thrust));
527 nd_read_vector(&(obj->mtype.spin_rate));
537 switch (obj->control_type) {
541 nd_read_fix(&(obj->ctype.expl_info.spawn_time));
542 nd_read_fix(&(obj->ctype.expl_info.delete_time));
543 nd_read_short(&(obj->ctype.expl_info.delete_objnum));
545 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
547 if (obj->flags & OF_ATTACHED) { //attach to previous object
548 Assert(prev_obj!=NULL);
549 if (prev_obj->control_type == CT_EXPLOSION) {
550 if (prev_obj->flags & OF_ATTACHED && prev_obj->ctype.expl_info.attach_parent!=-1)
551 obj_attach(&Objects[prev_obj->ctype.expl_info.attach_parent],obj);
553 obj->flags &= ~OF_ATTACHED;
556 obj_attach(prev_obj,obj);
562 nd_read_fix(&(obj->ctype.light_info.intensity));
584 switch (obj->render_type) {
593 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
594 nd_read_int(&(obj->rtype.pobj_info.model_num));
595 if (Newdemo_game_type == DEMO_GAME_TYPE_D1_SHARE)
596 obj->rtype.pobj_info.model_num = D1Share_Polymodel_map[obj->rtype.pobj_info.model_num];
597 else if (Newdemo_game_type == DEMO_GAME_TYPE_D1)
598 obj->rtype.pobj_info.model_num = D1_Polymodel_map[obj->rtype.pobj_info.model_num];
599 else if (Newdemo_is_d2demo)
600 obj->rtype.pobj_info.model_num = D2Demo_Polymodel_map[obj->rtype.pobj_info.model_num];
601 if (obj->rtype.pobj_info.model_num < 0)
603 nd_read_int(&(obj->rtype.pobj_info.subobj_flags));
606 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
608 for (i=0;i<MAX_SUBMODELS;i++)
609 nd_read_angvec(&(obj->pobj_info.anim_angles[i]));
611 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
612 nd_read_angvec(&obj->rtype.pobj_info.anim_angles[i]);
617 obj->rtype.pobj_info.tmap_override = tmo;
620 obj->rtype.pobj_info.tmap_override = -1;
622 int xlated_tmo = tmap_xlate_table[tmo];
623 if (xlated_tmo < 0) {
624 //mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->pobj_info.model_num));
628 obj->rtype.pobj_info.tmap_override = xlated_tmo;
636 case RT_WEAPON_VCLIP:
639 nd_read_int(&(obj->rtype.vclip_info.vclip_num));
640 nd_read_fix(&(obj->rtype.vclip_info.frametime));
641 nd_read_byte(&(obj->rtype.vclip_info.framenum));
655 void nd_write_object(object *obj)
659 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
663 * Do render_type first so on read, we can make determination of
664 * what else to read in
666 nd_write_byte(obj->render_type);
667 nd_write_byte(obj->type);
668 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
671 nd_write_byte(obj->id);
672 nd_write_byte(obj->flags);
673 nd_write_short((short)obj->signature);
674 nd_write_shortpos(obj);
676 if ((obj->type != OBJ_HOSTAGE) && (obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_POWERUP) && (obj->type != OBJ_CLUTTER)) {
677 nd_write_byte(obj->control_type);
678 nd_write_byte(obj->movement_type);
679 nd_write_fix(obj->size);
681 if (obj->type == OBJ_POWERUP)
682 nd_write_byte(obj->movement_type);
684 nd_write_vector(&obj->last_pos);
686 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
687 nd_write_fix(obj->lifeleft);
689 life = (int)obj->lifeleft;
693 nd_write_byte((ubyte)life);
696 if (obj->type == OBJ_ROBOT) {
697 if (Robot_info[obj->id].boss_flag) {
698 if ((GameTime > Boss_cloak_start_time) && (GameTime < Boss_cloak_end_time))
705 switch (obj->movement_type) {
708 nd_write_vector(&obj->mtype.phys_info.velocity);
709 nd_write_vector(&obj->mtype.phys_info.thrust);
713 nd_write_vector(&obj->mtype.spin_rate);
723 switch (obj->control_type) {
729 nd_write_fix(obj->ctype.expl_info.spawn_time);
730 nd_write_fix(obj->ctype.expl_info.delete_time);
731 nd_write_short(obj->ctype.expl_info.delete_objnum);
739 nd_write_fix(obj->ctype.light_info.intensity);
746 case CT_SLEW: //the player is generally saved as slew
759 switch (obj->render_type) {
768 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
769 nd_write_int(obj->rtype.pobj_info.model_num);
770 nd_write_int(obj->rtype.pobj_info.subobj_flags);
773 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
775 for (i=0;i<MAX_SUBMODELS;i++)
776 nd_write_angvec(&obj->pobj_info.anim_angles[i]);
778 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
779 nd_write_angvec(&obj->rtype.pobj_info.anim_angles[i]);
781 nd_write_int(obj->rtype.pobj_info.tmap_override);
787 case RT_WEAPON_VCLIP:
790 nd_write_int(obj->rtype.vclip_info.vclip_num);
791 nd_write_fix(obj->rtype.vclip_info.frametime);
792 nd_write_byte(obj->rtype.vclip_info.framenum);
805 int JustStartedRecording=0,JustStartedPlayback=0;
807 void newdemo_record_start_demo()
812 nd_write_byte(ND_EVENT_START_DEMO);
813 nd_write_byte(DEMO_VERSION);
814 nd_write_byte(DEMO_GAME_TYPE);
815 nd_write_fix(GameTime);
818 if (Game_mode & GM_MULTI)
819 nd_write_int(Game_mode | (Player_num << 16));
822 // NOTE LINK TO ABOVE!!!
823 nd_write_int(Game_mode);
826 if (Game_mode & GM_TEAM) {
827 nd_write_byte(Netgame.team_vector);
828 nd_write_string(Netgame.team_name[0]);
829 nd_write_string(Netgame.team_name[1]);
832 if (Game_mode & GM_MULTI) {
833 nd_write_byte((sbyte)N_players);
834 for (i = 0; i < N_players; i++) {
835 nd_write_string(Players[i].callsign);
836 nd_write_byte(Players[i].connected);
838 if (Game_mode & GM_MULTI_COOP) {
839 nd_write_int(Players[i].score);
841 nd_write_short((short)Players[i].net_killed_total);
842 nd_write_short((short)Players[i].net_kills_total);
847 // NOTE LINK TO ABOVE!!!
848 nd_write_int(Players[Player_num].score);
850 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
851 nd_write_short((short)Players[Player_num].primary_ammo[i]);
853 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
854 nd_write_short((short)Players[Player_num].secondary_ammo[i]);
856 nd_write_byte((sbyte)Players[Player_num].laser_level);
858 // Support for missions added here
860 nd_write_string(Current_mission_filename);
862 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
863 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
864 nd_write_int(Players[Player_num].flags); // be sure players flags are set
865 nd_write_byte((sbyte)Primary_weapon);
866 nd_write_byte((sbyte)Secondary_weapon);
867 Newdemo_start_frame = FrameCount;
868 JustStartedRecording=1;
870 newdemo_set_new_level(Current_level_num);
875 void newdemo_record_start_frame(int frame_number, fix frame_time )
879 if (Newdemo_no_space) {
880 newdemo_stop_playback();
886 for (i=0;i<MAX_OBJECTS;i++)
889 ViewWasRecorded[i]=0;
892 RenderingWasRecorded[i]=0;
894 frame_number -= Newdemo_start_frame;
896 Assert(frame_number >= 0 );
898 nd_write_byte(ND_EVENT_START_FRAME);
899 nd_write_short(frame_bytes_written - 1); // from previous frame
900 frame_bytes_written=3;
901 nd_write_int(frame_number);
902 nd_write_int(frame_time);
907 void newdemo_record_render_object(object * obj)
909 if (ViewWasRecorded[OBJECT_NUMBER(obj)])
912 //if (obj==&Objects[Players[Player_num].objnum] && !Player_is_dead)
916 nd_write_byte(ND_EVENT_RENDER_OBJECT);
917 nd_write_object(obj);
921 extern ubyte RenderingType;
923 void newdemo_record_viewer_object(object * obj)
926 if (ViewWasRecorded[OBJECT_NUMBER(obj)] && (ViewWasRecorded[OBJECT_NUMBER(obj)] - 1) == RenderingType)
928 //if (WasRecorded[OBJECT_NUMBER(obj)])
930 if (RenderingWasRecorded[RenderingType])
933 ViewWasRecorded[OBJECT_NUMBER(obj)] = RenderingType + 1;
934 RenderingWasRecorded[RenderingType]=1;
936 nd_write_byte(ND_EVENT_VIEWER_OBJECT);
937 nd_write_byte(RenderingType);
938 nd_write_object(obj);
942 void newdemo_record_sound( int soundno )
945 nd_write_byte(ND_EVENT_SOUND);
946 nd_write_int( soundno );
950 //--unused-- void newdemo_record_sound_once( int soundno ) {
951 //--unused-- stop_time();
952 //--unused-- nd_write_byte( ND_EVENT_SOUND_ONCE );
953 //--unused-- nd_write_int( soundno );
954 //--unused-- start_time();
958 void newdemo_record_cockpit_change (int mode)
961 nd_write_byte (ND_EVENT_CHANGE_COCKPIT);
967 void newdemo_record_sound_3d( int soundno, int angle, int volume )
970 nd_write_byte( ND_EVENT_SOUND_3D );
971 nd_write_int( soundno );
972 nd_write_int( angle );
973 nd_write_int( volume );
977 void newdemo_record_sound_3d_once( int soundno, int angle, int volume )
980 nd_write_byte( ND_EVENT_SOUND_3D_ONCE );
981 nd_write_int( soundno );
982 nd_write_int( angle );
983 nd_write_int( volume );
988 void newdemo_record_link_sound_to_object3( int soundno, short objnum, fix max_volume, fix max_distance, int loop_start, int loop_end )
991 nd_write_byte( ND_EVENT_LINK_SOUND_TO_OBJ );
992 nd_write_int( soundno );
993 nd_write_int( Objects[objnum].signature );
994 nd_write_int( max_volume );
995 nd_write_int( max_distance );
996 nd_write_int( loop_start );
997 nd_write_int( loop_end );
1001 void newdemo_record_kill_sound_linked_to_object( int objnum )
1004 nd_write_byte( ND_EVENT_KILL_SOUND_TO_OBJ );
1005 nd_write_int( Objects[objnum].signature );
1010 void newdemo_record_wall_hit_process( int segnum, int side, int damage, int playernum )
1016 //playernum = playernum;
1017 nd_write_byte( ND_EVENT_WALL_HIT_PROCESS );
1018 nd_write_int( segnum );
1019 nd_write_int( side );
1020 nd_write_int( damage );
1021 nd_write_int( playernum );
1025 void newdemo_record_guided_start ()
1027 nd_write_byte (ND_EVENT_START_GUIDED);
1030 void newdemo_record_guided_end ()
1032 nd_write_byte (ND_EVENT_END_GUIDED);
1035 void newdemo_record_secret_exit_blown(int truth)
1038 nd_write_byte( ND_EVENT_SECRET_THINGY );
1039 nd_write_int( truth );
1043 void newdemo_record_trigger( int segnum, int side, int objnum,int shot )
1046 nd_write_byte( ND_EVENT_TRIGGER );
1047 nd_write_int( segnum );
1048 nd_write_int( side );
1049 nd_write_int( objnum );
1054 void newdemo_record_hostage_rescued( int hostage_number ) {
1056 nd_write_byte( ND_EVENT_HOSTAGE_RESCUED );
1057 nd_write_int( hostage_number );
1061 void newdemo_record_morph_frame(morph_data *md)
1065 nd_write_byte( ND_EVENT_MORPH_FRAME );
1067 newdemo_write( md->morph_vecs, sizeof(md->morph_vecs), 1 );
1068 newdemo_write( md->submodel_active, sizeof(md->submodel_active), 1 );
1069 newdemo_write( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 );
1071 nd_write_object( md->obj );
1075 void newdemo_record_wall_toggle( int segnum, int side )
1078 nd_write_byte( ND_EVENT_WALL_TOGGLE );
1079 nd_write_int( segnum );
1080 nd_write_int( side );
1084 void newdemo_record_control_center_destroyed()
1087 nd_write_byte( ND_EVENT_CONTROL_CENTER_DESTROYED );
1088 nd_write_int( Countdown_seconds_left );
1092 void newdemo_record_hud_message( char * message )
1095 nd_write_byte( ND_EVENT_HUD_MESSAGE );
1096 nd_write_string(message);
1100 void newdemo_record_palette_effect(short r, short g, short b )
1103 nd_write_byte( ND_EVENT_PALETTE_EFFECT );
1104 nd_write_short( r );
1105 nd_write_short( g );
1106 nd_write_short( b );
1110 void newdemo_record_player_energy(int old_energy, int energy)
1113 nd_write_byte( ND_EVENT_PLAYER_ENERGY );
1114 nd_write_byte((sbyte) old_energy);
1115 nd_write_byte((sbyte) energy);
1119 void newdemo_record_player_afterburner(fix old_afterburner, fix afterburner)
1122 nd_write_byte( ND_EVENT_PLAYER_AFTERBURNER );
1123 nd_write_byte((sbyte) (old_afterburner>>9));
1124 nd_write_byte((sbyte) (afterburner>>9));
1128 void newdemo_record_player_shields(int old_shield, int shield)
1131 nd_write_byte( ND_EVENT_PLAYER_SHIELD );
1132 nd_write_byte((sbyte)old_shield);
1133 nd_write_byte((sbyte)shield);
1137 void newdemo_record_player_flags(uint oflags, uint flags)
1140 nd_write_byte( ND_EVENT_PLAYER_FLAGS );
1141 nd_write_int(((short)oflags << 16) | (short)flags);
1145 void newdemo_record_player_weapon(int weapon_type, int weapon_num)
1148 nd_write_byte( ND_EVENT_PLAYER_WEAPON );
1149 nd_write_byte((sbyte)weapon_type);
1150 nd_write_byte((sbyte)weapon_num);
1152 nd_write_byte((sbyte)Secondary_weapon);
1154 nd_write_byte((sbyte)Primary_weapon);
1158 void newdemo_record_effect_blowup(short segment, int side, vms_vector *pnt)
1161 nd_write_byte (ND_EVENT_EFFECT_BLOWUP);
1162 nd_write_short(segment);
1163 nd_write_byte((sbyte)side);
1164 nd_write_vector(pnt);
1168 void newdemo_record_homing_distance(fix distance)
1171 nd_write_byte(ND_EVENT_HOMING_DISTANCE);
1172 nd_write_short((short)(distance>>16));
1176 void newdemo_record_letterbox(void)
1179 nd_write_byte(ND_EVENT_LETTERBOX);
1183 void newdemo_record_rearview(void)
1186 nd_write_byte(ND_EVENT_REARVIEW);
1190 void newdemo_record_restore_cockpit(void)
1193 nd_write_byte(ND_EVENT_RESTORE_COCKPIT);
1197 void newdemo_record_restore_rearview(void)
1200 nd_write_byte(ND_EVENT_RESTORE_REARVIEW);
1204 void newdemo_record_wall_set_tmap_num1(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1207 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM1);
1208 nd_write_short(seg);
1209 nd_write_byte(side);
1210 nd_write_short(cseg);
1211 nd_write_byte(cside);
1212 nd_write_short(tmap);
1216 void newdemo_record_wall_set_tmap_num2(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1219 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM2);
1220 nd_write_short(seg);
1221 nd_write_byte(side);
1222 nd_write_short(cseg);
1223 nd_write_byte(cside);
1224 nd_write_short(tmap);
1228 void newdemo_record_multi_cloak(int pnum)
1231 nd_write_byte(ND_EVENT_MULTI_CLOAK);
1232 nd_write_byte((sbyte)pnum);
1236 void newdemo_record_multi_decloak(int pnum)
1239 nd_write_byte(ND_EVENT_MULTI_DECLOAK);
1240 nd_write_byte((sbyte)pnum);
1244 void newdemo_record_multi_death(int pnum)
1247 nd_write_byte(ND_EVENT_MULTI_DEATH);
1248 nd_write_byte((sbyte)pnum);
1252 void newdemo_record_multi_kill(int pnum, sbyte kill)
1255 nd_write_byte(ND_EVENT_MULTI_KILL);
1256 nd_write_byte((sbyte)pnum);
1257 nd_write_byte(kill);
1261 void newdemo_record_multi_connect(int pnum, int new_player, char *new_callsign)
1264 nd_write_byte(ND_EVENT_MULTI_CONNECT);
1265 nd_write_byte((sbyte)pnum);
1266 nd_write_byte((sbyte)new_player);
1268 nd_write_string(Players[pnum].callsign);
1269 nd_write_int(Players[pnum].net_killed_total);
1270 nd_write_int(Players[pnum].net_kills_total);
1272 nd_write_string(new_callsign);
1276 void newdemo_record_multi_reconnect(int pnum)
1279 nd_write_byte(ND_EVENT_MULTI_RECONNECT);
1280 nd_write_byte((sbyte)pnum);
1284 void newdemo_record_multi_disconnect(int pnum)
1287 nd_write_byte(ND_EVENT_MULTI_DISCONNECT);
1288 nd_write_byte((sbyte)pnum);
1292 void newdemo_record_player_score(int score)
1295 nd_write_byte(ND_EVENT_PLAYER_SCORE);
1296 nd_write_int(score);
1300 void newdemo_record_multi_score(int pnum, int score)
1303 nd_write_byte(ND_EVENT_MULTI_SCORE);
1304 nd_write_byte((sbyte)pnum);
1305 nd_write_int(score - Players[pnum].score); // called before score is changed!!!!
1309 void newdemo_record_primary_ammo(int old_ammo, int new_ammo)
1312 nd_write_byte(ND_EVENT_PRIMARY_AMMO);
1314 nd_write_short((short)new_ammo);
1316 nd_write_short((short)old_ammo);
1317 nd_write_short((short)new_ammo);
1321 void newdemo_record_secondary_ammo(int old_ammo, int new_ammo)
1324 nd_write_byte(ND_EVENT_SECONDARY_AMMO);
1326 nd_write_short((short)new_ammo);
1328 nd_write_short((short)old_ammo);
1329 nd_write_short((short)new_ammo);
1333 void newdemo_record_door_opening(int segnum, int side)
1336 nd_write_byte(ND_EVENT_DOOR_OPENING);
1337 nd_write_short((short)segnum);
1338 nd_write_byte((sbyte)side);
1342 void newdemo_record_laser_level(sbyte old_level, sbyte new_level)
1345 nd_write_byte(ND_EVENT_LASER_LEVEL);
1346 nd_write_byte(old_level);
1347 nd_write_byte(new_level);
1351 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)
1353 Assert(front_wall_num <= 255 && back_wall_num <= 255);
1356 nd_write_byte(ND_EVENT_CLOAKING_WALL);
1357 nd_write_byte(front_wall_num);
1358 nd_write_byte(back_wall_num);
1359 nd_write_byte(type);
1360 nd_write_byte(state);
1361 nd_write_byte(cloak_value);
1362 nd_write_short(l0>>8);
1363 nd_write_short(l1>>8);
1364 nd_write_short(l2>>8);
1365 nd_write_short(l3>>8);
1369 void newdemo_set_new_level(int level_num)
1376 nd_write_byte(ND_EVENT_NEW_LEVEL);
1377 nd_write_byte((sbyte)level_num);
1378 nd_write_byte((sbyte)Current_level_num);
1380 if (JustStartedRecording==1)
1382 nd_write_int(Num_walls);
1383 for (i=0;i<Num_walls;i++)
1385 nd_write_byte (Walls[i].type);
1386 nd_write_byte (Walls[i].flags);
1387 nd_write_byte (Walls[i].state);
1389 seg = &Segments[Walls[i].segnum];
1390 side = Walls[i].sidenum;
1391 nd_write_short (seg->sides[side].tmap_num);
1392 nd_write_short (seg->sides[side].tmap_num2);
1393 JustStartedRecording=0;
1400 int newdemo_read_demo_start(int rnd_demo)
1402 sbyte i, version, laser_level;
1403 sbyte c, energy, shield;
1404 char text[128], current_mission[9] = "";
1407 if ((c != ND_EVENT_START_DEMO) || nd_bad_read) {
1410 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_CORRUPT);
1411 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1412 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1415 nd_read_byte(&version);
1416 nd_read_byte(&Newdemo_game_type);
1417 if (Newdemo_game_type < DEMO_GAME_TYPE_D1_SHARE) {
1420 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1421 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1422 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Descent: First Strike";
1424 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1427 if (Newdemo_game_type > DEMO_GAME_TYPE) {
1430 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1431 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1432 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Unknown Descent version";
1434 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1437 if (version < DEMO_VERSION_D1_SHARE) {
1440 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_OLD);
1441 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1442 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1446 nd_read_fix(&GameTime);
1447 Boss_cloak_start_time=Boss_cloak_end_time=GameTime;
1448 JasonPlaybackTotal=0;
1450 nd_read_int(&Newdemo_game_mode);
1453 change_playernum_to((Newdemo_game_mode >> 16) & 0x7);
1454 if (Newdemo_game_mode & GM_TEAM) {
1455 nd_read_byte((sbyte *) &(Netgame.team_vector));
1457 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1) {
1458 nd_read_string(Netgame.team_name[0]);
1459 nd_read_string(Netgame.team_name[1]);
1463 if (Newdemo_game_mode & GM_MULTI) {
1468 // changed this to above two lines -- breaks on the mac because of
1470 // nd_read_byte((sbyte *)&N_players);
1471 for (i = 0 ; i < N_players; i++) {
1472 Players[i].cloak_time = 0;
1473 Players[i].invulnerable_time = 0;
1474 nd_read_string(Players[i].callsign);
1475 nd_read_byte(&(Players[i].connected));
1477 if (Newdemo_game_mode & GM_MULTI_COOP) {
1478 nd_read_int(&(Players[i].score));
1480 nd_read_short((short *)&(Players[i].net_killed_total));
1481 nd_read_short((short *)&(Players[i].net_kills_total));
1484 Game_mode = Newdemo_game_mode;
1485 multi_sort_kill_list();
1486 Game_mode = GM_NORMAL;
1489 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1)
1490 nd_read_int(&(Players[Player_num].score)); // Note link to above if!
1492 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1) {
1493 for (i = 0; i < (Newdemo_game_type < DEMO_GAME_TYPE_D2 ? 5 : MAX_PRIMARY_WEAPONS); i++)
1494 nd_read_short((short*)&(Players[Player_num].primary_ammo[i]));
1496 for (i = 0; i < (Newdemo_game_type < DEMO_GAME_TYPE_D2 ? 5 : MAX_SECONDARY_WEAPONS); i++)
1497 nd_read_short((short*)&(Players[Player_num].secondary_ammo[i]));
1499 nd_read_byte(&laser_level);
1500 if (laser_level != Players[Player_num].laser_level) {
1501 Players[Player_num].laser_level = laser_level;
1502 update_laser_weapon_info();
1505 // Support for missions
1507 nd_read_string(current_mission);
1509 if (!strcmp(current_mission, ""))
1510 strcpy(current_mission, "descent");
1511 if (!strcmp(current_mission, "d2demo") && !cfexist("d2demo.hog")) {
1512 strcpy(current_mission, "d2");
1513 Newdemo_is_d2demo = 1;
1515 if (!load_mission_by_name(current_mission)) {
1519 sprintf(text, TXT_NOMISSION4DEMO, current_mission);
1520 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1521 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1526 nd_recorded_total = 0;
1527 nd_playback_total = 0;
1528 nd_read_byte(&energy);
1529 nd_read_byte(&shield);
1531 nd_read_int((int *)&(Players[Player_num].flags));
1532 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
1533 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1534 Newdemo_players_cloaked |= (1 << Player_num);
1536 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
1537 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1539 nd_read_byte((sbyte *)&Primary_weapon);
1540 nd_read_byte((sbyte *)&Secondary_weapon);
1542 // Next bit of code to fix problem that I introduced between 1.0 and 1.1
1543 // check the next byte -- it _will_ be a load_new_level event. If it is
1544 // not, then we must shift all bytes up by one.
1546 if (Newdemo_game_type < DEMO_GAME_TYPE_D1)
1551 if (c != ND_EVENT_NEW_LEVEL) {
1554 flags = Players[Player_num].flags;
1556 shield = (unsigned char)flags;
1557 flags = (flags >> 8) & 0x00ffffff;
1558 flags |= (Primary_weapon << 24);
1559 Primary_weapon = Secondary_weapon;
1560 Secondary_weapon = c;
1562 PHYSFS_seek(infile, PHYSFS_tell(infile) - 1);
1565 Players[Player_num].energy = i2f(energy);
1566 Players[Player_num].shields = i2f(shield);
1567 JustStartedPlayback=1;
1571 void newdemo_pop_ctrlcen_triggers()
1575 segment *seg, *csegp;
1577 for (i = 0; i < ControlCenterTriggers.num_links; i++) {
1578 seg = &Segments[ControlCenterTriggers.seg[i]];
1579 side = ControlCenterTriggers.side[i];
1580 csegp = &Segments[seg->children[side]];
1581 cside = find_connect_side(seg, csegp);
1582 anim_num = Walls[seg->sides[side].wall_num].clip_num;
1583 n = WallAnims[anim_num].num_frames;
1584 if (WallAnims[anim_num].flags & WCF_TMAP1) {
1585 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[n-1];
1587 seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[n-1];
1592 #define N_PLAYER_SHIP_TEXTURES 6
1594 void nd_render_extras (ubyte,object *);
1595 extern void multi_apply_goal_textures(void);
1596 ubyte Newdemo_flying_guided=0;
1598 int newdemo_read_frame_information()
1600 int done, segnum, side, objnum, soundno, angle, volume, i,shot;
1602 sbyte c = 0, WhichWindow;
1603 static sbyte saved_letter_cockpit;
1604 static sbyte saved_rearview_cockpit;
1606 static char LastReadValue=101;
1611 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1612 for (segnum=0; segnum <= Highest_segment_index; segnum++)
1613 Segments[segnum].objects = -1;
1616 Players[Player_num].homing_object_dist = -F1_0;
1622 if (nd_bad_read) { done = -1; break; }
1626 case ND_EVENT_START_FRAME: { // Followed by an integer frame number, then a fix FrameTime
1627 short last_frame_length;
1630 nd_read_short(&last_frame_length);
1631 nd_read_int(&NewdemoFrameCount);
1632 nd_read_int((int *)&nd_recorded_time);
1633 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1634 nd_recorded_total += nd_recorded_time;
1635 NewdemoFrameCount--;
1637 if (nd_bad_read) { done = -1; break; }
1641 case ND_EVENT_VIEWER_OBJECT: // Followed by an object structure
1642 if (Newdemo_game_type < DEMO_GAME_TYPE_D2)
1645 nd_read_byte (&WhichWindow);
1648 //mprintf ((0,"Reading extra!\n"));
1649 nd_read_object (&extraobj);
1650 if (Newdemo_vcr_state!=ND_STATE_PAUSED)
1652 if (nd_bad_read) { done = -1; break; }
1654 nd_render_extras (WhichWindow,&extraobj);
1659 //mprintf ((0,"Reading viewer!\n"));
1660 //Viewer=&Objects[0];
1661 nd_read_object(Viewer);
1663 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1664 if (nd_bad_read) { done = -1; break; }
1665 segnum = Viewer->segnum;
1666 Viewer->next = Viewer->prev = Viewer->segnum = -1;
1668 // HACK HACK HACK -- since we have multiple level recording, it can be the case
1669 // HACK HACK HACK -- that when rewinding the demo, the viewer is in a segment
1670 // HACK HACK HACK -- that is greater than the highest index of segments. Bash
1671 // HACK HACK HACK -- the viewer to segment 0 for bogus view.
1673 if (segnum > Highest_segment_index)
1675 obj_link(OBJECT_NUMBER(Viewer), segnum);
1680 case ND_EVENT_RENDER_OBJECT: // Followed by an object structure
1681 objnum = obj_allocate();
1684 obj = &Objects[objnum];
1685 nd_read_object(obj);
1686 if (nd_bad_read) { done = -1; break; }
1687 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1688 segnum = obj->segnum;
1689 obj->next = obj->prev = obj->segnum = -1;
1691 // HACK HACK HACK -- don't render objects is segments greater than Highest_segment_index
1692 // HACK HACK HACK -- (see above)
1694 if (segnum > Highest_segment_index)
1697 obj_link(OBJECT_NUMBER(obj), segnum);
1699 if ((obj->type == OBJ_PLAYER) && (Newdemo_game_mode & GM_MULTI)) {
1702 if (Newdemo_game_mode & GM_TEAM)
1703 player = get_team(obj->id);
1710 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
1711 multi_player_textures[player][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[obj->rtype.pobj_info.model_num].first_texture+i]];
1713 multi_player_textures[player][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2]];
1714 multi_player_textures[player][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2+1]];
1715 obj->rtype.pobj_info.alt_textures = player+1;
1721 case ND_EVENT_SOUND:
1722 nd_read_int(&soundno);
1723 if (nd_bad_read) {done = -1; break; }
1724 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1725 digi_play_sample( soundno, F1_0 );
1728 //--unused case ND_EVENT_SOUND_ONCE:
1729 //--unused nd_read_int(&soundno);
1730 //--unused if (nd_bad_read) { done = -1; break; }
1731 //--unused if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1732 //--unused digi_play_sample_once( soundno, F1_0 );
1735 case ND_EVENT_SOUND_3D:
1736 nd_read_int(&soundno);
1737 nd_read_int(&angle);
1738 nd_read_int(&volume);
1739 if (nd_bad_read) { done = -1; break; }
1740 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1741 digi_play_sample_3d( soundno, angle, volume, 0 );
1744 case ND_EVENT_SOUND_3D_ONCE:
1745 nd_read_int(&soundno);
1746 nd_read_int(&angle);
1747 nd_read_int(&volume);
1748 if (nd_bad_read) { done = -1; break; }
1749 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1750 digi_play_sample_3d( soundno, angle, volume, 1 );
1753 case ND_EVENT_LINK_SOUND_TO_OBJ:
1755 int soundno, objnum, max_volume, max_distance, loop_start, loop_end;
1757 nd_read_int( &soundno );
1758 nd_read_int( &signature );
1759 nd_read_int( &max_volume );
1760 nd_read_int( &max_distance );
1761 nd_read_int( &loop_start );
1762 nd_read_int( &loop_end );
1763 objnum = newdemo_find_object( signature );
1764 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1765 digi_link_sound_to_object3( soundno, objnum, 1, max_volume, max_distance, loop_start, loop_end );
1770 case ND_EVENT_KILL_SOUND_TO_OBJ:
1772 int objnum, signature;
1773 nd_read_int( &signature );
1774 objnum = newdemo_find_object( signature );
1775 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1776 digi_kill_sound_linked_to_object(objnum);
1781 case ND_EVENT_WALL_HIT_PROCESS: {
1785 nd_read_int(&segnum);
1787 nd_read_fix(&damage);
1788 nd_read_int(&player);
1789 if (nd_bad_read) { done = -1; break; }
1790 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1791 wall_hit_process(&Segments[segnum], side, damage, player, &(Objects[0]) );
1795 case ND_EVENT_TRIGGER:
1796 nd_read_int(&segnum);
1798 nd_read_int(&objnum);
1800 if (nd_bad_read) { done = -1; break; }
1801 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1803 mprintf ((0,"EVENT TRIGGER! shot=%d\n",shot));
1805 if (Triggers[Walls[Segments[segnum].sides[side].wall_num].trigger].type == TT_SECRET_EXIT) {
1809 Assert(c == ND_EVENT_SECRET_THINGY);
1810 nd_read_int(&truth);
1812 check_trigger(&Segments[segnum], side, objnum,shot);
1814 check_trigger(&Segments[segnum], side, objnum,shot);
1818 case ND_EVENT_HOSTAGE_RESCUED: {
1821 nd_read_int(&hostage_number);
1822 if (nd_bad_read) { done = -1; break; }
1823 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1824 hostage_rescue( hostage_number );
1828 case ND_EVENT_MORPH_FRAME: {
1832 md = &morph_objects[0];
1833 if (newdemo_read( md->morph_vecs, sizeof(md->morph_vecs), 1 )!=1) { done=-1; break; }
1834 if (newdemo_read( md->submodel_active, sizeof(md->submodel_active), 1 )!=1) { done=-1; break; }
1835 if (newdemo_read( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 )!=1) { done=-1; break; }
1837 objnum = obj_allocate();
1840 obj = &Objects[objnum];
1841 nd_read_object(obj);
1842 obj->render_type = RT_POLYOBJ;
1843 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1844 if (nd_bad_read) { done = -1; break; }
1845 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1846 segnum = obj->segnum;
1847 obj->next = obj->prev = obj->segnum = -1;
1848 obj_link(OBJECT_NUMBER(obj), segnum);
1854 case ND_EVENT_WALL_TOGGLE:
1855 nd_read_int(&segnum);
1857 if (nd_bad_read) {done = -1; break; }
1858 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1859 wall_toggle(&Segments[segnum], side);
1862 case ND_EVENT_CONTROL_CENTER_DESTROYED:
1863 nd_read_int(&Countdown_seconds_left);
1864 Control_center_destroyed = 1;
1865 if (nd_bad_read) { done = -1; break; }
1866 if (!Newdemo_cntrlcen_destroyed) {
1867 newdemo_pop_ctrlcen_triggers();
1868 Newdemo_cntrlcen_destroyed = 1;
1869 //do_controlcen_destroyed_stuff(NULL);
1873 case ND_EVENT_HUD_MESSAGE: {
1876 nd_read_string(&(hud_msg[0]));
1877 if (nd_bad_read) { done = -1; break; }
1878 HUD_init_message( hud_msg );
1881 case ND_EVENT_START_GUIDED:
1882 Newdemo_flying_guided=1;
1883 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1884 Newdemo_flying_guided=0;
1886 case ND_EVENT_END_GUIDED:
1887 Newdemo_flying_guided=0;
1888 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1889 Newdemo_flying_guided=1;
1892 case ND_EVENT_PALETTE_EFFECT: {
1898 if (nd_bad_read) { done = -1; break; }
1899 PALETTE_FLASH_SET(r,g,b);
1903 case ND_EVENT_PLAYER_ENERGY: {
1905 sbyte old_energy = 0;
1907 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1)
1908 nd_read_byte(&old_energy);
1909 nd_read_byte(&energy);
1910 if (nd_bad_read) {done = -1; break; }
1911 if ((Newdemo_game_type < DEMO_GAME_TYPE_D1) || (Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1912 Players[Player_num].energy = i2f(energy);
1913 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1914 if (old_energy != -128)
1915 Players[Player_num].energy = i2f(old_energy);
1920 case ND_EVENT_PLAYER_AFTERBURNER: {
1922 sbyte old_afterburner;
1924 nd_read_byte(&old_afterburner);
1925 nd_read_byte(&afterburner);
1926 if (nd_bad_read) {done = -1; break; }
1927 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1928 Afterburner_charge = afterburner<<9;
1929 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1930 if (old_afterburner != -128)
1931 Afterburner_charge = old_afterburner<<9;
1936 case ND_EVENT_PLAYER_SHIELD: {
1938 sbyte old_shield = 0;
1940 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1)
1941 nd_read_byte(&old_shield);
1942 nd_read_byte(&shield);
1943 if (nd_bad_read) {done = -1; break; }
1944 if ((Newdemo_game_type < DEMO_GAME_TYPE_D1) || (Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1945 Players[Player_num].shields = i2f(shield);
1946 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1947 if (old_shield != -128)
1948 Players[Player_num].shields = i2f(old_shield);
1953 case ND_EVENT_PLAYER_FLAGS: {
1956 nd_read_int((int *)&(Players[Player_num].flags));
1957 if (nd_bad_read) {done = -1; break; }
1959 oflags = Players[Player_num].flags >> 16;
1960 Players[Player_num].flags &= 0xffff;
1962 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || ((Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) && (oflags != 0xffff)) ) {
1963 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1964 Players[Player_num].cloak_time = 0;
1965 Newdemo_players_cloaked &= ~(1 << Player_num);
1967 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1968 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1969 Newdemo_players_cloaked |= (1 << Player_num);
1971 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1972 Players[Player_num].invulnerable_time = 0;
1973 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1974 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1975 Players[Player_num].flags = oflags;
1976 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1977 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1978 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1979 Newdemo_players_cloaked |= (1 << Player_num);
1981 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1982 Players[Player_num].cloak_time = 0;
1983 Newdemo_players_cloaked &= ~(1 << Player_num);
1985 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1986 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1987 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1988 Players[Player_num].invulnerable_time = 0;
1990 update_laser_weapon_info(); // in case of quad laser change
1994 case ND_EVENT_PLAYER_WEAPON: {
1995 sbyte weapon_type, weapon_num;
1996 sbyte old_weapon = 0;
1998 nd_read_byte(&weapon_type);
1999 nd_read_byte(&weapon_num);
2000 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1)
2001 nd_read_byte(&old_weapon);
2002 if ((Newdemo_game_type < DEMO_GAME_TYPE_D1) || (Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2003 if (weapon_type == 0)
2004 Primary_weapon = (int)weapon_num;
2006 Secondary_weapon = (int)weapon_num;
2007 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2008 if (weapon_type == 0)
2009 Primary_weapon = (int)old_weapon;
2011 Secondary_weapon = (int)old_weapon;
2016 case ND_EVENT_EFFECT_BLOWUP: {
2022 //create a dummy object which will be the weapon that hits
2023 //the monitor. the blowup code wants to know who the parent of the
2024 //laser is, so create a laser whose parent is the player
2025 dummy.ctype.laser_info.parent_type = OBJ_PLAYER;
2027 nd_read_short(&segnum);
2028 nd_read_byte(&side);
2029 nd_read_vector(&pnt);
2030 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2031 check_effect_blowup(&(Segments[segnum]), side, &pnt, &dummy, 0);
2035 case ND_EVENT_HOMING_DISTANCE: {
2038 nd_read_short(&distance);
2039 Players[Player_num].homing_object_dist = i2f((int)(distance << 16));
2043 case ND_EVENT_LETTERBOX:
2044 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2045 saved_letter_cockpit = Cockpit_mode.intval;
2046 select_cockpit(CM_LETTERBOX);
2047 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2048 select_cockpit(saved_letter_cockpit);
2051 case ND_EVENT_CHANGE_COCKPIT: {
2054 nd_read_int (&dummy);
2055 select_cockpit (dummy);
2059 case ND_EVENT_REARVIEW:
2060 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2061 saved_rearview_cockpit = Cockpit_mode.intval;
2062 if (Cockpit_mode.intval == CM_FULL_COCKPIT)
2063 select_cockpit(CM_REAR_VIEW);
2065 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2066 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2067 saved_rearview_cockpit = CM_FULL_COCKPIT;
2068 select_cockpit(saved_rearview_cockpit);
2073 case ND_EVENT_RESTORE_COCKPIT:
2074 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2075 saved_letter_cockpit = Cockpit_mode.intval;
2076 select_cockpit(CM_LETTERBOX);
2077 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2078 select_cockpit(saved_letter_cockpit);
2082 case ND_EVENT_RESTORE_REARVIEW:
2083 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2084 saved_rearview_cockpit = Cockpit_mode.intval;
2085 if (Cockpit_mode.intval == CM_FULL_COCKPIT)
2086 select_cockpit(CM_REAR_VIEW);
2088 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2089 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2090 saved_rearview_cockpit = CM_FULL_COCKPIT;
2091 select_cockpit(saved_rearview_cockpit);
2097 case ND_EVENT_WALL_SET_TMAP_NUM1: {
2098 short seg, cseg, tmap;
2101 nd_read_short(&seg);
2102 nd_read_byte(&side);
2103 nd_read_short(&cseg);
2104 nd_read_byte(&cside);
2105 nd_read_short( &tmap );
2106 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD))
2107 Segments[seg].sides[side].tmap_num = Segments[cseg].sides[cside].tmap_num = tmap;
2111 case ND_EVENT_WALL_SET_TMAP_NUM2: {
2112 short seg, cseg, tmap;
2115 nd_read_short(&seg);
2116 nd_read_byte(&side);
2117 nd_read_short(&cseg);
2118 nd_read_byte(&cside);
2119 nd_read_short( &tmap );
2120 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) {
2121 Assert(tmap!=0 && Segments[seg].sides[side].tmap_num2!=0);
2122 Segments[seg].sides[side].tmap_num2 = Segments[cseg].sides[cside].tmap_num2 = tmap;
2127 case ND_EVENT_MULTI_CLOAK: {
2130 nd_read_byte(&pnum);
2131 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2132 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2133 Players[pnum].cloak_time = 0;
2134 Newdemo_players_cloaked &= ~(1 << pnum);
2135 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2136 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2137 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2138 Newdemo_players_cloaked |= (1 << pnum);
2143 case ND_EVENT_MULTI_DECLOAK: {
2146 nd_read_byte(&pnum);
2148 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2149 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2150 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2151 Newdemo_players_cloaked |= (1 << pnum);
2152 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2153 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2154 Players[pnum].cloak_time = 0;
2155 Newdemo_players_cloaked &= ~(1 << pnum);
2160 case ND_EVENT_MULTI_DEATH: {
2163 nd_read_byte(&pnum);
2164 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2165 Players[pnum].net_killed_total--;
2166 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2167 Players[pnum].net_killed_total++;
2172 case ND_EVENT_MULTI_KILL: {
2175 nd_read_byte(&pnum);
2176 nd_read_byte(&kill);
2177 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2178 Players[pnum].net_kills_total -= kill;
2179 if (Newdemo_game_mode & GM_TEAM)
2180 team_kills[get_team(pnum)] -= kill;
2181 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2182 Players[pnum].net_kills_total += kill;
2183 if (Newdemo_game_mode & GM_TEAM)
2184 team_kills[get_team(pnum)] += kill;
2186 Game_mode = Newdemo_game_mode;
2187 multi_sort_kill_list();
2188 Game_mode = GM_NORMAL;
2192 case ND_EVENT_MULTI_CONNECT: {
2193 sbyte pnum, new_player;
2194 int killed_total = 0, kills_total = 0;
2195 char new_callsign[CALLSIGN_LEN+1], old_callsign[CALLSIGN_LEN+1];
2197 nd_read_byte(&pnum);
2198 nd_read_byte(&new_player);
2200 nd_read_string(old_callsign);
2201 nd_read_int(&killed_total);
2202 nd_read_int(&kills_total);
2204 nd_read_string(new_callsign);
2205 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2206 Players[pnum].connected = 0;
2208 memcpy(Players[pnum].callsign, old_callsign, CALLSIGN_LEN+1);
2209 Players[pnum].net_killed_total = killed_total;
2210 Players[pnum].net_kills_total = kills_total;
2214 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2215 Players[pnum].connected = 1;
2216 Players[pnum].net_kills_total = 0;
2217 Players[pnum].net_killed_total = 0;
2218 memcpy(Players[pnum].callsign, new_callsign, CALLSIGN_LEN+1);
2225 case ND_EVENT_MULTI_RECONNECT: {
2228 nd_read_byte(&pnum);
2229 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2230 Players[pnum].connected = 0;
2231 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2232 Players[pnum].connected = 1;
2236 case ND_EVENT_MULTI_DISCONNECT: {
2239 nd_read_byte(&pnum);
2240 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2241 Players[pnum].connected = 1;
2242 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2243 Players[pnum].connected = 0;
2247 case ND_EVENT_MULTI_SCORE: {
2251 nd_read_byte(&pnum);
2252 nd_read_int(&score);
2253 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2254 Players[pnum].score -= score;
2255 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2256 Players[pnum].score += score;
2257 Game_mode = Newdemo_game_mode;
2258 multi_sort_kill_list();
2259 Game_mode = GM_NORMAL;
2264 case ND_EVENT_PLAYER_SCORE: {
2267 nd_read_int(&score);
2268 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2269 Players[Player_num].score -= score;
2270 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2271 Players[Player_num].score += score;
2276 case ND_EVENT_PRIMARY_AMMO: {
2277 short old_ammo, new_ammo;
2279 nd_read_short(&old_ammo);
2280 nd_read_short(&new_ammo);
2282 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2283 Players[Player_num].primary_ammo[Primary_weapon] = old_ammo;
2284 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2285 Players[Player_num].primary_ammo[Primary_weapon] = new_ammo;
2289 case ND_EVENT_SECONDARY_AMMO: {
2290 short old_ammo, new_ammo;
2292 nd_read_short(&old_ammo);
2293 nd_read_short(&new_ammo);
2295 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2296 Players[Player_num].secondary_ammo[Secondary_weapon] = old_ammo;
2297 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2298 Players[Player_num].secondary_ammo[Secondary_weapon] = new_ammo;
2302 case ND_EVENT_DOOR_OPENING: {
2306 nd_read_short(&segnum);
2307 nd_read_byte(&side);
2308 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2311 segment *segp, *csegp;
2313 segp = &Segments[segnum];
2314 csegp = &Segments[segp->children[side]];
2315 cside = find_connect_side(segp, csegp);
2316 anim_num = Walls[segp->sides[side].wall_num].clip_num;
2318 if (WallAnims[anim_num].flags & WCF_TMAP1) {
2319 segp->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[0];
2321 segp->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[0];
2327 case ND_EVENT_LASER_LEVEL: {
2328 sbyte old_level, new_level;
2330 nd_read_byte(&old_level);
2331 nd_read_byte(&new_level);
2332 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2333 Players[Player_num].laser_level = old_level;
2334 update_laser_weapon_info();
2335 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2336 Players[Player_num].laser_level = new_level;
2337 update_laser_weapon_info();
2342 case ND_EVENT_CLOAKING_WALL: {
2343 sbyte back_wall_num,front_wall_num,type,state,cloak_value;
2348 nd_read_byte(&front_wall_num);
2349 nd_read_byte(&back_wall_num);
2350 nd_read_byte(&type);
2351 nd_read_byte(&state);
2352 nd_read_byte(&cloak_value);
2358 Walls[front_wall_num].type = type;
2359 Walls[front_wall_num].state = state;
2360 Walls[front_wall_num].cloak_value = cloak_value;
2361 segp = &Segments[Walls[front_wall_num].segnum];
2362 sidenum = Walls[front_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;
2368 Walls[back_wall_num].type = type;
2369 Walls[back_wall_num].state = state;
2370 Walls[back_wall_num].cloak_value = cloak_value;
2371 segp = &Segments[Walls[back_wall_num].segnum];
2372 sidenum = Walls[back_wall_num].sidenum;
2373 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2374 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2375 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2376 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2381 case ND_EVENT_NEW_LEVEL: {
2382 sbyte new_level, old_level, loaded_level;
2384 nd_read_byte (&new_level);
2385 nd_read_byte (&old_level);
2386 if (Newdemo_vcr_state == ND_STATE_PAUSED)
2390 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2391 loaded_level = old_level;
2393 loaded_level = new_level;
2394 for (i = 0; i < MAX_PLAYERS; i++) {
2395 Players[i].cloak_time = 0;
2396 Players[i].flags &= ~PLAYER_FLAGS_CLOAKED;
2399 if ((loaded_level < Last_secret_level) || (loaded_level > Last_level)) {
2402 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2403 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2404 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2405 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2409 LoadLevel((int)loaded_level,1);
2410 Newdemo_cntrlcen_destroyed = 0;
2412 if (JustStartedPlayback && Newdemo_game_type >= DEMO_GAME_TYPE_D2)
2414 nd_read_int (&Num_walls);
2415 for (i=0;i<Num_walls;i++) // restore the walls
2417 nd_read_byte ((signed char *)&Walls[i].type);
2418 nd_read_byte ((signed char *)&Walls[i].flags);
2419 nd_read_byte ((signed char *)&Walls[i].state);
2421 seg = &Segments[Walls[i].segnum];
2422 side = Walls[i].sidenum;
2423 nd_read_short (&seg->sides[side].tmap_num);
2424 nd_read_short (&seg->sides[side].tmap_num2);
2427 if (Newdemo_game_mode & GM_CAPTURE)
2428 multi_apply_goal_textures ();
2430 JustStartedPlayback=0;
2434 // so says Rob H.!!! if (Newdemo_game_mode & GM_MULTI) {
2435 // so says Rob H.!!! for (i = 0; i < Num_walls; i++) {
2436 // so says Rob H.!!! if (Walls[i].type == WALL_BLASTABLE)
2437 // so says Rob H.!!! {
2438 // so says Rob H.!!! int a, n;
2439 // so says Rob H.!!! int side;
2440 // so says Rob H.!!! segment *seg;
2441 // so says Rob H.!!!
2442 // so says Rob H.!!! seg = &Segments[Walls[i].segnum];
2443 // so says Rob H.!!! side = Walls[i].sidenum;
2444 // so says Rob H.!!! a = Walls[i].clip_num;
2445 // so says Rob H.!!! n = WallAnims[a].num_frames;
2446 // so says Rob H.!!! seg->sides[side].tmap_num = WallAnims[a].frames[n-1];
2447 // so says Rob H.!!! Walls[i].flags |= WALL_BLASTED;
2448 // so says Rob H.!!! }
2449 // so says Rob H.!!! }
2450 // so says Rob H.!!! }
2452 reset_palette_add(); // get palette back to normal
2457 case ND_EVENT_EOF: {
2459 PHYSFS_seek(infile, PHYSFS_tell(infile) - 1); // get back to the EOF marker
2461 NewdemoFrameCount++;
2475 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_ERR_READING;
2476 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_DEMO_OLD_CORRUPT;
2477 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2483 void newdemo_goto_beginning()
2485 //if (NewdemoFrameCount == 0)
2487 PHYSFS_seek(infile, 0);
2488 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2489 if (newdemo_read_demo_start(0))
2490 newdemo_stop_playback();
2491 if (newdemo_read_frame_information() == -1)
2492 newdemo_stop_playback();
2493 if (newdemo_read_frame_information() == -1)
2494 newdemo_stop_playback();
2495 Newdemo_vcr_state = ND_STATE_PAUSED;
2499 void newdemo_goto_end()
2501 short frame_length, byte_count, bshort;
2502 sbyte level, bbyte, laser_level;
2503 sbyte energy, shield, c;
2506 PHYSFS_seek(infile, PHYSFS_fileLength(infile) - 2);
2507 nd_read_byte(&level);
2509 if ((level < Last_secret_level) || (level > Last_level)) {
2512 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2513 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2514 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2515 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2516 newdemo_stop_playback();
2519 if (level != Current_level_num)
2522 if (Newdemo_game_type >= DEMO_GAME_TYPE_D1) {
2523 PHYSFS_seek(infile, PHYSFS_fileLength(infile) - 4);
2524 nd_read_short(&byte_count);
2525 PHYSFS_seek(infile, PHYSFS_tell(infile) - 2 - byte_count);
2527 PHYSFS_seek(infile, PHYSFS_fileLength(infile) - 12);
2529 nd_read_short(&frame_length);
2530 loc = (int)PHYSFS_tell(infile);
2531 if (Newdemo_game_mode & GM_MULTI)
2532 nd_read_byte(&Newdemo_players_cloaked);
2534 nd_read_byte(&bbyte);
2535 nd_read_byte(&bbyte);
2536 nd_read_short(&bshort);
2539 nd_read_byte(&energy);
2540 nd_read_byte(&shield);
2541 Players[Player_num].energy = i2f(energy);
2542 Players[Player_num].shields = i2f(shield);
2543 nd_read_int((int *)&(Players[Player_num].flags));
2544 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
2545 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2546 Newdemo_players_cloaked |= (1 << Player_num);
2548 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2549 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2550 nd_read_byte((sbyte *)&Primary_weapon);
2551 nd_read_byte((sbyte *)&Secondary_weapon);
2552 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
2553 nd_read_short((short *)&(Players[Player_num].primary_ammo[i]));
2554 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
2555 nd_read_short((short *)&(Players[Player_num].secondary_ammo[i]));
2556 nd_read_byte(&laser_level);
2557 if (laser_level != Players[Player_num].laser_level) {
2558 Players[Player_num].laser_level = laser_level;
2559 update_laser_weapon_info();
2562 if (Newdemo_game_mode & GM_MULTI) {
2565 // see newdemo_read_start_demo for explanation of
2566 // why this is commented out
2567 // nd_read_byte((sbyte *)&N_players);
2568 for (i = 0; i < N_players; i++) {
2569 nd_read_string(Players[i].callsign);
2570 nd_read_byte(&(Players[i].connected));
2571 if (Newdemo_game_mode & GM_MULTI_COOP) {
2572 nd_read_int(&(Players[i].score));
2574 nd_read_short((short *)&(Players[i].net_killed_total));
2575 nd_read_short((short *)&(Players[i].net_kills_total));
2579 nd_read_int(&(Players[Player_num].score));
2582 PHYSFS_seek(infile, loc);
2583 PHYSFS_seek(infile, PHYSFS_tell(infile) - frame_length);
2584 nd_read_int(&NewdemoFrameCount); // get the frame count
2585 NewdemoFrameCount--;
2586 PHYSFS_seek(infile, PHYSFS_tell(infile) + 4);
2587 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2588 newdemo_read_frame_information(); // then the frame information
2589 Newdemo_vcr_state = ND_STATE_PAUSED;
2593 void newdemo_back_frames(int frames)
2595 short last_frame_length;
2598 for (i = 0; i < frames; i++)
2600 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
2601 nd_read_short(&last_frame_length);
2602 PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
2604 if (!Newdemo_at_eof && newdemo_read_frame_information() == -1) {
2605 newdemo_stop_playback();
2611 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
2612 nd_read_short(&last_frame_length);
2613 PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
2619 * routine to interpolate the viewer position. the current position is
2620 * stored in the Viewer object. Save this position, and read the next
2621 * frame to get all objects read in. Calculate the delta playback and
2622 * the delta recording frame times between the two frames, then intepolate
2623 * the viewers position accordingly. nd_recorded_time is the time that it
2624 * took the recording to render the frame that we are currently looking
2628 void interpolate_frame(fix d_play, fix d_recorded)
2630 int i, j, num_cur_objs;
2634 factor = fixdiv(d_play, d_recorded);
2638 num_cur_objs = Highest_object_index;
2639 cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1));
2640 if (cur_objs == NULL) {
2641 mprintf((0,"Couldn't get %d bytes for cur_objs in interpolate_frame\n", sizeof(object) * num_cur_objs));
2645 for (i = 0; i <= num_cur_objs; i++)
2646 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2648 Newdemo_vcr_state = ND_STATE_PAUSED;
2649 if (newdemo_read_frame_information() == -1) {
2651 newdemo_stop_playback();
2655 for (i = 0; i <= num_cur_objs; i++) {
2656 for (j = 0; j <= Highest_object_index; j++) {
2657 if (cur_objs[i].signature == Objects[j].signature) {
2658 sbyte render_type = cur_objs[i].render_type;
2659 //fix delta_p, delta_h, delta_b;
2660 fix delta_x, delta_y, delta_z;
2661 //vms_angvec cur_angles, dest_angles;
2663 // Extract the angles from the object orientation matrix.
2664 // Some of this code taken from ai_turn_towards_vector
2665 // Don't do the interpolation on certain render types which don't use an orientation matrix
2667 if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) {
2669 vms_vector fvec1, fvec2, rvec1, rvec2;
2672 fvec1 = cur_objs[i].orient.fvec;
2673 vm_vec_scale(&fvec1, F1_0-factor);
2674 fvec2 = Objects[j].orient.fvec;
2675 vm_vec_scale(&fvec2, factor);
2676 vm_vec_add2(&fvec1, &fvec2);
2677 mag1 = vm_vec_normalize_quick(&fvec1);
2678 if (mag1 > F1_0/256) {
2679 rvec1 = cur_objs[i].orient.rvec;
2680 vm_vec_scale(&rvec1, F1_0-factor);
2681 rvec2 = Objects[j].orient.rvec;
2682 vm_vec_scale(&rvec2, factor);
2683 vm_vec_add2(&rvec1, &rvec2);
2684 vm_vec_normalize_quick(&rvec1); // Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1
2685 vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2688 //--old new way -- vms_vector fvec1, fvec2, rvec1, rvec2;
2690 //--old new way -- fvec1 = cur_objs[i].orient.fvec;
2691 //--old new way -- vm_vec_scale(&fvec1, F1_0-factor);
2692 //--old new way -- fvec2 = Objects[j].orient.fvec;
2693 //--old new way -- vm_vec_scale(&fvec2, factor);
2694 //--old new way -- vm_vec_add2(&fvec1, &fvec2);
2695 //--old new way -- vm_vec_normalize_quick(&fvec1);
2697 //--old new way -- rvec1 = cur_objs[i].orient.rvec;
2698 //--old new way -- vm_vec_scale(&rvec1, F1_0-factor);
2699 //--old new way -- rvec2 = Objects[j].orient.rvec;
2700 //--old new way -- vm_vec_scale(&rvec2, factor);
2701 //--old new way -- vm_vec_add2(&rvec1, &rvec2);
2702 //--old new way -- vm_vec_normalize_quick(&rvec1);
2704 //--old new way -- vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2706 // -- old fashioned way -- vm_extract_angles_matrix(&cur_angles, &(cur_objs[i].orient));
2707 // -- old fashioned way -- vm_extract_angles_matrix(&dest_angles, &(Objects[j].orient));
2708 // -- old fashioned way --
2709 // -- old fashioned way -- delta_p = (dest_angles.p - cur_angles.p);
2710 // -- old fashioned way -- delta_h = (dest_angles.h - cur_angles.h);
2711 // -- old fashioned way -- delta_b = (dest_angles.b - cur_angles.b);
2712 // -- old fashioned way --
2713 // -- old fashioned way -- if (delta_p != 0) {
2714 // -- old fashioned way -- if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
2715 // -- old fashioned way -- if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
2716 // -- old fashioned way -- delta_p = fixmul(delta_p, factor);
2717 // -- old fashioned way -- cur_angles.p += delta_p;
2718 // -- old fashioned way -- }
2719 // -- old fashioned way -- if (delta_h != 0) {
2720 // -- old fashioned way -- if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
2721 // -- old fashioned way -- if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
2722 // -- old fashioned way -- delta_h = fixmul(delta_h, factor);
2723 // -- old fashioned way -- cur_angles.h += delta_h;
2724 // -- old fashioned way -- }
2725 // -- old fashioned way -- if (delta_b != 0) {
2726 // -- old fashioned way -- if (delta_b > F1_0/2) delta_b = dest_angles.b - cur_angles.b - F1_0;
2727 // -- old fashioned way -- if (delta_b < -F1_0/2) delta_b = dest_angles.b - cur_angles.b + F1_0;
2728 // -- old fashioned way -- delta_b = fixmul(delta_b, factor);
2729 // -- old fashioned way -- cur_angles.b += delta_b;
2730 // -- old fashioned way -- }
2733 // Interpolate the object position. This is just straight linear
2736 delta_x = Objects[j].pos.x - cur_objs[i].pos.x;
2737 delta_y = Objects[j].pos.y - cur_objs[i].pos.y;
2738 delta_z = Objects[j].pos.z - cur_objs[i].pos.z;
2740 delta_x = fixmul(delta_x, factor);
2741 delta_y = fixmul(delta_y, factor);
2742 delta_z = fixmul(delta_z, factor);
2744 cur_objs[i].pos.x += delta_x;
2745 cur_objs[i].pos.y += delta_y;
2746 cur_objs[i].pos.z += delta_z;
2748 // -- old fashioned way --// stuff the new angles back into the object structure
2749 // -- old fashioned way -- vm_angles_2_matrix(&(cur_objs[i].orient), &cur_angles);
2754 // get back to original position in the demo file. Reread the current
2755 // frame information again to reset all of the object stuff not covered
2756 // with Highest_object_index and the object array (previously rendered
2757 // objects, etc....)
2759 newdemo_back_frames(1);
2760 newdemo_back_frames(1);
2761 if (newdemo_read_frame_information() == -1)
2762 newdemo_stop_playback();
2763 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2765 for (i = 0; i <= num_cur_objs; i++)
2766 memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object));
2767 Highest_object_index = num_cur_objs;
2771 void newdemo_playback_one_frame()
2773 int frames_back, i, level;
2774 static fix base_interpol_time = 0;
2775 static fix d_recorded = 0;
2777 for (i = 0; i < MAX_PLAYERS; i++)
2778 if (Newdemo_players_cloaked & (1 << i))
2779 Players[i].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2781 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2782 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2784 if (Newdemo_vcr_state == ND_STATE_PAUSED) // render a frame or not
2787 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2788 DoJasonInterpolate(nd_recorded_time);
2790 Control_center_destroyed = 0;
2791 Countdown_seconds_left = -1;
2792 PALETTE_FLASH_SET(0,0,0); //clear flash
2794 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2796 level = Current_level_num;
2797 if (NewdemoFrameCount == 0)
2799 else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (NewdemoFrameCount < 10)) {
2800 newdemo_goto_beginning();
2803 if (Newdemo_vcr_state == ND_STATE_REWINDING)
2807 if (Newdemo_at_eof) {
2808 if (Newdemo_game_type == DEMO_GAME_TYPE_D1_SHARE)
2809 PHYSFS_seek(infile, PHYSFS_fileLength(infile) - 2);
2811 PHYSFS_seek(infile, PHYSFS_tell(infile) + 11);
2813 newdemo_back_frames(frames_back);
2815 if (level != Current_level_num)
2816 newdemo_pop_ctrlcen_triggers();
2818 if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) {
2819 if (level != Current_level_num)
2820 newdemo_back_frames(1);
2821 Newdemo_vcr_state = ND_STATE_PAUSED;
2824 else if (Newdemo_vcr_state == ND_STATE_FASTFORWARD) {
2825 if (!Newdemo_at_eof)
2827 for (i = 0; i < 10; i++)
2829 if (newdemo_read_frame_information() == -1)
2832 Newdemo_vcr_state = ND_STATE_PAUSED;
2834 newdemo_stop_playback();
2840 Newdemo_vcr_state = ND_STATE_PAUSED;
2842 else if (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD) {
2843 if (!Newdemo_at_eof) {
2844 level = Current_level_num;
2845 if (newdemo_read_frame_information() == -1) {
2846 if (!Newdemo_at_eof)
2847 newdemo_stop_playback();
2849 if (level != Current_level_num) {
2850 if (newdemo_read_frame_information() == -1) {
2851 if (!Newdemo_at_eof)
2852 newdemo_stop_playback();
2855 Newdemo_vcr_state = ND_STATE_PAUSED;
2857 Newdemo_vcr_state = ND_STATE_PAUSED;
2861 // First, uptate the total playback time to date. Then we check to see
2862 // if we need to change the playback style to interpolate frames or
2863 // skip frames based on where the playback time is relative to the
2866 if (NewdemoFrameCount <= 0)
2867 nd_playback_total = nd_recorded_total; // baseline total playback time
2869 nd_playback_total += FrameTime;
2870 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2871 if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) {
2872 playback_style = INTERPOLATE_PLAYBACK;
2873 nd_playback_total = nd_recorded_total + FrameTime; // baseline playback time
2874 base_interpol_time = nd_recorded_total;
2875 d_recorded = nd_recorded_time; // baseline delta recorded
2877 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2878 if (nd_playback_total > nd_recorded_total)
2879 playback_style = SKIP_PLAYBACK;
2882 if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) {
2885 if (nd_recorded_total - nd_playback_total < FrameTime) {
2886 d_recorded = nd_recorded_total - nd_playback_total;
2888 while (nd_recorded_total - nd_playback_total < FrameTime) {
2890 int i, j, num_objs, level;
2892 num_objs = Highest_object_index;
2893 cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1));
2894 if (cur_objs == NULL) {
2895 Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs);
2898 for (i = 0; i <= num_objs; i++)
2899 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2901 level = Current_level_num;
2902 if (newdemo_read_frame_information() == -1) {
2904 newdemo_stop_playback();
2907 if (level != Current_level_num) {
2909 if (newdemo_read_frame_information() == -1)
2910 newdemo_stop_playback();
2914 // for each new object in the frame just read in, determine if there is
2915 // a corresponding object that we have been interpolating. If so, then
2916 // copy that interpolated object to the new Objects array so that the
2917 // interpolated position and orientation can be preserved.
2919 for (i = 0; i <= num_objs; i++) {
2920 for (j = 0; j <= Highest_object_index; j++) {
2921 if (cur_objs[i].signature == Objects[j].signature) {
2922 memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix));
2923 memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector));
2929 d_recorded += nd_recorded_time;
2930 base_interpol_time = nd_playback_total - FrameTime;
2934 d_play = nd_playback_total - base_interpol_time;
2935 interpolate_frame(d_play, d_recorded);
2939 //mprintf ((0, "*"));
2940 if (newdemo_read_frame_information() == -1) {
2941 newdemo_stop_playback();
2944 if (playback_style == SKIP_PLAYBACK) {
2945 //mprintf ((0, "."));
2946 while (nd_playback_total > nd_recorded_total) {
2947 if (newdemo_read_frame_information() == -1) {
2948 newdemo_stop_playback();
2957 void newdemo_start_recording()
2959 Newdemo_size = (int)PHYSFSX_getFreeDiskSpace();
2960 con_printf(CON_VERBOSE, "Free space = %d\n", Newdemo_size);
2962 Newdemo_size -= 100000;
2964 if ((Newdemo_size+100000) < 2000000000) {
2965 if (((int)(Newdemo_size)) < 500000) {
2967 nm_messagebox(NULL, 1, TXT_OK, TXT_DEMO_NO_SPACE);
2969 nm_messagebox(NULL, 1, TXT_OK, "Not enough space on current\ndrive to start demo recording.");
2975 Newdemo_num_written = 0;
2977 Newdemo_state = ND_STATE_RECORDING;
2978 outfile = PHYSFSX_openWriteBuffered(DEMO_FILENAME);
2980 #if !defined(MACINTOSH) && !defined(_WIN32_WCE)
2981 if (outfile == NULL && errno == ENOENT) //dir doesn't exist?
2983 if (outfile == NULL) //dir doesn't exist and no errno on mac!
2986 PHYSFS_mkdir(DEMO_DIR); //try making directory
2987 outfile = PHYSFSX_openWriteBuffered(DEMO_FILENAME);
2990 if (outfile == NULL)
2992 nm_messagebox(NULL, 1, TXT_OK, "Cannot open demo temp file");
2993 Newdemo_state = ND_STATE_NORMAL;
2996 newdemo_record_start_demo();
3000 char demoname_allowed_chars[] = "azAZ09__--";
3001 void newdemo_stop_recording()
3005 static char filename[15] = "", *s;
3006 static sbyte tmpcnt = 0;
3008 char fullname[15+FILENAME_LEN] = DEMO_DIR;
3009 unsigned short byte_count = 0;
3013 nd_write_byte(ND_EVENT_EOF);
3014 nd_write_short(frame_bytes_written - 1);
3015 if (Game_mode & GM_MULTI) {
3016 for (l = 0; l < N_players; l++) {
3017 if (Players[l].flags & PLAYER_FLAGS_CLOAKED)
3018 cloaked |= (1 << l);
3020 nd_write_byte(cloaked);
3021 nd_write_byte(ND_EVENT_EOF);
3023 nd_write_short(ND_EVENT_EOF);
3025 nd_write_short(ND_EVENT_EOF);
3026 nd_write_int(ND_EVENT_EOF);
3028 byte_count += 10; // from frame_bytes_written
3030 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
3031 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
3032 nd_write_int(Players[Player_num].flags); // be sure players flags are set
3033 nd_write_byte((sbyte)Primary_weapon);
3034 nd_write_byte((sbyte)Secondary_weapon);
3037 for (l = 0; l < MAX_PRIMARY_WEAPONS; l++)
3038 nd_write_short((short)Players[Player_num].primary_ammo[l]);
3040 for (l = 0; l < MAX_SECONDARY_WEAPONS; l++)
3041 nd_write_short((short)Players[Player_num].secondary_ammo[l]);
3042 byte_count += (sizeof(short) * (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS));
3044 nd_write_byte(Players[Player_num].laser_level);
3047 if (Game_mode & GM_MULTI) {
3048 nd_write_byte((sbyte)N_players);
3050 for (l = 0; l < N_players; l++) {
3051 nd_write_string(Players[l].callsign);
3052 byte_count += (strlen(Players[l].callsign) + 2);
3053 nd_write_byte(Players[l].connected);
3054 if (Game_mode & GM_MULTI_COOP) {
3055 nd_write_int(Players[l].score);
3058 nd_write_short((short)Players[l].net_killed_total);
3059 nd_write_short((short)Players[l].net_kills_total);
3064 nd_write_int(Players[Player_num].score);
3067 nd_write_short(byte_count);
3069 nd_write_byte(Current_level_num);
3070 nd_write_byte(ND_EVENT_EOF);
3072 l = (int)PHYSFS_tell(outfile);
3073 PHYSFS_close(outfile);
3075 Newdemo_state = ND_STATE_NORMAL;
3076 gr_palette_load( gr_palette );
3078 if (filename[0] != '\0') {
3079 int num, i = (int)strlen(filename) - 1;
3082 while (isdigit(filename[i])) {
3088 num = atoi(&(filename[i]));
3091 snprintf(newfile, sizeof(newfile)-1, "%.13s%d", filename, num);
3092 strncpy(filename, newfile, 8);
3099 Newmenu_allowed_chars = demoname_allowed_chars;
3100 if (!Newdemo_no_space) {
3101 m[0].type=NM_TYPE_INPUT; m[0].text_len = 8; m[0].text = filename;
3102 exit = newmenu_do( NULL, TXT_SAVE_DEMO_AS, 1, &(m[0]), NULL );
3103 } else if (Newdemo_no_space == 1) {
3104 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_BAD;
3105 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3106 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3107 } else if (Newdemo_no_space == 2) {
3108 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_NOSPACE;
3109 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3110 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3112 Newmenu_allowed_chars = NULL;
3114 if (exit == -2) { // got bumped out from network menu
3115 char save_file[7+FILENAME_LEN];
3117 if (filename[0] != '\0') {
3118 strcpy(save_file, DEMO_DIR);
3119 strcat(save_file, filename);
3120 strcat(save_file, ".dem");
3122 sprintf (save_file, "%stmp%d.dem", DEMO_DIR, tmpcnt++);
3124 PHYSFSX_rename(DEMO_FILENAME, save_file);
3127 if (exit == -1) { // pressed ESC
3128 PHYSFS_delete(DEMO_FILENAME); // might as well remove the file
3129 return; // return without doing anything
3132 if (filename[0]==0) //null string
3135 //check to make sure name is ok
3136 for (s=filename;*s;s++)
3137 if (!isalnum(*s) && *s!='_') {
3138 nm_messagebox1(NULL, NULL,1,TXT_CONTINUE, TXT_DEMO_USE_LETTERS);
3142 if (Newdemo_no_space)
3143 strcat(fullname, m[1].text);
3145 strcat(fullname, m[0].text);
3146 strcat(fullname, ".dem");
3147 PHYSFS_delete(fullname);
3148 PHYSFSX_rename(DEMO_FILENAME, fullname);
3152 extern char AltHogDir[64];
3153 extern char AltHogdir_initialized;
3155 //returns the number of demo files on the disk
3156 int newdemo_count_demos()
3161 find = PHYSFS_enumerateFiles(DEMO_DIR);
3163 for (i = find; *i != NULL; i++)
3166 PHYSFS_freeList(find);
3171 void newdemo_start_playback(char * filename)
3173 char **find = NULL, **i;
3175 char filename2[PATH_MAX+FILENAME_LEN] = DEMO_DIR;
3178 change_playernum_to(0);
3180 First_time_playback=1;
3181 JasonPlaybackTotal=0;
3184 strcat(filename2, filename);
3187 // Randomly pick a filename
3188 int NumFiles = 0, RandFileNum;
3191 NumFiles = newdemo_count_demos();
3193 if ( NumFiles == 0 ) {
3194 return; // No files found!
3196 RandFileNum = d_rand() % NumFiles;
3199 find = PHYSFS_enumerateFiles(DEMO_DIR);
3201 for (i = find; *i != NULL; i++)
3203 if (NumFiles == RandFileNum)
3205 strcat(filename2, *i);
3211 PHYSFS_freeList(find);
3213 if (NumFiles > RandFileNum)
3217 infile = PHYSFSX_openReadBuffered(filename2);
3220 mprintf( (0, "Error reading '%s'\n", filename ));
3226 change_playernum_to(0); // force playernum to 0
3228 strncpy(nd_save_callsign, Players[Player_num].callsign, CALLSIGN_LEN);
3229 Viewer = ConsoleObject = &Objects[0]; // play properly as if console player
3230 if (newdemo_read_demo_start(rnd_demo)) {
3231 PHYSFS_close(infile);
3235 Game_mode = GM_NORMAL;
3236 Newdemo_state = ND_STATE_PLAYBACK;
3237 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3238 Newdemo_old_cockpit = Cockpit_mode.intval;
3239 Newdemo_size = (unsigned int)PHYSFS_fileLength(infile);
3242 NewdemoFrameCount = 0;
3243 Newdemo_players_cloaked = 0;
3244 playback_style = NORMAL_PLAYBACK;
3245 Function_mode = FMODE_GAME;
3246 cvar_setint(&Cockpit_3d_view[0], CV_NONE); // turn off 3d views on cockpit
3247 cvar_setint(&Cockpit_3d_view[1], CV_NONE); // turn off 3d views on cockpit
3248 newdemo_playback_one_frame(); // this one loads new level
3249 newdemo_playback_one_frame(); // get all of the objects to renderb game
3252 void newdemo_stop_playback()
3254 PHYSFS_close(infile);
3255 Newdemo_state = ND_STATE_NORMAL;
3257 change_playernum_to(0); //this is reality
3259 strncpy(Players[Player_num].callsign, nd_save_callsign, CALLSIGN_LEN);
3260 cvar_setint(&Cockpit_mode, Newdemo_old_cockpit);
3261 Game_mode = GM_GAME_OVER;
3262 Function_mode = FMODE_MENU;
3263 longjmp(LeaveGame,0); // Exit game loop
3269 #define BUF_SIZE 16384
3271 void newdemo_strip_frames(char *outname, int bytes_to_strip)
3273 PHYSFS_file *outfile;
3275 int total_size, bytes_done, read_elems, bytes_back;
3276 int trailer_start, loc1, loc2, stop_loc, bytes_to_read;
3277 short last_frame_length;
3280 total_size = (int)PHYSFS_fileLength(infile);
3281 outfile = PHYSFSX_openWriteBuffered(outname);
3282 if (outfile == NULL) {
3285 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't open output file";
3286 newmenu_do( NULL, NULL, 1, m, NULL );
3287 newdemo_stop_playback();
3290 buf = d_malloc(BUF_SIZE);
3294 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't malloc output buffer";
3295 newmenu_do( NULL, NULL, 1, m, NULL );
3296 PHYSFS_close(outfile);
3297 newdemo_stop_playback();
3301 trailer_start = (int)PHYSFS_tell(infile);
3302 PHYSFS_seek(infile, PHYSFS_tell(infile) + 11);
3304 while (bytes_back < bytes_to_strip) {
3305 loc1 = (int)PHYSFS_tell(infile);
3306 //PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
3307 //nd_read_short(&last_frame_length);
3308 //PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
3309 newdemo_back_frames(1);
3310 loc2 = (int)PHYSFS_tell(infile);
3311 bytes_back += (loc1 - loc2);
3313 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
3314 nd_read_short(&last_frame_length);
3315 PHYSFS_seek(infile, PHYSFS_tell(infile) - 3);
3316 stop_loc = (int)PHYSFS_tell(infile);
3317 PHYSFS_seek(infile, 0);
3318 while (stop_loc > 0) {
3319 if (stop_loc < BUF_SIZE)
3320 bytes_to_read = stop_loc;
3322 bytes_to_read = BUF_SIZE;
3323 read_elems = (int)PHYSFS_read(infile, buf, 1, bytes_to_read);
3324 PHYSFS_write(outfile, buf, 1, read_elems);
3325 stop_loc -= read_elems;
3327 stop_loc = (int)PHYSFS_tell(outfile);
3328 PHYSFS_seek(infile, trailer_start);
3329 while ((read_elems = (int)PHYSFS_read(infile, buf, 1, BUF_SIZE)) != 0)
3330 PHYSFS_write(outfile, buf, 1, read_elems);
3331 PHYSFS_seek(outfile, stop_loc);
3332 PHYSFS_seek(outfile, PHYSFS_tell(infile) + 1);
3333 PHYSFS_write(outfile, &last_frame_length, 2, 1);
3334 PHYSFS_close(outfile);
3335 newdemo_stop_playback();
3341 object DemoRightExtra,DemoLeftExtra;
3342 ubyte DemoDoRight=0,DemoDoLeft=0;
3344 void nd_render_extras (ubyte which,object *obj)
3347 ubyte type=which&15;
3351 Int3(); // how'd we get here?
3352 do_cockpit_window_view(w,NULL,0,WBU_WEAPON,NULL);
3358 memcpy (&DemoRightExtra,obj,sizeof(object)); DemoDoRight=type;
3362 memcpy (&DemoLeftExtra,obj,sizeof(object)); DemoDoLeft=type;
3367 void DoJasonInterpolate (fix recorded_time)
3371 JasonPlaybackTotal+=FrameTime;
3373 if (!First_time_playback)
3375 // get the difference between the recorded time and the playback time
3376 the_delay=(recorded_time - FrameTime);
3377 //mprintf ((0,"The delay=%d\n", f2i(the_delay)));
3378 if (the_delay >= f0_0)
3381 timer_delay(the_delay);
3386 while (JasonPlaybackTotal > nd_recorded_total)
3387 if (newdemo_read_frame_information() == -1)
3389 newdemo_stop_playback();
3393 //the_delay = nd_recorded_total - JasonPlaybackTotal;
3394 //if (the_delay > f0_0)
3395 // timer_delay(the_delay);
3400 First_time_playback=0;
3404 #pragma global_optimizer reset