1 /* $Id: newdemo.c,v 1.21 2005-07-21 09:36:24 chris 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>
102 #include "editor/editor.h"
106 #pragma global_optimizer off // pretty much sucks...need to look into this
109 void DoJasonInterpolate (fix recorded_time);
111 //#include "nocfile.h"
113 //Does demo start automatically?
116 sbyte WasRecorded [MAX_OBJECTS];
117 sbyte ViewWasRecorded[MAX_OBJECTS];
118 sbyte RenderingWasRecorded[32];
120 #define ND_EVENT_EOF 0 // EOF
121 #define ND_EVENT_START_DEMO 1 // Followed by 16 character, NULL terminated filename of .SAV file to use
122 #define ND_EVENT_START_FRAME 2 // Followed by integer frame number, then a fix FrameTime
123 #define ND_EVENT_VIEWER_OBJECT 3 // Followed by an object structure
124 #define ND_EVENT_RENDER_OBJECT 4 // Followed by an object structure
125 #define ND_EVENT_SOUND 5 // Followed by int soundum
126 #define ND_EVENT_SOUND_ONCE 6 // Followed by int soundum
127 #define ND_EVENT_SOUND_3D 7 // Followed by int soundum, int angle, int volume
128 #define ND_EVENT_WALL_HIT_PROCESS 8 // Followed by int segnum, int side, fix damage
129 #define ND_EVENT_TRIGGER 9 // Followed by int segnum, int side, int objnum
130 #define ND_EVENT_HOSTAGE_RESCUED 10 // Followed by int hostage_type
131 #define ND_EVENT_SOUND_3D_ONCE 11 // Followed by int soundum, int angle, int volume
132 #define ND_EVENT_MORPH_FRAME 12 // Followed by ? data
133 #define ND_EVENT_WALL_TOGGLE 13 // Followed by int seg, int side
134 #define ND_EVENT_HUD_MESSAGE 14 // Followed by char size, char * string (+null)
135 #define ND_EVENT_CONTROL_CENTER_DESTROYED 15 // Just a simple flag
136 #define ND_EVENT_PALETTE_EFFECT 16 // Followed by short r,g,b
137 #define ND_EVENT_PLAYER_ENERGY 17 // followed by byte energy
138 #define ND_EVENT_PLAYER_SHIELD 18 // followed by byte shields
139 #define ND_EVENT_PLAYER_FLAGS 19 // followed by player flags
140 #define ND_EVENT_PLAYER_WEAPON 20 // followed by weapon type and weapon number
141 #define ND_EVENT_EFFECT_BLOWUP 21 // followed by segment, side, and pnt
142 #define ND_EVENT_HOMING_DISTANCE 22 // followed by homing distance
143 #define ND_EVENT_LETTERBOX 23 // letterbox mode for death seq.
144 #define ND_EVENT_RESTORE_COCKPIT 24 // restore cockpit after death
145 #define ND_EVENT_REARVIEW 25 // going to rear view mode
146 #define ND_EVENT_WALL_SET_TMAP_NUM1 26 // Wall changed
147 #define ND_EVENT_WALL_SET_TMAP_NUM2 27 // Wall changed
148 #define ND_EVENT_NEW_LEVEL 28 // followed by level number
149 #define ND_EVENT_MULTI_CLOAK 29 // followed by player num
150 #define ND_EVENT_MULTI_DECLOAK 30 // followed by player num
151 #define ND_EVENT_RESTORE_REARVIEW 31 // restore cockpit after rearview mode
152 #define ND_EVENT_MULTI_DEATH 32 // with player number
153 #define ND_EVENT_MULTI_KILL 33 // with player number
154 #define ND_EVENT_MULTI_CONNECT 34 // with player number
155 #define ND_EVENT_MULTI_RECONNECT 35 // with player number
156 #define ND_EVENT_MULTI_DISCONNECT 36 // with player number
157 #define ND_EVENT_MULTI_SCORE 37 // playernum / score
158 #define ND_EVENT_PLAYER_SCORE 38 // followed by score
159 #define ND_EVENT_PRIMARY_AMMO 39 // with old/new ammo count
160 #define ND_EVENT_SECONDARY_AMMO 40 // with old/new ammo count
161 #define ND_EVENT_DOOR_OPENING 41 // with segment/side
162 #define ND_EVENT_LASER_LEVEL 42 // no data
163 #define ND_EVENT_PLAYER_AFTERBURNER 43 // followed by byte old ab, current ab
164 #define ND_EVENT_CLOAKING_WALL 44 // info changing while wall cloaking
165 #define ND_EVENT_CHANGE_COCKPIT 45 // change the cockpit
166 #define ND_EVENT_START_GUIDED 46 // switch to guided view
167 #define ND_EVENT_END_GUIDED 47 // stop guided view/return to ship
168 #define ND_EVENT_SECRET_THINGY 48 // 0/1 = secret exit functional/non-functional
169 #define ND_EVENT_LINK_SOUND_TO_OBJ 49 // record digi_link_sound_to_object3
170 #define ND_EVENT_KILL_SOUND_TO_OBJ 50 // record digi_kill_sound_linked_to_object
173 #define NORMAL_PLAYBACK 0
174 #define SKIP_PLAYBACK 1
175 #define INTERPOLATE_PLAYBACK 2
176 #define INTERPOL_FACTOR (F1_0 + (F1_0/5))
178 #define DEMO_VERSION_D1_SHARE 5
179 #define DEMO_VERSION_D1 13
180 #define DEMO_VERSION_D2 15
181 #define DEMO_VERSION DEMO_VERSION_D2
183 #define DEMO_GAME_TYPE_D1_SHARE 1
184 #define DEMO_GAME_TYPE_D1 2
185 #define DEMO_GAME_TYPE_D2 3
186 #define DEMO_GAME_TYPE DEMO_GAME_TYPE_D2
188 #define DEMO_FILENAME DEMO_DIR "tmpdemo.dem"
190 #define DEMO_MAX_LEVELS 29
193 char nd_save_callsign[CALLSIGN_LEN+1];
194 int Newdemo_state = 0;
195 int Newdemo_vcr_state = 0;
196 int Newdemo_start_frame = -1;
197 unsigned int Newdemo_size;
198 int Newdemo_num_written;
199 int Newdemo_game_mode;
200 sbyte Newdemo_game_type;
201 int Newdemo_old_cockpit;
202 int Newdemo_is_d2demo = 0;
203 sbyte Newdemo_no_space;
204 sbyte Newdemo_at_eof;
205 sbyte Newdemo_do_interpolate = 0; // 1
206 sbyte Newdemo_players_cloaked;
207 sbyte Newdemo_warning_given = 0;
208 sbyte Newdemo_cntrlcen_destroyed = 0;
209 static sbyte nd_bad_read;
210 int NewdemoFrameCount;
211 short frame_bytes_written = 0;
212 fix nd_playback_total;
213 fix nd_recorded_total;
214 fix nd_recorded_time;
215 sbyte playback_style;
216 sbyte First_time_playback=1;
217 fix JasonPlaybackTotal=0;
221 PHYSFS_file *outfile = NULL;
223 int newdemo_get_percent_done() {
224 if ( Newdemo_state == ND_STATE_PLAYBACK ) {
225 return (PHYSFS_tell(infile) * 100) / Newdemo_size;
227 if ( Newdemo_state == ND_STATE_RECORDING ) {
228 return PHYSFS_tell(outfile);
233 #define VEL_PRECISION 12
235 void my_extract_shortpos(object *objp, shortpos *spp)
241 objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
242 objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
243 objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
245 objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
246 objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
247 objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
249 objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
250 objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
251 objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
253 segnum = spp->segment;
254 objp->segnum = segnum;
256 objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x;
257 objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y;
258 objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z;
260 objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION);
261 objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION);
262 objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION);
265 int newdemo_read( void *buffer, int elsize, int nelem )
268 num_read = PHYSFS_read(infile, buffer, elsize, nelem);
269 if (num_read < nelem || PHYSFS_eof(infile))
275 int newdemo_find_object( int signature )
280 for (i=0; i<=Highest_object_index; i++, objp++ ) {
281 if ( (objp->type != OBJ_NONE) && (objp->signature == signature))
287 int newdemo_write( void *buffer, int elsize, int nelem )
289 int num_written, total_size;
291 total_size = elsize * nelem;
292 frame_bytes_written += total_size;
293 Newdemo_num_written += total_size;
294 Assert(outfile != NULL);
295 num_written = PHYSFS_write(outfile, buffer, elsize, nelem);
296 //if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) {
297 // Newdemo_no_space=1;
298 // newdemo_stop_recording();
301 if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space)
303 if (num_written == nelem && !Newdemo_no_space)
307 newdemo_stop_recording();
312 * The next bunch of files taken from Matt's gamesave.c. We have to modify
313 * these since the demo must save more information about objects that
317 static void nd_write_byte(sbyte b)
319 newdemo_write(&b, 1, 1);
322 static void nd_write_short(short s)
324 newdemo_write(&s, 2, 1);
327 static void nd_write_int(int i)
329 newdemo_write(&i, 4, 1);
332 static void nd_write_string(char *str)
334 nd_write_byte(strlen(str) + 1);
335 newdemo_write(str, strlen(str) + 1, 1);
338 static void nd_write_fix(fix f)
340 newdemo_write(&f, sizeof(fix), 1);
343 static void nd_write_fixang(fixang f)
345 newdemo_write(&f, sizeof(fixang), 1);
348 static void nd_write_vector(vms_vector *v)
355 static void nd_write_angvec(vms_angvec *v)
357 nd_write_fixang(v->p);
358 nd_write_fixang(v->b);
359 nd_write_fixang(v->h);
362 void nd_write_shortpos(object *obj)
368 create_shortpos(&sp, obj, 0);
370 render_type = obj->render_type;
371 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
372 for (i = 0; i < 9; i++)
373 nd_write_byte(sp.bytemat[i]);
374 for (i = 0; i < 9; i++) {
375 if (sp.bytemat[i] != 0)
379 Int3(); // contact Allender about this.
383 nd_write_short(sp.xo);
384 nd_write_short(sp.yo);
385 nd_write_short(sp.zo);
386 nd_write_short(sp.segment);
387 nd_write_short(sp.velx);
388 nd_write_short(sp.vely);
389 nd_write_short(sp.velz);
392 static void nd_read_byte(sbyte *b)
394 newdemo_read(b, 1, 1);
397 static void nd_read_short(short *s)
399 newdemo_read(s, 2, 1);
402 static void nd_read_int(int *i)
404 newdemo_read(i, 4, 1);
407 static void nd_read_string(char *str)
412 newdemo_read(str, len, 1);
415 static void nd_read_fix(fix *f)
417 newdemo_read(f, sizeof(fix), 1);
420 static void nd_read_fixang(fixang *f)
422 newdemo_read(f, sizeof(fixang), 1);
425 static void nd_read_vector(vms_vector *v)
427 nd_read_fix(&(v->x));
428 nd_read_fix(&(v->y));
429 nd_read_fix(&(v->z));
432 static void nd_read_angvec(vms_angvec *v)
434 nd_read_fixang(&(v->p));
435 nd_read_fixang(&(v->b));
436 nd_read_fixang(&(v->h));
439 static void nd_read_shortpos(object *obj)
445 render_type = obj->render_type;
446 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
447 for (i = 0; i < 9; i++)
448 nd_read_byte(&(sp.bytemat[i]));
451 nd_read_short(&(sp.xo));
452 nd_read_short(&(sp.yo));
453 nd_read_short(&(sp.zo));
454 nd_read_short(&(sp.segment));
455 nd_read_short(&(sp.velx));
456 nd_read_short(&(sp.vely));
457 nd_read_short(&(sp.velz));
459 my_extract_shortpos(obj, &sp);
460 if ((obj->id == VCLIP_MORPHING_ROBOT) && (render_type == RT_FIREBALL) && (obj->control_type == CT_EXPLOSION))
461 extract_orient_from_segment(&obj->orient,&Segments[obj->segnum]);
465 object *prev_obj=NULL; //ptr to last object read in
467 void nd_read_object(object *obj)
469 memset(obj, 0, sizeof(object));
472 * Do render type first, since with render_type == RT_NONE, we
473 * blow by all other object information
475 nd_read_byte((sbyte *) &(obj->render_type));
476 nd_read_byte((sbyte *) &(obj->type));
477 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
480 nd_read_byte((sbyte *) &(obj->id));
481 nd_read_byte((sbyte *) &(obj->flags));
482 nd_read_short((short *)&(obj->signature));
483 nd_read_shortpos(obj);
485 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
488 obj->attached_obj = -1;
493 obj->control_type = CT_POWERUP;
494 obj->movement_type = MT_NONE;
495 obj->size = HOSTAGE_SIZE;
499 obj->control_type = CT_AI;
500 // (MarkA and MikeK said we should not do the crazy last secret stuff with multiple reactors...
501 // This necessary code is our vindication. --MK, 2/15/96)
502 if (obj->id != SPECIAL_REACTOR_ROBOT)
503 obj->movement_type = MT_PHYSICS;
505 obj->movement_type = MT_NONE;
506 obj->size = Polygon_models[Robot_info[obj->id].model_num].rad;
507 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
508 obj->rtype.pobj_info.subobj_flags = 0;
509 obj->ctype.ai_info.CLOAKED = (Robot_info[obj->id].cloak_type?1:0);
513 obj->control_type = CT_POWERUP;
514 nd_read_byte((sbyte *) &(obj->movement_type)); // might have physics movement
515 obj->size = Powerup_info[obj->id].size;
519 obj->control_type = CT_NONE;
520 obj->movement_type = MT_PHYSICS;
521 obj->size = Polygon_models[Player_ship->model_num].rad;
522 obj->rtype.pobj_info.model_num = Player_ship->model_num;
523 obj->rtype.pobj_info.subobj_flags = 0;
527 obj->control_type = CT_NONE;
528 obj->movement_type = MT_NONE;
529 obj->size = Polygon_models[obj->id].rad;
530 obj->rtype.pobj_info.model_num = obj->id;
531 obj->rtype.pobj_info.subobj_flags = 0;
535 nd_read_byte((sbyte *) &(obj->control_type));
536 nd_read_byte((sbyte *) &(obj->movement_type));
537 nd_read_fix(&(obj->size));
542 nd_read_vector(&(obj->last_pos));
543 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
544 nd_read_fix(&(obj->lifeleft));
549 obj->lifeleft = (fix)b;
550 // MWA old way -- won't work with big endian machines nd_read_byte((ubyte *)&(obj->lifeleft));
551 obj->lifeleft = (fix)((int)obj->lifeleft << 12);
554 if (obj->type == OBJ_ROBOT) {
555 if (Robot_info[obj->id].boss_flag) {
558 nd_read_byte(&cloaked);
559 obj->ctype.ai_info.CLOAKED = cloaked;
563 switch (obj->movement_type) {
566 nd_read_vector(&(obj->mtype.phys_info.velocity));
567 nd_read_vector(&(obj->mtype.phys_info.thrust));
571 nd_read_vector(&(obj->mtype.spin_rate));
581 switch (obj->control_type) {
585 nd_read_fix(&(obj->ctype.expl_info.spawn_time));
586 nd_read_fix(&(obj->ctype.expl_info.delete_time));
587 nd_read_short(&(obj->ctype.expl_info.delete_objnum));
589 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
591 if (obj->flags & OF_ATTACHED) { //attach to previous object
592 Assert(prev_obj!=NULL);
593 if (prev_obj->control_type == CT_EXPLOSION) {
594 if (prev_obj->flags & OF_ATTACHED && prev_obj->ctype.expl_info.attach_parent!=-1)
595 obj_attach(&Objects[prev_obj->ctype.expl_info.attach_parent],obj);
597 obj->flags &= ~OF_ATTACHED;
600 obj_attach(prev_obj,obj);
606 nd_read_fix(&(obj->ctype.light_info.intensity));
628 switch (obj->render_type) {
637 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
638 nd_read_int(&(obj->rtype.pobj_info.model_num));
639 if (Newdemo_game_type < DEMO_GAME_TYPE_D2)
640 obj->rtype.pobj_info.model_num = D1_Polymodel_map[obj->rtype.pobj_info.model_num];
641 else if (Newdemo_is_d2demo)
642 obj->rtype.pobj_info.model_num = D2Demo_Polymodel_map[obj->rtype.pobj_info.model_num];
643 if (obj->rtype.pobj_info.model_num < 0)
645 nd_read_int(&(obj->rtype.pobj_info.subobj_flags));
648 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
650 for (i=0;i<MAX_SUBMODELS;i++)
651 nd_read_angvec(&(obj->pobj_info.anim_angles[i]));
653 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
654 nd_read_angvec(&obj->rtype.pobj_info.anim_angles[i]);
659 obj->rtype.pobj_info.tmap_override = tmo;
662 obj->rtype.pobj_info.tmap_override = -1;
664 int xlated_tmo = tmap_xlate_table[tmo];
665 if (xlated_tmo < 0) {
666 //mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->pobj_info.model_num));
670 obj->rtype.pobj_info.tmap_override = xlated_tmo;
678 case RT_WEAPON_VCLIP:
681 nd_read_int(&(obj->rtype.vclip_info.vclip_num));
682 nd_read_fix(&(obj->rtype.vclip_info.frametime));
683 nd_read_byte(&(obj->rtype.vclip_info.framenum));
697 void nd_write_object(object *obj)
701 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
705 * Do render_type first so on read, we can make determination of
706 * what else to read in
708 nd_write_byte(obj->render_type);
709 nd_write_byte(obj->type);
710 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
713 nd_write_byte(obj->id);
714 nd_write_byte(obj->flags);
715 nd_write_short((short)obj->signature);
716 nd_write_shortpos(obj);
718 if ((obj->type != OBJ_HOSTAGE) && (obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_POWERUP) && (obj->type != OBJ_CLUTTER)) {
719 nd_write_byte(obj->control_type);
720 nd_write_byte(obj->movement_type);
721 nd_write_fix(obj->size);
723 if (obj->type == OBJ_POWERUP)
724 nd_write_byte(obj->movement_type);
726 nd_write_vector(&obj->last_pos);
728 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
729 nd_write_fix(obj->lifeleft);
731 life = (int)obj->lifeleft;
735 nd_write_byte((ubyte)life);
738 if (obj->type == OBJ_ROBOT) {
739 if (Robot_info[obj->id].boss_flag) {
740 if ((GameTime > Boss_cloak_start_time) && (GameTime < Boss_cloak_end_time))
747 switch (obj->movement_type) {
750 nd_write_vector(&obj->mtype.phys_info.velocity);
751 nd_write_vector(&obj->mtype.phys_info.thrust);
755 nd_write_vector(&obj->mtype.spin_rate);
765 switch (obj->control_type) {
771 nd_write_fix(obj->ctype.expl_info.spawn_time);
772 nd_write_fix(obj->ctype.expl_info.delete_time);
773 nd_write_short(obj->ctype.expl_info.delete_objnum);
781 nd_write_fix(obj->ctype.light_info.intensity);
788 case CT_SLEW: //the player is generally saved as slew
801 switch (obj->render_type) {
810 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
811 nd_write_int(obj->rtype.pobj_info.model_num);
812 nd_write_int(obj->rtype.pobj_info.subobj_flags);
815 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
817 for (i=0;i<MAX_SUBMODELS;i++)
818 nd_write_angvec(&obj->pobj_info.anim_angles[i]);
820 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
821 nd_write_angvec(&obj->rtype.pobj_info.anim_angles[i]);
823 nd_write_int(obj->rtype.pobj_info.tmap_override);
829 case RT_WEAPON_VCLIP:
832 nd_write_int(obj->rtype.vclip_info.vclip_num);
833 nd_write_fix(obj->rtype.vclip_info.frametime);
834 nd_write_byte(obj->rtype.vclip_info.framenum);
847 int JustStartedRecording=0,JustStartedPlayback=0;
849 void newdemo_record_start_demo()
854 nd_write_byte(ND_EVENT_START_DEMO);
855 nd_write_byte(DEMO_VERSION);
856 nd_write_byte(DEMO_GAME_TYPE);
857 nd_write_fix(GameTime);
860 if (Game_mode & GM_MULTI)
861 nd_write_int(Game_mode | (Player_num << 16));
864 // NOTE LINK TO ABOVE!!!
865 nd_write_int(Game_mode);
868 if (Game_mode & GM_TEAM) {
869 nd_write_byte(Netgame.team_vector);
870 nd_write_string(Netgame.team_name[0]);
871 nd_write_string(Netgame.team_name[1]);
874 if (Game_mode & GM_MULTI) {
875 nd_write_byte((sbyte)N_players);
876 for (i = 0; i < N_players; i++) {
877 nd_write_string(Players[i].callsign);
878 nd_write_byte(Players[i].connected);
880 if (Game_mode & GM_MULTI_COOP) {
881 nd_write_int(Players[i].score);
883 nd_write_short((short)Players[i].net_killed_total);
884 nd_write_short((short)Players[i].net_kills_total);
889 // NOTE LINK TO ABOVE!!!
890 nd_write_int(Players[Player_num].score);
892 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
893 nd_write_short((short)Players[Player_num].primary_ammo[i]);
895 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
896 nd_write_short((short)Players[Player_num].secondary_ammo[i]);
898 nd_write_byte((sbyte)Players[Player_num].laser_level);
900 // Support for missions added here
902 nd_write_string(Current_mission_filename);
904 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
905 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
906 nd_write_int(Players[Player_num].flags); // be sure players flags are set
907 nd_write_byte((sbyte)Primary_weapon);
908 nd_write_byte((sbyte)Secondary_weapon);
909 Newdemo_start_frame = FrameCount;
910 JustStartedRecording=1;
912 newdemo_set_new_level(Current_level_num);
917 void newdemo_record_start_frame(int frame_number, fix frame_time )
921 if (Newdemo_no_space) {
922 newdemo_stop_playback();
928 for (i=0;i<MAX_OBJECTS;i++)
931 ViewWasRecorded[i]=0;
934 RenderingWasRecorded[i]=0;
936 frame_number -= Newdemo_start_frame;
938 Assert(frame_number >= 0 );
940 nd_write_byte(ND_EVENT_START_FRAME);
941 nd_write_short(frame_bytes_written - 1); // from previous frame
942 frame_bytes_written=3;
943 nd_write_int(frame_number);
944 nd_write_int(frame_time);
949 void newdemo_record_render_object(object * obj)
951 if (ViewWasRecorded[OBJECT_NUMBER(obj)])
954 //if (obj==&Objects[Players[Player_num].objnum] && !Player_is_dead)
958 nd_write_byte(ND_EVENT_RENDER_OBJECT);
959 nd_write_object(obj);
963 extern ubyte RenderingType;
965 void newdemo_record_viewer_object(object * obj)
968 if (ViewWasRecorded[OBJECT_NUMBER(obj)] && (ViewWasRecorded[OBJECT_NUMBER(obj)] - 1) == RenderingType)
970 //if (WasRecorded[OBJECT_NUMBER(obj)])
972 if (RenderingWasRecorded[RenderingType])
975 ViewWasRecorded[OBJECT_NUMBER(obj)] = RenderingType + 1;
976 RenderingWasRecorded[RenderingType]=1;
978 nd_write_byte(ND_EVENT_VIEWER_OBJECT);
979 nd_write_byte(RenderingType);
980 nd_write_object(obj);
984 void newdemo_record_sound( int soundno )
987 nd_write_byte(ND_EVENT_SOUND);
988 nd_write_int( soundno );
992 //--unused-- void newdemo_record_sound_once( int soundno ) {
993 //--unused-- stop_time();
994 //--unused-- nd_write_byte( ND_EVENT_SOUND_ONCE );
995 //--unused-- nd_write_int( soundno );
996 //--unused-- start_time();
1000 void newdemo_record_cockpit_change (int mode)
1003 nd_write_byte (ND_EVENT_CHANGE_COCKPIT);
1009 void newdemo_record_sound_3d( int soundno, int angle, int volume )
1012 nd_write_byte( ND_EVENT_SOUND_3D );
1013 nd_write_int( soundno );
1014 nd_write_int( angle );
1015 nd_write_int( volume );
1019 void newdemo_record_sound_3d_once( int soundno, int angle, int volume )
1022 nd_write_byte( ND_EVENT_SOUND_3D_ONCE );
1023 nd_write_int( soundno );
1024 nd_write_int( angle );
1025 nd_write_int( volume );
1030 void newdemo_record_link_sound_to_object3( int soundno, short objnum, fix max_volume, fix max_distance, int loop_start, int loop_end )
1033 nd_write_byte( ND_EVENT_LINK_SOUND_TO_OBJ );
1034 nd_write_int( soundno );
1035 nd_write_int( Objects[objnum].signature );
1036 nd_write_int( max_volume );
1037 nd_write_int( max_distance );
1038 nd_write_int( loop_start );
1039 nd_write_int( loop_end );
1043 void newdemo_record_kill_sound_linked_to_object( int objnum )
1046 nd_write_byte( ND_EVENT_KILL_SOUND_TO_OBJ );
1047 nd_write_int( Objects[objnum].signature );
1052 void newdemo_record_wall_hit_process( int segnum, int side, int damage, int playernum )
1058 //playernum = playernum;
1059 nd_write_byte( ND_EVENT_WALL_HIT_PROCESS );
1060 nd_write_int( segnum );
1061 nd_write_int( side );
1062 nd_write_int( damage );
1063 nd_write_int( playernum );
1067 void newdemo_record_guided_start ()
1069 nd_write_byte (ND_EVENT_START_GUIDED);
1072 void newdemo_record_guided_end ()
1074 nd_write_byte (ND_EVENT_END_GUIDED);
1077 void newdemo_record_secret_exit_blown(int truth)
1080 nd_write_byte( ND_EVENT_SECRET_THINGY );
1081 nd_write_int( truth );
1085 void newdemo_record_trigger( int segnum, int side, int objnum,int shot )
1088 nd_write_byte( ND_EVENT_TRIGGER );
1089 nd_write_int( segnum );
1090 nd_write_int( side );
1091 nd_write_int( objnum );
1096 void newdemo_record_hostage_rescued( int hostage_number ) {
1098 nd_write_byte( ND_EVENT_HOSTAGE_RESCUED );
1099 nd_write_int( hostage_number );
1103 void newdemo_record_morph_frame(morph_data *md)
1107 nd_write_byte( ND_EVENT_MORPH_FRAME );
1109 newdemo_write( md->morph_vecs, sizeof(md->morph_vecs), 1 );
1110 newdemo_write( md->submodel_active, sizeof(md->submodel_active), 1 );
1111 newdemo_write( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 );
1113 nd_write_object( md->obj );
1117 void newdemo_record_wall_toggle( int segnum, int side )
1120 nd_write_byte( ND_EVENT_WALL_TOGGLE );
1121 nd_write_int( segnum );
1122 nd_write_int( side );
1126 void newdemo_record_control_center_destroyed()
1129 nd_write_byte( ND_EVENT_CONTROL_CENTER_DESTROYED );
1130 nd_write_int( Countdown_seconds_left );
1134 void newdemo_record_hud_message( char * message )
1137 nd_write_byte( ND_EVENT_HUD_MESSAGE );
1138 nd_write_string(message);
1142 void newdemo_record_palette_effect(short r, short g, short b )
1145 nd_write_byte( ND_EVENT_PALETTE_EFFECT );
1146 nd_write_short( r );
1147 nd_write_short( g );
1148 nd_write_short( b );
1152 void newdemo_record_player_energy(int old_energy, int energy)
1155 nd_write_byte( ND_EVENT_PLAYER_ENERGY );
1156 nd_write_byte((sbyte) old_energy);
1157 nd_write_byte((sbyte) energy);
1161 void newdemo_record_player_afterburner(fix old_afterburner, fix afterburner)
1164 nd_write_byte( ND_EVENT_PLAYER_AFTERBURNER );
1165 nd_write_byte((sbyte) (old_afterburner>>9));
1166 nd_write_byte((sbyte) (afterburner>>9));
1170 void newdemo_record_player_shields(int old_shield, int shield)
1173 nd_write_byte( ND_EVENT_PLAYER_SHIELD );
1174 nd_write_byte((sbyte)old_shield);
1175 nd_write_byte((sbyte)shield);
1179 void newdemo_record_player_flags(uint oflags, uint flags)
1182 nd_write_byte( ND_EVENT_PLAYER_FLAGS );
1183 nd_write_int(((short)oflags << 16) | (short)flags);
1187 void newdemo_record_player_weapon(int weapon_type, int weapon_num)
1190 nd_write_byte( ND_EVENT_PLAYER_WEAPON );
1191 nd_write_byte((sbyte)weapon_type);
1192 nd_write_byte((sbyte)weapon_num);
1194 nd_write_byte((sbyte)Secondary_weapon);
1196 nd_write_byte((sbyte)Primary_weapon);
1200 void newdemo_record_effect_blowup(short segment, int side, vms_vector *pnt)
1203 nd_write_byte (ND_EVENT_EFFECT_BLOWUP);
1204 nd_write_short(segment);
1205 nd_write_byte((sbyte)side);
1206 nd_write_vector(pnt);
1210 void newdemo_record_homing_distance(fix distance)
1213 nd_write_byte(ND_EVENT_HOMING_DISTANCE);
1214 nd_write_short((short)(distance>>16));
1218 void newdemo_record_letterbox(void)
1221 nd_write_byte(ND_EVENT_LETTERBOX);
1225 void newdemo_record_rearview(void)
1228 nd_write_byte(ND_EVENT_REARVIEW);
1232 void newdemo_record_restore_cockpit(void)
1235 nd_write_byte(ND_EVENT_RESTORE_COCKPIT);
1239 void newdemo_record_restore_rearview(void)
1242 nd_write_byte(ND_EVENT_RESTORE_REARVIEW);
1246 void newdemo_record_wall_set_tmap_num1(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1249 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM1);
1250 nd_write_short(seg);
1251 nd_write_byte(side);
1252 nd_write_short(cseg);
1253 nd_write_byte(cside);
1254 nd_write_short(tmap);
1258 void newdemo_record_wall_set_tmap_num2(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1261 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM2);
1262 nd_write_short(seg);
1263 nd_write_byte(side);
1264 nd_write_short(cseg);
1265 nd_write_byte(cside);
1266 nd_write_short(tmap);
1270 void newdemo_record_multi_cloak(int pnum)
1273 nd_write_byte(ND_EVENT_MULTI_CLOAK);
1274 nd_write_byte((sbyte)pnum);
1278 void newdemo_record_multi_decloak(int pnum)
1281 nd_write_byte(ND_EVENT_MULTI_DECLOAK);
1282 nd_write_byte((sbyte)pnum);
1286 void newdemo_record_multi_death(int pnum)
1289 nd_write_byte(ND_EVENT_MULTI_DEATH);
1290 nd_write_byte((sbyte)pnum);
1294 void newdemo_record_multi_kill(int pnum, sbyte kill)
1297 nd_write_byte(ND_EVENT_MULTI_KILL);
1298 nd_write_byte((sbyte)pnum);
1299 nd_write_byte(kill);
1303 void newdemo_record_multi_connect(int pnum, int new_player, char *new_callsign)
1306 nd_write_byte(ND_EVENT_MULTI_CONNECT);
1307 nd_write_byte((sbyte)pnum);
1308 nd_write_byte((sbyte)new_player);
1310 nd_write_string(Players[pnum].callsign);
1311 nd_write_int(Players[pnum].net_killed_total);
1312 nd_write_int(Players[pnum].net_kills_total);
1314 nd_write_string(new_callsign);
1318 void newdemo_record_multi_reconnect(int pnum)
1321 nd_write_byte(ND_EVENT_MULTI_RECONNECT);
1322 nd_write_byte((sbyte)pnum);
1326 void newdemo_record_multi_disconnect(int pnum)
1329 nd_write_byte(ND_EVENT_MULTI_DISCONNECT);
1330 nd_write_byte((sbyte)pnum);
1334 void newdemo_record_player_score(int score)
1337 nd_write_byte(ND_EVENT_PLAYER_SCORE);
1338 nd_write_int(score);
1342 void newdemo_record_multi_score(int pnum, int score)
1345 nd_write_byte(ND_EVENT_MULTI_SCORE);
1346 nd_write_byte((sbyte)pnum);
1347 nd_write_int(score - Players[pnum].score); // called before score is changed!!!!
1351 void newdemo_record_primary_ammo(int old_ammo, int new_ammo)
1354 nd_write_byte(ND_EVENT_PRIMARY_AMMO);
1356 nd_write_short((short)new_ammo);
1358 nd_write_short((short)old_ammo);
1359 nd_write_short((short)new_ammo);
1363 void newdemo_record_secondary_ammo(int old_ammo, int new_ammo)
1366 nd_write_byte(ND_EVENT_SECONDARY_AMMO);
1368 nd_write_short((short)new_ammo);
1370 nd_write_short((short)old_ammo);
1371 nd_write_short((short)new_ammo);
1375 void newdemo_record_door_opening(int segnum, int side)
1378 nd_write_byte(ND_EVENT_DOOR_OPENING);
1379 nd_write_short((short)segnum);
1380 nd_write_byte((sbyte)side);
1384 void newdemo_record_laser_level(sbyte old_level, sbyte new_level)
1387 nd_write_byte(ND_EVENT_LASER_LEVEL);
1388 nd_write_byte(old_level);
1389 nd_write_byte(new_level);
1393 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)
1395 Assert(front_wall_num <= 255 && back_wall_num <= 255);
1398 nd_write_byte(ND_EVENT_CLOAKING_WALL);
1399 nd_write_byte(front_wall_num);
1400 nd_write_byte(back_wall_num);
1401 nd_write_byte(type);
1402 nd_write_byte(state);
1403 nd_write_byte(cloak_value);
1404 nd_write_short(l0>>8);
1405 nd_write_short(l1>>8);
1406 nd_write_short(l2>>8);
1407 nd_write_short(l3>>8);
1411 void newdemo_set_new_level(int level_num)
1418 nd_write_byte(ND_EVENT_NEW_LEVEL);
1419 nd_write_byte((sbyte)level_num);
1420 nd_write_byte((sbyte)Current_level_num);
1422 if (JustStartedRecording==1)
1424 nd_write_int(Num_walls);
1425 for (i=0;i<Num_walls;i++)
1427 nd_write_byte (Walls[i].type);
1428 nd_write_byte (Walls[i].flags);
1429 nd_write_byte (Walls[i].state);
1431 seg = &Segments[Walls[i].segnum];
1432 side = Walls[i].sidenum;
1433 nd_write_short (seg->sides[side].tmap_num);
1434 nd_write_short (seg->sides[side].tmap_num2);
1435 JustStartedRecording=0;
1442 int newdemo_read_demo_start(int rnd_demo)
1444 sbyte i, version, laser_level;
1445 sbyte c, energy, shield;
1446 char text[128], current_mission[9];
1449 if ((c != ND_EVENT_START_DEMO) || nd_bad_read) {
1452 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_CORRUPT);
1453 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1454 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1457 nd_read_byte(&version);
1458 nd_read_byte(&Newdemo_game_type);
1459 if (Newdemo_game_type < DEMO_GAME_TYPE_D1) {
1462 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1463 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1464 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Descent: First Strike";
1466 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1469 if (Newdemo_game_type > DEMO_GAME_TYPE) {
1472 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1473 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1474 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Unknown Descent version";
1476 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1479 if (version < DEMO_VERSION_D1) {
1482 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_OLD);
1483 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1484 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1488 nd_read_fix(&GameTime);
1489 Boss_cloak_start_time=Boss_cloak_end_time=GameTime;
1490 JasonPlaybackTotal=0;
1492 nd_read_int(&Newdemo_game_mode);
1495 change_playernum_to((Newdemo_game_mode >> 16) & 0x7);
1496 if (Newdemo_game_mode & GM_TEAM) {
1497 nd_read_byte((sbyte *) &(Netgame.team_vector));
1498 nd_read_string(Netgame.team_name[0]);
1499 nd_read_string(Netgame.team_name[1]);
1501 if (Newdemo_game_mode & GM_MULTI) {
1506 // changed this to above two lines -- breaks on the mac because of
1508 // nd_read_byte((sbyte *)&N_players);
1509 for (i = 0 ; i < N_players; i++) {
1510 Players[i].cloak_time = 0;
1511 Players[i].invulnerable_time = 0;
1512 nd_read_string(Players[i].callsign);
1513 nd_read_byte(&(Players[i].connected));
1515 if (Newdemo_game_mode & GM_MULTI_COOP) {
1516 nd_read_int(&(Players[i].score));
1518 nd_read_short((short *)&(Players[i].net_killed_total));
1519 nd_read_short((short *)&(Players[i].net_kills_total));
1522 Game_mode = Newdemo_game_mode;
1523 multi_sort_kill_list();
1524 Game_mode = GM_NORMAL;
1527 nd_read_int(&(Players[Player_num].score)); // Note link to above if!
1529 for (i = 0; i < (Newdemo_game_type < DEMO_GAME_TYPE_D2 ? 5 : MAX_PRIMARY_WEAPONS); i++)
1530 nd_read_short((short*)&(Players[Player_num].primary_ammo[i]));
1532 for (i = 0; i < (Newdemo_game_type < DEMO_GAME_TYPE_D2 ? 5 : MAX_SECONDARY_WEAPONS); i++)
1533 nd_read_short((short*)&(Players[Player_num].secondary_ammo[i]));
1535 nd_read_byte(&laser_level);
1536 if (laser_level != Players[Player_num].laser_level) {
1537 Players[Player_num].laser_level = laser_level;
1538 update_laser_weapon_info();
1541 // Support for missions
1543 nd_read_string(current_mission);
1544 if (!strcmp(current_mission, ""))
1545 strcpy(current_mission, "descent");
1546 if (!strcmp(current_mission, "d2demo") && !cfexist("d2demo.hog")) {
1547 strcpy(current_mission, "d2");
1548 Newdemo_is_d2demo = 1;
1550 if (!load_mission_by_name(current_mission)) {
1554 sprintf(text, TXT_NOMISSION4DEMO, current_mission);
1555 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1556 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1561 nd_recorded_total = 0;
1562 nd_playback_total = 0;
1563 nd_read_byte(&energy);
1564 nd_read_byte(&shield);
1566 nd_read_int((int *)&(Players[Player_num].flags));
1567 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
1568 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1569 Newdemo_players_cloaked |= (1 << Player_num);
1571 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
1572 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1574 nd_read_byte((sbyte *)&Primary_weapon);
1575 nd_read_byte((sbyte *)&Secondary_weapon);
1577 // Next bit of code to fix problem that I introduced between 1.0 and 1.1
1578 // check the next byte -- it _will_ be a load_new_level event. If it is
1579 // not, then we must shift all bytes up by one.
1581 Players[Player_num].energy = i2f(energy);
1582 Players[Player_num].shields = i2f(shield);
1583 JustStartedPlayback=1;
1587 void newdemo_pop_ctrlcen_triggers()
1591 segment *seg, *csegp;
1593 for (i = 0; i < ControlCenterTriggers.num_links; i++) {
1594 seg = &Segments[ControlCenterTriggers.seg[i]];
1595 side = ControlCenterTriggers.side[i];
1596 csegp = &Segments[seg->children[side]];
1597 cside = find_connect_side(seg, csegp);
1598 anim_num = Walls[seg->sides[side].wall_num].clip_num;
1599 n = WallAnims[anim_num].num_frames;
1600 if (WallAnims[anim_num].flags & WCF_TMAP1) {
1601 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[n-1];
1603 seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[n-1];
1608 #define N_PLAYER_SHIP_TEXTURES 6
1610 void nd_render_extras (ubyte,object *);
1611 extern void multi_apply_goal_textures ();
1612 ubyte Newdemo_flying_guided=0;
1614 int newdemo_read_frame_information()
1616 int done, segnum, side, objnum, soundno, angle, volume, i,shot;
1618 sbyte c,WhichWindow;
1619 static sbyte saved_letter_cockpit;
1620 static sbyte saved_rearview_cockpit;
1622 static char LastReadValue=101;
1627 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1628 for (segnum=0; segnum <= Highest_segment_index; segnum++)
1629 Segments[segnum].objects = -1;
1632 Players[Player_num].homing_object_dist = -F1_0;
1638 if (nd_bad_read) { done = -1; break; }
1642 case ND_EVENT_START_FRAME: { // Followed by an integer frame number, then a fix FrameTime
1643 short last_frame_length;
1646 nd_read_short(&last_frame_length);
1647 nd_read_int(&NewdemoFrameCount);
1648 nd_read_int((int *)&nd_recorded_time);
1649 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1650 nd_recorded_total += nd_recorded_time;
1651 NewdemoFrameCount--;
1653 if (nd_bad_read) { done = -1; break; }
1657 case ND_EVENT_VIEWER_OBJECT: // Followed by an object structure
1658 if (Newdemo_game_type < DEMO_GAME_TYPE_D2)
1661 nd_read_byte (&WhichWindow);
1664 //mprintf ((0,"Reading extra!\n"));
1665 nd_read_object (&extraobj);
1666 if (Newdemo_vcr_state!=ND_STATE_PAUSED)
1668 if (nd_bad_read) { done = -1; break; }
1670 nd_render_extras (WhichWindow,&extraobj);
1675 //mprintf ((0,"Reading viewer!\n"));
1676 //Viewer=&Objects[0];
1677 nd_read_object(Viewer);
1679 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1680 if (nd_bad_read) { done = -1; break; }
1681 segnum = Viewer->segnum;
1682 Viewer->next = Viewer->prev = Viewer->segnum = -1;
1684 // HACK HACK HACK -- since we have multiple level recording, it can be the case
1685 // HACK HACK HACK -- that when rewinding the demo, the viewer is in a segment
1686 // HACK HACK HACK -- that is greater than the highest index of segments. Bash
1687 // HACK HACK HACK -- the viewer to segment 0 for bogus view.
1689 if (segnum > Highest_segment_index)
1691 obj_link(OBJECT_NUMBER(Viewer), segnum);
1696 case ND_EVENT_RENDER_OBJECT: // Followed by an object structure
1697 objnum = obj_allocate();
1700 obj = &Objects[objnum];
1701 nd_read_object(obj);
1702 if (nd_bad_read) { done = -1; break; }
1703 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1704 segnum = obj->segnum;
1705 obj->next = obj->prev = obj->segnum = -1;
1707 // HACK HACK HACK -- don't render objects is segments greater than Highest_segment_index
1708 // HACK HACK HACK -- (see above)
1710 if (segnum > Highest_segment_index)
1713 obj_link(OBJECT_NUMBER(obj), segnum);
1715 if ((obj->type == OBJ_PLAYER) && (Newdemo_game_mode & GM_MULTI)) {
1718 if (Newdemo_game_mode & GM_TEAM)
1719 player = get_team(obj->id);
1726 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
1727 multi_player_textures[player][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[obj->rtype.pobj_info.model_num].first_texture+i]];
1729 multi_player_textures[player][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2]];
1730 multi_player_textures[player][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2+1]];
1731 obj->rtype.pobj_info.alt_textures = player+1;
1737 case ND_EVENT_SOUND:
1738 nd_read_int(&soundno);
1739 if (nd_bad_read) {done = -1; break; }
1740 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1741 digi_play_sample( soundno, F1_0 );
1744 //--unused case ND_EVENT_SOUND_ONCE:
1745 //--unused nd_read_int(&soundno);
1746 //--unused if (nd_bad_read) { done = -1; break; }
1747 //--unused if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1748 //--unused digi_play_sample_once( soundno, F1_0 );
1751 case ND_EVENT_SOUND_3D:
1752 nd_read_int(&soundno);
1753 nd_read_int(&angle);
1754 nd_read_int(&volume);
1755 if (nd_bad_read) { done = -1; break; }
1756 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1757 digi_play_sample_3d( soundno, angle, volume, 0 );
1760 case ND_EVENT_SOUND_3D_ONCE:
1761 nd_read_int(&soundno);
1762 nd_read_int(&angle);
1763 nd_read_int(&volume);
1764 if (nd_bad_read) { done = -1; break; }
1765 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1766 digi_play_sample_3d( soundno, angle, volume, 1 );
1769 case ND_EVENT_LINK_SOUND_TO_OBJ:
1771 int soundno, objnum, max_volume, max_distance, loop_start, loop_end;
1773 nd_read_int( &soundno );
1774 nd_read_int( &signature );
1775 nd_read_int( &max_volume );
1776 nd_read_int( &max_distance );
1777 nd_read_int( &loop_start );
1778 nd_read_int( &loop_end );
1779 objnum = newdemo_find_object( signature );
1780 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1781 digi_link_sound_to_object3( soundno, objnum, 1, max_volume, max_distance, loop_start, loop_end );
1786 case ND_EVENT_KILL_SOUND_TO_OBJ:
1788 int objnum, signature;
1789 nd_read_int( &signature );
1790 objnum = newdemo_find_object( signature );
1791 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1792 digi_kill_sound_linked_to_object(objnum);
1797 case ND_EVENT_WALL_HIT_PROCESS: {
1801 nd_read_int(&segnum);
1803 nd_read_fix(&damage);
1804 nd_read_int(&player);
1805 if (nd_bad_read) { done = -1; break; }
1806 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1807 wall_hit_process(&Segments[segnum], side, damage, player, &(Objects[0]) );
1811 case ND_EVENT_TRIGGER:
1812 nd_read_int(&segnum);
1814 nd_read_int(&objnum);
1816 if (nd_bad_read) { done = -1; break; }
1817 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1819 mprintf ((0,"EVENT TRIGGER! shot=%d\n",shot));
1821 if (Triggers[Walls[Segments[segnum].sides[side].wall_num].trigger].type == TT_SECRET_EXIT) {
1825 Assert(c == ND_EVENT_SECRET_THINGY);
1826 nd_read_int(&truth);
1828 check_trigger(&Segments[segnum], side, objnum,shot);
1830 check_trigger(&Segments[segnum], side, objnum,shot);
1834 case ND_EVENT_HOSTAGE_RESCUED: {
1837 nd_read_int(&hostage_number);
1838 if (nd_bad_read) { done = -1; break; }
1839 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1840 hostage_rescue( hostage_number );
1844 case ND_EVENT_MORPH_FRAME: {
1848 md = &morph_objects[0];
1849 if (newdemo_read( md->morph_vecs, sizeof(md->morph_vecs), 1 )!=1) { done=-1; break; }
1850 if (newdemo_read( md->submodel_active, sizeof(md->submodel_active), 1 )!=1) { done=-1; break; }
1851 if (newdemo_read( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 )!=1) { done=-1; break; }
1853 objnum = obj_allocate();
1856 obj = &Objects[objnum];
1857 nd_read_object(obj);
1858 obj->render_type = RT_POLYOBJ;
1859 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1860 if (nd_bad_read) { done = -1; break; }
1861 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1862 segnum = obj->segnum;
1863 obj->next = obj->prev = obj->segnum = -1;
1864 obj_link(OBJECT_NUMBER(obj), segnum);
1870 case ND_EVENT_WALL_TOGGLE:
1871 nd_read_int(&segnum);
1873 if (nd_bad_read) {done = -1; break; }
1874 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1875 wall_toggle(&Segments[segnum], side);
1878 case ND_EVENT_CONTROL_CENTER_DESTROYED:
1879 nd_read_int(&Countdown_seconds_left);
1880 Control_center_destroyed = 1;
1881 if (nd_bad_read) { done = -1; break; }
1882 if (!Newdemo_cntrlcen_destroyed) {
1883 newdemo_pop_ctrlcen_triggers();
1884 Newdemo_cntrlcen_destroyed = 1;
1885 //do_controlcen_destroyed_stuff(NULL);
1889 case ND_EVENT_HUD_MESSAGE: {
1892 nd_read_string(&(hud_msg[0]));
1893 if (nd_bad_read) { done = -1; break; }
1894 HUD_init_message( hud_msg );
1897 case ND_EVENT_START_GUIDED:
1898 Newdemo_flying_guided=1;
1899 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1900 Newdemo_flying_guided=0;
1902 case ND_EVENT_END_GUIDED:
1903 Newdemo_flying_guided=0;
1904 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1905 Newdemo_flying_guided=1;
1908 case ND_EVENT_PALETTE_EFFECT: {
1914 if (nd_bad_read) { done = -1; break; }
1915 PALETTE_FLASH_SET(r,g,b);
1919 case ND_EVENT_PLAYER_ENERGY: {
1923 nd_read_byte(&old_energy);
1924 nd_read_byte(&energy);
1925 if (nd_bad_read) {done = -1; break; }
1926 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1927 Players[Player_num].energy = i2f(energy);
1928 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1929 if (old_energy != -128)
1930 Players[Player_num].energy = i2f(old_energy);
1935 case ND_EVENT_PLAYER_AFTERBURNER: {
1937 sbyte old_afterburner;
1939 nd_read_byte(&old_afterburner);
1940 nd_read_byte(&afterburner);
1941 if (nd_bad_read) {done = -1; break; }
1942 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1943 Afterburner_charge = afterburner<<9;
1944 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1945 if (old_afterburner != -128)
1946 Afterburner_charge = old_afterburner<<9;
1951 case ND_EVENT_PLAYER_SHIELD: {
1955 nd_read_byte(&old_shield);
1956 nd_read_byte(&shield);
1957 if (nd_bad_read) {done = -1; break; }
1958 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1959 Players[Player_num].shields = i2f(shield);
1960 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1961 if (old_shield != -128)
1962 Players[Player_num].shields = i2f(old_shield);
1967 case ND_EVENT_PLAYER_FLAGS: {
1970 nd_read_int((int *)&(Players[Player_num].flags));
1971 if (nd_bad_read) {done = -1; break; }
1973 oflags = Players[Player_num].flags >> 16;
1974 Players[Player_num].flags &= 0xffff;
1976 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || ((Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) && (oflags != 0xffff)) ) {
1977 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1978 Players[Player_num].cloak_time = 0;
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 = GameTime - (CLOAK_TIME_MAX / 2);
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 = 0;
1987 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1988 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1989 Players[Player_num].flags = oflags;
1990 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1991 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1992 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1993 Newdemo_players_cloaked |= (1 << Player_num);
1995 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1996 Players[Player_num].cloak_time = 0;
1997 Newdemo_players_cloaked &= ~(1 << Player_num);
1999 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2000 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2001 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
2002 Players[Player_num].invulnerable_time = 0;
2004 update_laser_weapon_info(); // in case of quad laser change
2008 case ND_EVENT_PLAYER_WEAPON: {
2009 sbyte weapon_type, weapon_num;
2012 nd_read_byte(&weapon_type);
2013 nd_read_byte(&weapon_num);
2014 nd_read_byte(&old_weapon);
2015 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2016 if (weapon_type == 0)
2017 Primary_weapon = (int)weapon_num;
2019 Secondary_weapon = (int)weapon_num;
2020 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2021 if (weapon_type == 0)
2022 Primary_weapon = (int)old_weapon;
2024 Secondary_weapon = (int)old_weapon;
2029 case ND_EVENT_EFFECT_BLOWUP: {
2035 //create a dummy object which will be the weapon that hits
2036 //the monitor. the blowup code wants to know who the parent of the
2037 //laser is, so create a laser whose parent is the player
2038 dummy.ctype.laser_info.parent_type = OBJ_PLAYER;
2040 nd_read_short(&segnum);
2041 nd_read_byte(&side);
2042 nd_read_vector(&pnt);
2043 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2044 check_effect_blowup(&(Segments[segnum]), side, &pnt, &dummy, 0);
2048 case ND_EVENT_HOMING_DISTANCE: {
2051 nd_read_short(&distance);
2052 Players[Player_num].homing_object_dist = i2f((int)(distance << 16));
2056 case ND_EVENT_LETTERBOX:
2057 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2058 saved_letter_cockpit = Cockpit_mode;
2059 select_cockpit(CM_LETTERBOX);
2060 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2061 select_cockpit(saved_letter_cockpit);
2064 case ND_EVENT_CHANGE_COCKPIT: {
2067 nd_read_int (&dummy);
2068 select_cockpit (dummy);
2072 case ND_EVENT_REARVIEW:
2073 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
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_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
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);
2086 case ND_EVENT_RESTORE_COCKPIT:
2087 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2088 saved_letter_cockpit = Cockpit_mode;
2089 select_cockpit(CM_LETTERBOX);
2090 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2091 select_cockpit(saved_letter_cockpit);
2095 case ND_EVENT_RESTORE_REARVIEW:
2096 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2097 saved_rearview_cockpit = Cockpit_mode;
2098 if (Cockpit_mode == CM_FULL_COCKPIT)
2099 select_cockpit(CM_REAR_VIEW);
2101 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2102 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2103 saved_rearview_cockpit = CM_FULL_COCKPIT;
2104 select_cockpit(saved_rearview_cockpit);
2110 case ND_EVENT_WALL_SET_TMAP_NUM1: {
2111 short seg, cseg, tmap;
2114 nd_read_short(&seg);
2115 nd_read_byte(&side);
2116 nd_read_short(&cseg);
2117 nd_read_byte(&cside);
2118 nd_read_short( &tmap );
2119 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD))
2120 Segments[seg].sides[side].tmap_num = Segments[cseg].sides[cside].tmap_num = tmap;
2124 case ND_EVENT_WALL_SET_TMAP_NUM2: {
2125 short seg, cseg, tmap;
2128 nd_read_short(&seg);
2129 nd_read_byte(&side);
2130 nd_read_short(&cseg);
2131 nd_read_byte(&cside);
2132 nd_read_short( &tmap );
2133 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) {
2134 Assert(tmap!=0 && Segments[seg].sides[side].tmap_num2!=0);
2135 Segments[seg].sides[side].tmap_num2 = Segments[cseg].sides[cside].tmap_num2 = tmap;
2140 case ND_EVENT_MULTI_CLOAK: {
2143 nd_read_byte(&pnum);
2144 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2145 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2146 Players[pnum].cloak_time = 0;
2147 Newdemo_players_cloaked &= ~(1 << pnum);
2148 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2149 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2150 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2151 Newdemo_players_cloaked |= (1 << pnum);
2156 case ND_EVENT_MULTI_DECLOAK: {
2159 nd_read_byte(&pnum);
2161 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2162 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2163 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2164 Newdemo_players_cloaked |= (1 << pnum);
2165 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2166 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2167 Players[pnum].cloak_time = 0;
2168 Newdemo_players_cloaked &= ~(1 << pnum);
2173 case ND_EVENT_MULTI_DEATH: {
2176 nd_read_byte(&pnum);
2177 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2178 Players[pnum].net_killed_total--;
2179 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2180 Players[pnum].net_killed_total++;
2185 case ND_EVENT_MULTI_KILL: {
2188 nd_read_byte(&pnum);
2189 nd_read_byte(&kill);
2190 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2191 Players[pnum].net_kills_total -= kill;
2192 if (Newdemo_game_mode & GM_TEAM)
2193 team_kills[get_team(pnum)] -= kill;
2194 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2195 Players[pnum].net_kills_total += kill;
2196 if (Newdemo_game_mode & GM_TEAM)
2197 team_kills[get_team(pnum)] += kill;
2199 Game_mode = Newdemo_game_mode;
2200 multi_sort_kill_list();
2201 Game_mode = GM_NORMAL;
2205 case ND_EVENT_MULTI_CONNECT: {
2206 sbyte pnum, new_player;
2207 int killed_total, kills_total;
2208 char new_callsign[CALLSIGN_LEN+1], old_callsign[CALLSIGN_LEN+1];
2210 nd_read_byte(&pnum);
2211 nd_read_byte(&new_player);
2213 nd_read_string(old_callsign);
2214 nd_read_int(&killed_total);
2215 nd_read_int(&kills_total);
2217 nd_read_string(new_callsign);
2218 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2219 Players[pnum].connected = 0;
2221 memcpy(Players[pnum].callsign, old_callsign, CALLSIGN_LEN+1);
2222 Players[pnum].net_killed_total = killed_total;
2223 Players[pnum].net_kills_total = kills_total;
2227 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2228 Players[pnum].connected = 1;
2229 Players[pnum].net_kills_total = 0;
2230 Players[pnum].net_killed_total = 0;
2231 memcpy(Players[pnum].callsign, new_callsign, CALLSIGN_LEN+1);
2238 case ND_EVENT_MULTI_RECONNECT: {
2241 nd_read_byte(&pnum);
2242 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2243 Players[pnum].connected = 0;
2244 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2245 Players[pnum].connected = 1;
2249 case ND_EVENT_MULTI_DISCONNECT: {
2252 nd_read_byte(&pnum);
2253 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2254 Players[pnum].connected = 1;
2255 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2256 Players[pnum].connected = 0;
2260 case ND_EVENT_MULTI_SCORE: {
2264 nd_read_byte(&pnum);
2265 nd_read_int(&score);
2266 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2267 Players[pnum].score -= score;
2268 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2269 Players[pnum].score += score;
2270 Game_mode = Newdemo_game_mode;
2271 multi_sort_kill_list();
2272 Game_mode = GM_NORMAL;
2277 case ND_EVENT_PLAYER_SCORE: {
2280 nd_read_int(&score);
2281 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2282 Players[Player_num].score -= score;
2283 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2284 Players[Player_num].score += score;
2289 case ND_EVENT_PRIMARY_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].primary_ammo[Primary_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].primary_ammo[Primary_weapon] = new_ammo;
2302 case ND_EVENT_SECONDARY_AMMO: {
2303 short old_ammo, new_ammo;
2305 nd_read_short(&old_ammo);
2306 nd_read_short(&new_ammo);
2308 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2309 Players[Player_num].secondary_ammo[Secondary_weapon] = old_ammo;
2310 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2311 Players[Player_num].secondary_ammo[Secondary_weapon] = new_ammo;
2315 case ND_EVENT_DOOR_OPENING: {
2319 nd_read_short(&segnum);
2320 nd_read_byte(&side);
2321 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2324 segment *segp, *csegp;
2326 segp = &Segments[segnum];
2327 csegp = &Segments[segp->children[side]];
2328 cside = find_connect_side(segp, csegp);
2329 anim_num = Walls[segp->sides[side].wall_num].clip_num;
2331 if (WallAnims[anim_num].flags & WCF_TMAP1) {
2332 segp->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[0];
2334 segp->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[0];
2340 case ND_EVENT_LASER_LEVEL: {
2341 sbyte old_level, new_level;
2343 nd_read_byte(&old_level);
2344 nd_read_byte(&new_level);
2345 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2346 Players[Player_num].laser_level = old_level;
2347 update_laser_weapon_info();
2348 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2349 Players[Player_num].laser_level = new_level;
2350 update_laser_weapon_info();
2355 case ND_EVENT_CLOAKING_WALL: {
2356 sbyte back_wall_num,front_wall_num,type,state,cloak_value;
2361 nd_read_byte(&front_wall_num);
2362 nd_read_byte(&back_wall_num);
2363 nd_read_byte(&type);
2364 nd_read_byte(&state);
2365 nd_read_byte(&cloak_value);
2371 Walls[front_wall_num].type = type;
2372 Walls[front_wall_num].state = state;
2373 Walls[front_wall_num].cloak_value = cloak_value;
2374 segp = &Segments[Walls[front_wall_num].segnum];
2375 sidenum = Walls[front_wall_num].sidenum;
2376 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2377 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2378 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2379 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2381 Walls[back_wall_num].type = type;
2382 Walls[back_wall_num].state = state;
2383 Walls[back_wall_num].cloak_value = cloak_value;
2384 segp = &Segments[Walls[back_wall_num].segnum];
2385 sidenum = Walls[back_wall_num].sidenum;
2386 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2387 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2388 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2389 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2394 case ND_EVENT_NEW_LEVEL: {
2395 sbyte new_level, old_level, loaded_level;
2397 nd_read_byte (&new_level);
2398 nd_read_byte (&old_level);
2399 if (Newdemo_vcr_state == ND_STATE_PAUSED)
2403 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2404 loaded_level = old_level;
2406 loaded_level = new_level;
2407 for (i = 0; i < MAX_PLAYERS; i++) {
2408 Players[i].cloak_time = 0;
2409 Players[i].flags &= ~PLAYER_FLAGS_CLOAKED;
2412 if ((loaded_level < Last_secret_level) || (loaded_level > Last_level)) {
2415 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2416 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2417 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2418 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2422 LoadLevel((int)loaded_level,1);
2423 Newdemo_cntrlcen_destroyed = 0;
2425 if (JustStartedPlayback && Newdemo_game_type >= DEMO_GAME_TYPE_D2)
2427 nd_read_int (&Num_walls);
2428 for (i=0;i<Num_walls;i++) // restore the walls
2430 nd_read_byte ((signed char *)&Walls[i].type);
2431 nd_read_byte ((signed char *)&Walls[i].flags);
2432 nd_read_byte ((signed char *)&Walls[i].state);
2434 seg = &Segments[Walls[i].segnum];
2435 side = Walls[i].sidenum;
2436 nd_read_short (&seg->sides[side].tmap_num);
2437 nd_read_short (&seg->sides[side].tmap_num2);
2440 if (Newdemo_game_mode & GM_CAPTURE)
2441 multi_apply_goal_textures ();
2443 JustStartedPlayback=0;
2447 // so says Rob H.!!! if (Newdemo_game_mode & GM_MULTI) {
2448 // so says Rob H.!!! for (i = 0; i < Num_walls; i++) {
2449 // so says Rob H.!!! if (Walls[i].type == WALL_BLASTABLE)
2450 // so says Rob H.!!! {
2451 // so says Rob H.!!! int a, n;
2452 // so says Rob H.!!! int side;
2453 // so says Rob H.!!! segment *seg;
2454 // so says Rob H.!!!
2455 // so says Rob H.!!! seg = &Segments[Walls[i].segnum];
2456 // so says Rob H.!!! side = Walls[i].sidenum;
2457 // so says Rob H.!!! a = Walls[i].clip_num;
2458 // so says Rob H.!!! n = WallAnims[a].num_frames;
2459 // so says Rob H.!!! seg->sides[side].tmap_num = WallAnims[a].frames[n-1];
2460 // so says Rob H.!!! Walls[i].flags |= WALL_BLASTED;
2461 // so says Rob H.!!! }
2462 // so says Rob H.!!! }
2463 // so says Rob H.!!! }
2465 reset_palette_add(); // get palette back to normal
2470 case ND_EVENT_EOF: {
2472 PHYSFS_seek(infile, PHYSFS_tell(infile) - 1); // get back to the EOF marker
2474 NewdemoFrameCount++;
2488 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_ERR_READING;
2489 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_DEMO_OLD_CORRUPT;
2490 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2496 void newdemo_goto_beginning()
2498 //if (NewdemoFrameCount == 0)
2500 PHYSFS_seek(infile, 0);
2501 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2502 if (newdemo_read_demo_start(0))
2503 newdemo_stop_playback();
2504 if (newdemo_read_frame_information() == -1)
2505 newdemo_stop_playback();
2506 if (newdemo_read_frame_information() == -1)
2507 newdemo_stop_playback();
2508 Newdemo_vcr_state = ND_STATE_PAUSED;
2512 void newdemo_goto_end()
2514 short frame_length, byte_count, bshort;
2515 sbyte level, bbyte, laser_level;
2516 sbyte energy, shield, c;
2519 PHYSFS_seek(infile, PHYSFS_tell(infile) - 2);
2520 nd_read_byte(&level);
2522 if ((level < Last_secret_level) || (level > Last_level)) {
2525 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2526 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2527 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2528 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2529 newdemo_stop_playback();
2532 if (level != Current_level_num)
2535 PHYSFS_seek(infile, PHYSFS_tell(infile) - 4);
2536 nd_read_short(&byte_count);
2537 PHYSFS_seek(infile, PHYSFS_tell(infile) - 2 - byte_count);
2539 nd_read_short(&frame_length);
2540 loc = PHYSFS_tell(infile);
2541 if (Newdemo_game_mode & GM_MULTI)
2542 nd_read_byte(&Newdemo_players_cloaked);
2544 nd_read_byte(&bbyte);
2545 nd_read_byte(&bbyte);
2546 nd_read_short(&bshort);
2549 nd_read_byte(&energy);
2550 nd_read_byte(&shield);
2551 Players[Player_num].energy = i2f(energy);
2552 Players[Player_num].shields = i2f(shield);
2553 nd_read_int((int *)&(Players[Player_num].flags));
2554 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
2555 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2556 Newdemo_players_cloaked |= (1 << Player_num);
2558 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2559 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2560 nd_read_byte((sbyte *)&Primary_weapon);
2561 nd_read_byte((sbyte *)&Secondary_weapon);
2562 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
2563 nd_read_short((short *)&(Players[Player_num].primary_ammo[i]));
2564 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
2565 nd_read_short((short *)&(Players[Player_num].secondary_ammo[i]));
2566 nd_read_byte(&laser_level);
2567 if (laser_level != Players[Player_num].laser_level) {
2568 Players[Player_num].laser_level = laser_level;
2569 update_laser_weapon_info();
2572 if (Newdemo_game_mode & GM_MULTI) {
2575 // see newdemo_read_start_demo for explanation of
2576 // why this is commented out
2577 // nd_read_byte((sbyte *)&N_players);
2578 for (i = 0; i < N_players; i++) {
2579 nd_read_string(Players[i].callsign);
2580 nd_read_byte(&(Players[i].connected));
2581 if (Newdemo_game_mode & GM_MULTI_COOP) {
2582 nd_read_int(&(Players[i].score));
2584 nd_read_short((short *)&(Players[i].net_killed_total));
2585 nd_read_short((short *)&(Players[i].net_kills_total));
2589 nd_read_int(&(Players[Player_num].score));
2592 PHYSFS_seek(infile, loc);
2593 PHYSFS_seek(infile, PHYSFS_tell(infile) - frame_length);
2594 nd_read_int(&NewdemoFrameCount); // get the frame count
2595 NewdemoFrameCount--;
2596 PHYSFS_seek(infile, PHYSFS_tell(infile) + 4);
2597 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2598 newdemo_read_frame_information(); // then the frame information
2599 Newdemo_vcr_state = ND_STATE_PAUSED;
2603 void newdemo_back_frames(int frames)
2605 short last_frame_length;
2608 for (i = 0; i < frames; i++)
2610 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
2611 nd_read_short(&last_frame_length);
2612 PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
2614 if (!Newdemo_at_eof && newdemo_read_frame_information() == -1) {
2615 newdemo_stop_playback();
2621 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
2622 nd_read_short(&last_frame_length);
2623 PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
2629 * routine to interpolate the viewer position. the current position is
2630 * stored in the Viewer object. Save this position, and read the next
2631 * frame to get all objects read in. Calculate the delta playback and
2632 * the delta recording frame times between the two frames, then intepolate
2633 * the viewers position accordingly. nd_recorded_time is the time that it
2634 * took the recording to render the frame that we are currently looking
2638 void interpolate_frame(fix d_play, fix d_recorded)
2640 int i, j, num_cur_objs;
2644 factor = fixdiv(d_play, d_recorded);
2648 num_cur_objs = Highest_object_index;
2649 cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1));
2650 if (cur_objs == NULL) {
2651 mprintf((0,"Couldn't get %d bytes for cur_objs in interpolate_frame\n", sizeof(object) * num_cur_objs));
2655 for (i = 0; i <= num_cur_objs; i++)
2656 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2658 Newdemo_vcr_state = ND_STATE_PAUSED;
2659 if (newdemo_read_frame_information() == -1) {
2661 newdemo_stop_playback();
2665 for (i = 0; i <= num_cur_objs; i++) {
2666 for (j = 0; j <= Highest_object_index; j++) {
2667 if (cur_objs[i].signature == Objects[j].signature) {
2668 sbyte render_type = cur_objs[i].render_type;
2669 //fix delta_p, delta_h, delta_b;
2670 fix delta_x, delta_y, delta_z;
2671 //vms_angvec cur_angles, dest_angles;
2673 // Extract the angles from the object orientation matrix.
2674 // Some of this code taken from ai_turn_towards_vector
2675 // Don't do the interpolation on certain render types which don't use an orientation matrix
2677 if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) {
2679 vms_vector fvec1, fvec2, rvec1, rvec2;
2682 fvec1 = cur_objs[i].orient.fvec;
2683 vm_vec_scale(&fvec1, F1_0-factor);
2684 fvec2 = Objects[j].orient.fvec;
2685 vm_vec_scale(&fvec2, factor);
2686 vm_vec_add2(&fvec1, &fvec2);
2687 mag1 = vm_vec_normalize_quick(&fvec1);
2688 if (mag1 > F1_0/256) {
2689 rvec1 = cur_objs[i].orient.rvec;
2690 vm_vec_scale(&rvec1, F1_0-factor);
2691 rvec2 = Objects[j].orient.rvec;
2692 vm_vec_scale(&rvec2, factor);
2693 vm_vec_add2(&rvec1, &rvec2);
2694 vm_vec_normalize_quick(&rvec1); // Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1
2695 vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2698 //--old new way -- vms_vector fvec1, fvec2, rvec1, rvec2;
2700 //--old new way -- fvec1 = cur_objs[i].orient.fvec;
2701 //--old new way -- vm_vec_scale(&fvec1, F1_0-factor);
2702 //--old new way -- fvec2 = Objects[j].orient.fvec;
2703 //--old new way -- vm_vec_scale(&fvec2, factor);
2704 //--old new way -- vm_vec_add2(&fvec1, &fvec2);
2705 //--old new way -- vm_vec_normalize_quick(&fvec1);
2707 //--old new way -- rvec1 = cur_objs[i].orient.rvec;
2708 //--old new way -- vm_vec_scale(&rvec1, F1_0-factor);
2709 //--old new way -- rvec2 = Objects[j].orient.rvec;
2710 //--old new way -- vm_vec_scale(&rvec2, factor);
2711 //--old new way -- vm_vec_add2(&rvec1, &rvec2);
2712 //--old new way -- vm_vec_normalize_quick(&rvec1);
2714 //--old new way -- vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2716 // -- old fashioned way -- vm_extract_angles_matrix(&cur_angles, &(cur_objs[i].orient));
2717 // -- old fashioned way -- vm_extract_angles_matrix(&dest_angles, &(Objects[j].orient));
2718 // -- old fashioned way --
2719 // -- old fashioned way -- delta_p = (dest_angles.p - cur_angles.p);
2720 // -- old fashioned way -- delta_h = (dest_angles.h - cur_angles.h);
2721 // -- old fashioned way -- delta_b = (dest_angles.b - cur_angles.b);
2722 // -- old fashioned way --
2723 // -- old fashioned way -- if (delta_p != 0) {
2724 // -- old fashioned way -- if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
2725 // -- old fashioned way -- if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
2726 // -- old fashioned way -- delta_p = fixmul(delta_p, factor);
2727 // -- old fashioned way -- cur_angles.p += delta_p;
2728 // -- old fashioned way -- }
2729 // -- old fashioned way -- if (delta_h != 0) {
2730 // -- old fashioned way -- if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
2731 // -- old fashioned way -- if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
2732 // -- old fashioned way -- delta_h = fixmul(delta_h, factor);
2733 // -- old fashioned way -- cur_angles.h += delta_h;
2734 // -- old fashioned way -- }
2735 // -- old fashioned way -- if (delta_b != 0) {
2736 // -- old fashioned way -- if (delta_b > F1_0/2) delta_b = dest_angles.b - cur_angles.b - F1_0;
2737 // -- old fashioned way -- if (delta_b < -F1_0/2) delta_b = dest_angles.b - cur_angles.b + F1_0;
2738 // -- old fashioned way -- delta_b = fixmul(delta_b, factor);
2739 // -- old fashioned way -- cur_angles.b += delta_b;
2740 // -- old fashioned way -- }
2743 // Interpolate the object position. This is just straight linear
2746 delta_x = Objects[j].pos.x - cur_objs[i].pos.x;
2747 delta_y = Objects[j].pos.y - cur_objs[i].pos.y;
2748 delta_z = Objects[j].pos.z - cur_objs[i].pos.z;
2750 delta_x = fixmul(delta_x, factor);
2751 delta_y = fixmul(delta_y, factor);
2752 delta_z = fixmul(delta_z, factor);
2754 cur_objs[i].pos.x += delta_x;
2755 cur_objs[i].pos.y += delta_y;
2756 cur_objs[i].pos.z += delta_z;
2758 // -- old fashioned way --// stuff the new angles back into the object structure
2759 // -- old fashioned way -- vm_angles_2_matrix(&(cur_objs[i].orient), &cur_angles);
2764 // get back to original position in the demo file. Reread the current
2765 // frame information again to reset all of the object stuff not covered
2766 // with Highest_object_index and the object array (previously rendered
2767 // objects, etc....)
2769 newdemo_back_frames(1);
2770 newdemo_back_frames(1);
2771 if (newdemo_read_frame_information() == -1)
2772 newdemo_stop_playback();
2773 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2775 for (i = 0; i <= num_cur_objs; i++)
2776 memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object));
2777 Highest_object_index = num_cur_objs;
2781 void newdemo_playback_one_frame()
2783 int frames_back, i, level;
2784 static fix base_interpol_time = 0;
2785 static fix d_recorded = 0;
2787 for (i = 0; i < MAX_PLAYERS; i++)
2788 if (Newdemo_players_cloaked & (1 << i))
2789 Players[i].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2791 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2792 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2794 if (Newdemo_vcr_state == ND_STATE_PAUSED) // render a frame or not
2797 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2798 DoJasonInterpolate(nd_recorded_time);
2800 Control_center_destroyed = 0;
2801 Countdown_seconds_left = -1;
2802 PALETTE_FLASH_SET(0,0,0); //clear flash
2804 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2806 level = Current_level_num;
2807 if (NewdemoFrameCount == 0)
2809 else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (NewdemoFrameCount < 10)) {
2810 newdemo_goto_beginning();
2813 if (Newdemo_vcr_state == ND_STATE_REWINDING)
2817 if (Newdemo_at_eof) {
2818 PHYSFS_seek(infile, PHYSFS_tell(infile) + 11);
2820 newdemo_back_frames(frames_back);
2822 if (level != Current_level_num)
2823 newdemo_pop_ctrlcen_triggers();
2825 if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) {
2826 if (level != Current_level_num)
2827 newdemo_back_frames(1);
2828 Newdemo_vcr_state = ND_STATE_PAUSED;
2831 else if (Newdemo_vcr_state == ND_STATE_FASTFORWARD) {
2832 if (!Newdemo_at_eof)
2834 for (i = 0; i < 10; i++)
2836 if (newdemo_read_frame_information() == -1)
2839 Newdemo_vcr_state = ND_STATE_PAUSED;
2841 newdemo_stop_playback();
2847 Newdemo_vcr_state = ND_STATE_PAUSED;
2849 else if (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD) {
2850 if (!Newdemo_at_eof) {
2851 level = Current_level_num;
2852 if (newdemo_read_frame_information() == -1) {
2853 if (!Newdemo_at_eof)
2854 newdemo_stop_playback();
2856 if (level != Current_level_num) {
2857 if (newdemo_read_frame_information() == -1) {
2858 if (!Newdemo_at_eof)
2859 newdemo_stop_playback();
2862 Newdemo_vcr_state = ND_STATE_PAUSED;
2864 Newdemo_vcr_state = ND_STATE_PAUSED;
2868 // First, uptate the total playback time to date. Then we check to see
2869 // if we need to change the playback style to interpolate frames or
2870 // skip frames based on where the playback time is relative to the
2873 if (NewdemoFrameCount <= 0)
2874 nd_playback_total = nd_recorded_total; // baseline total playback time
2876 nd_playback_total += FrameTime;
2877 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2878 if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) {
2879 playback_style = INTERPOLATE_PLAYBACK;
2880 nd_playback_total = nd_recorded_total + FrameTime; // baseline playback time
2881 base_interpol_time = nd_recorded_total;
2882 d_recorded = nd_recorded_time; // baseline delta recorded
2884 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2885 if (nd_playback_total > nd_recorded_total)
2886 playback_style = SKIP_PLAYBACK;
2889 if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) {
2892 if (nd_recorded_total - nd_playback_total < FrameTime) {
2893 d_recorded = nd_recorded_total - nd_playback_total;
2895 while (nd_recorded_total - nd_playback_total < FrameTime) {
2897 int i, j, num_objs, level;
2899 num_objs = Highest_object_index;
2900 cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1));
2901 if (cur_objs == NULL) {
2902 Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs);
2905 for (i = 0; i <= num_objs; i++)
2906 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2908 level = Current_level_num;
2909 if (newdemo_read_frame_information() == -1) {
2911 newdemo_stop_playback();
2914 if (level != Current_level_num) {
2916 if (newdemo_read_frame_information() == -1)
2917 newdemo_stop_playback();
2921 // for each new object in the frame just read in, determine if there is
2922 // a corresponding object that we have been interpolating. If so, then
2923 // copy that interpolated object to the new Objects array so that the
2924 // interpolated position and orientation can be preserved.
2926 for (i = 0; i <= num_objs; i++) {
2927 for (j = 0; j <= Highest_object_index; j++) {
2928 if (cur_objs[i].signature == Objects[j].signature) {
2929 memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix));
2930 memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector));
2936 d_recorded += nd_recorded_time;
2937 base_interpol_time = nd_playback_total - FrameTime;
2941 d_play = nd_playback_total - base_interpol_time;
2942 interpolate_frame(d_play, d_recorded);
2946 //mprintf ((0, "*"));
2947 if (newdemo_read_frame_information() == -1) {
2948 newdemo_stop_playback();
2951 if (playback_style == SKIP_PLAYBACK) {
2952 //mprintf ((0, "."));
2953 while (nd_playback_total > nd_recorded_total) {
2954 if (newdemo_read_frame_information() == -1) {
2955 newdemo_stop_playback();
2964 void newdemo_start_recording()
2966 Newdemo_size = PHYSFSX_getFreeDiskSpace();
2967 con_printf(CON_VERBOSE, "Free space = %d\n", Newdemo_size);
2969 Newdemo_size -= 100000;
2971 if ((Newdemo_size+100000) < 2000000000) {
2972 if (((int)(Newdemo_size)) < 500000) {
2974 nm_messagebox(NULL, 1, TXT_OK, TXT_DEMO_NO_SPACE);
2976 nm_messagebox(NULL, 1, TXT_OK, "Not enough space on current\ndrive to start demo recording.");
2982 Newdemo_num_written = 0;
2984 Newdemo_state = ND_STATE_RECORDING;
2985 outfile = PHYSFSX_openWriteBuffered(DEMO_FILENAME);
2987 #if !defined(MACINTOSH) && !defined(_WIN32_WCE)
2988 if (outfile == NULL && errno == ENOENT) //dir doesn't exist?
2990 if (outfile == NULL) //dir doesn't exist and no errno on mac!
2993 PHYSFS_mkdir(DEMO_DIR); //try making directory
2994 outfile = PHYSFSX_openWriteBuffered(DEMO_FILENAME);
2997 if (outfile == NULL)
2999 nm_messagebox(NULL, 1, TXT_OK, "Cannot open demo temp file");
3000 Newdemo_state = ND_STATE_NORMAL;
3003 newdemo_record_start_demo();
3007 char demoname_allowed_chars[] = "azAZ09__--";
3008 void newdemo_stop_recording()
3012 static char filename[15] = "", *s;
3013 static sbyte tmpcnt = 0;
3015 char fullname[15+FILENAME_LEN] = DEMO_DIR;
3016 unsigned short byte_count = 0;
3020 nd_write_byte(ND_EVENT_EOF);
3021 nd_write_short(frame_bytes_written - 1);
3022 if (Game_mode & GM_MULTI) {
3023 for (l = 0; l < N_players; l++) {
3024 if (Players[l].flags & PLAYER_FLAGS_CLOAKED)
3025 cloaked |= (1 << l);
3027 nd_write_byte(cloaked);
3028 nd_write_byte(ND_EVENT_EOF);
3030 nd_write_short(ND_EVENT_EOF);
3032 nd_write_short(ND_EVENT_EOF);
3033 nd_write_int(ND_EVENT_EOF);
3035 byte_count += 10; // from frame_bytes_written
3037 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
3038 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
3039 nd_write_int(Players[Player_num].flags); // be sure players flags are set
3040 nd_write_byte((sbyte)Primary_weapon);
3041 nd_write_byte((sbyte)Secondary_weapon);
3044 for (l = 0; l < MAX_PRIMARY_WEAPONS; l++)
3045 nd_write_short((short)Players[Player_num].primary_ammo[l]);
3047 for (l = 0; l < MAX_SECONDARY_WEAPONS; l++)
3048 nd_write_short((short)Players[Player_num].secondary_ammo[l]);
3049 byte_count += (sizeof(short) * (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS));
3051 nd_write_byte(Players[Player_num].laser_level);
3054 if (Game_mode & GM_MULTI) {
3055 nd_write_byte((sbyte)N_players);
3057 for (l = 0; l < N_players; l++) {
3058 nd_write_string(Players[l].callsign);
3059 byte_count += (strlen(Players[l].callsign) + 2);
3060 nd_write_byte(Players[l].connected);
3061 if (Game_mode & GM_MULTI_COOP) {
3062 nd_write_int(Players[l].score);
3065 nd_write_short((short)Players[l].net_killed_total);
3066 nd_write_short((short)Players[l].net_kills_total);
3071 nd_write_int(Players[Player_num].score);
3074 nd_write_short(byte_count);
3076 nd_write_byte(Current_level_num);
3077 nd_write_byte(ND_EVENT_EOF);
3079 l = PHYSFS_tell(outfile);
3080 PHYSFS_close(outfile);
3082 Newdemo_state = ND_STATE_NORMAL;
3083 gr_palette_load( gr_palette );
3085 if (filename[0] != '\0') {
3086 int num, i = strlen(filename) - 1;
3089 while (isdigit(filename[i])) {
3095 num = atoi(&(filename[i]));
3098 sprintf (newfile, "%s%d", filename, num);
3099 strncpy(filename, newfile, 8);
3106 Newmenu_allowed_chars = demoname_allowed_chars;
3107 if (!Newdemo_no_space) {
3108 m[0].type=NM_TYPE_INPUT; m[0].text_len = 8; m[0].text = filename;
3109 exit = newmenu_do( NULL, TXT_SAVE_DEMO_AS, 1, &(m[0]), NULL );
3110 } else if (Newdemo_no_space == 1) {
3111 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_BAD;
3112 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3113 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3114 } else if (Newdemo_no_space == 2) {
3115 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_NOSPACE;
3116 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3117 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3119 Newmenu_allowed_chars = NULL;
3121 if (exit == -2) { // got bumped out from network menu
3122 char save_file[7+FILENAME_LEN];
3124 if (filename[0] != '\0') {
3125 strcpy(save_file, DEMO_DIR);
3126 strcat(save_file, filename);
3127 strcat(save_file, ".dem");
3129 sprintf (save_file, "%stmp%d.dem", DEMO_DIR, tmpcnt++);
3131 PHYSFSX_rename(DEMO_FILENAME, save_file);
3134 if (exit == -1) { // pressed ESC
3135 PHYSFS_delete(DEMO_FILENAME); // might as well remove the file
3136 return; // return without doing anything
3139 if (filename[0]==0) //null string
3142 //check to make sure name is ok
3143 for (s=filename;*s;s++)
3144 if (!isalnum(*s) && *s!='_') {
3145 nm_messagebox1(NULL, NULL,1,TXT_CONTINUE, TXT_DEMO_USE_LETTERS);
3149 if (Newdemo_no_space)
3150 strcat(fullname, m[1].text);
3152 strcat(fullname, m[0].text);
3153 strcat(fullname, ".dem");
3154 PHYSFS_delete(fullname);
3155 PHYSFSX_rename(DEMO_FILENAME, fullname);
3159 extern char AltHogDir[64];
3160 extern char AltHogdir_initialized;
3162 //returns the number of demo files on the disk
3163 int newdemo_count_demos()
3168 find = PHYSFS_enumerateFiles(DEMO_DIR);
3170 for (i = find; *i != NULL; i++)
3173 PHYSFS_freeList(find);
3178 void newdemo_start_playback(char * filename)
3180 char **find = NULL, **i;
3182 char filename2[PATH_MAX+FILENAME_LEN] = DEMO_DIR;
3185 change_playernum_to(0);
3187 First_time_playback=1;
3188 JasonPlaybackTotal=0;
3191 strcat(filename2, filename);
3194 // Randomly pick a filename
3195 int NumFiles = 0, RandFileNum;
3198 NumFiles = newdemo_count_demos();
3200 if ( NumFiles == 0 ) {
3201 return; // No files found!
3203 RandFileNum = d_rand() % NumFiles;
3206 find = PHYSFS_enumerateFiles(DEMO_DIR);
3208 for (i = find; *i != NULL; i++)
3210 if (NumFiles == RandFileNum)
3212 strcat(filename2, *i);
3218 PHYSFS_freeList(find);
3220 if (NumFiles > RandFileNum)
3224 infile = PHYSFSX_openReadBuffered(filename2);
3227 mprintf( (0, "Error reading '%s'\n", filename ));
3233 change_playernum_to(0); // force playernum to 0
3235 strncpy(nd_save_callsign, Players[Player_num].callsign, CALLSIGN_LEN);
3236 Viewer = ConsoleObject = &Objects[0]; // play properly as if console player
3237 if (newdemo_read_demo_start(rnd_demo)) {
3238 PHYSFS_close(infile);
3242 Game_mode = GM_NORMAL;
3243 Newdemo_state = ND_STATE_PLAYBACK;
3244 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3245 Newdemo_old_cockpit = Cockpit_mode;
3246 Newdemo_size = PHYSFS_fileLength(infile);
3249 NewdemoFrameCount = 0;
3250 Newdemo_players_cloaked = 0;
3251 playback_style = NORMAL_PLAYBACK;
3252 Function_mode = FMODE_GAME;
3253 Cockpit_3d_view[0] = CV_NONE; //turn off 3d views on cockpit
3254 Cockpit_3d_view[1] = CV_NONE; //turn off 3d views on cockpit
3255 newdemo_playback_one_frame(); // this one loads new level
3256 newdemo_playback_one_frame(); // get all of the objects to renderb game
3259 void newdemo_stop_playback()
3261 PHYSFS_close(infile);
3262 Newdemo_state = ND_STATE_NORMAL;
3264 change_playernum_to(0); //this is reality
3266 strncpy(Players[Player_num].callsign, nd_save_callsign, CALLSIGN_LEN);
3267 Cockpit_mode = Newdemo_old_cockpit;
3268 Game_mode = GM_GAME_OVER;
3269 Function_mode = FMODE_MENU;
3270 longjmp(LeaveGame,0); // Exit game loop
3276 #define BUF_SIZE 16384
3278 void newdemo_strip_frames(char *outname, int bytes_to_strip)
3280 PHYSFS_file *outfile;
3282 int total_size, bytes_done, read_elems, bytes_back;
3283 int trailer_start, loc1, loc2, stop_loc, bytes_to_read;
3284 short last_frame_length;
3287 total_size = PHYSFS_fileLength(infile);
3288 outfile = PHYSFSX_openWriteBuffered(outname);
3289 if (outfile == NULL) {
3292 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't open output file";
3293 newmenu_do( NULL, NULL, 1, m, NULL );
3294 newdemo_stop_playback();
3297 buf = d_malloc(BUF_SIZE);
3301 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't malloc output buffer";
3302 newmenu_do( NULL, NULL, 1, m, NULL );
3303 PHYSFS_close(outfile);
3304 newdemo_stop_playback();
3308 trailer_start = PHYSFS_tell(infile);
3309 PHYSFS_seek(infile, PHYSFS_tell(infile) + 11);
3311 while (bytes_back < bytes_to_strip) {
3312 loc1 = PHYSFS_tell(infile);
3313 //PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
3314 //nd_read_short(&last_frame_length);
3315 //PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
3316 newdemo_back_frames(1);
3317 loc2 = PHYSFS_tell(infile);
3318 bytes_back += (loc1 - loc2);
3320 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
3321 nd_read_short(&last_frame_length);
3322 PHYSFS_seek(infile, PHYSFS_tell(infile) - 3);
3323 stop_loc = PHYSFS_tell(infile);
3324 PHYSFS_seek(infile, 0);
3325 while (stop_loc > 0) {
3326 if (stop_loc < BUF_SIZE)
3327 bytes_to_read = stop_loc;
3329 bytes_to_read = BUF_SIZE;
3330 read_elems = PHYSFS_read(infile, buf, 1, bytes_to_read);
3331 PHYSFS_write(outfile, buf, 1, read_elems);
3332 stop_loc -= read_elems;
3334 stop_loc = PHYSFS_tell(outfile);
3335 PHYSFS_seek(infile, trailer_start);
3336 while ((read_elems = PHYSFS_read(infile, buf, 1, BUF_SIZE)) != 0)
3337 PHYSFS_write(outfile, buf, 1, read_elems);
3338 PHYSFS_seek(outfile, stop_loc);
3339 PHYSFS_seek(outfile, PHYSFS_tell(infile) + 1);
3340 PHYSFS_write(outfile, &last_frame_length, 2, 1);
3341 PHYSFS_close(outfile);
3342 newdemo_stop_playback();
3348 object DemoRightExtra,DemoLeftExtra;
3349 ubyte DemoDoRight=0,DemoDoLeft=0;
3351 void nd_render_extras (ubyte which,object *obj)
3354 ubyte type=which&15;
3358 Int3(); // how'd we get here?
3359 do_cockpit_window_view(w,NULL,0,WBU_WEAPON,NULL);
3365 memcpy (&DemoRightExtra,obj,sizeof(object)); DemoDoRight=type;
3369 memcpy (&DemoLeftExtra,obj,sizeof(object)); DemoDoLeft=type;
3374 void DoJasonInterpolate (fix recorded_time)
3378 JasonPlaybackTotal+=FrameTime;
3380 if (!First_time_playback)
3382 // get the difference between the recorded time and the playback time
3383 the_delay=(recorded_time - FrameTime);
3384 //mprintf ((0,"The delay=%d\n", f2i(the_delay)));
3385 if (the_delay >= f0_0)
3388 timer_delay(the_delay);
3393 while (JasonPlaybackTotal > nd_recorded_total)
3394 if (newdemo_read_frame_information() == -1)
3396 newdemo_stop_playback();
3400 //the_delay = nd_recorded_total - JasonPlaybackTotal;
3401 //if (the_delay > f0_0)
3402 // timer_delay(the_delay);
3407 First_time_playback=0;
3411 #pragma global_optimizer reset