1 /* $Id: newdemo.c,v 1.18 2004-12-01 12:48:13 btb 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 15 // last D1 version was 13
179 #define DEMO_GAME_TYPE 3 // 1 was shareware, 2 registered
181 #define DEMO_FILENAME DEMO_DIR "tmpdemo.dem"
183 #define DEMO_MAX_LEVELS 29
186 char nd_save_callsign[CALLSIGN_LEN+1];
187 int Newdemo_state = 0;
188 int Newdemo_vcr_state = 0;
189 int Newdemo_start_frame = -1;
190 unsigned int Newdemo_size;
191 int Newdemo_num_written;
192 int Newdemo_game_mode;
193 int Newdemo_old_cockpit;
194 sbyte Newdemo_no_space;
195 sbyte Newdemo_at_eof;
196 sbyte Newdemo_do_interpolate = 0; // 1
197 sbyte Newdemo_players_cloaked;
198 sbyte Newdemo_warning_given = 0;
199 sbyte Newdemo_cntrlcen_destroyed = 0;
200 static sbyte nd_bad_read;
201 int NewdemoFrameCount;
202 short frame_bytes_written = 0;
203 fix nd_playback_total;
204 fix nd_recorded_total;
205 fix nd_recorded_time;
206 sbyte playback_style;
207 sbyte First_time_playback=1;
208 fix JasonPlaybackTotal=0;
212 PHYSFS_file *outfile = NULL;
214 int newdemo_get_percent_done() {
215 if ( Newdemo_state == ND_STATE_PLAYBACK ) {
216 return (PHYSFS_tell(infile) * 100) / Newdemo_size;
218 if ( Newdemo_state == ND_STATE_RECORDING ) {
219 return PHYSFS_tell(outfile);
224 #define VEL_PRECISION 12
226 void my_extract_shortpos(object *objp, shortpos *spp)
232 objp->orient.rvec.x = *sp++ << MATRIX_PRECISION;
233 objp->orient.uvec.x = *sp++ << MATRIX_PRECISION;
234 objp->orient.fvec.x = *sp++ << MATRIX_PRECISION;
236 objp->orient.rvec.y = *sp++ << MATRIX_PRECISION;
237 objp->orient.uvec.y = *sp++ << MATRIX_PRECISION;
238 objp->orient.fvec.y = *sp++ << MATRIX_PRECISION;
240 objp->orient.rvec.z = *sp++ << MATRIX_PRECISION;
241 objp->orient.uvec.z = *sp++ << MATRIX_PRECISION;
242 objp->orient.fvec.z = *sp++ << MATRIX_PRECISION;
244 segnum = spp->segment;
245 objp->segnum = segnum;
247 objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x;
248 objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y;
249 objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z;
251 objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION);
252 objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION);
253 objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION);
256 int newdemo_read( void *buffer, int elsize, int nelem )
259 num_read = PHYSFS_read(infile, buffer, elsize, nelem);
260 if (num_read < nelem || PHYSFS_eof(infile))
266 int newdemo_find_object( int signature )
271 for (i=0; i<=Highest_object_index; i++, objp++ ) {
272 if ( (objp->type != OBJ_NONE) && (objp->signature == signature))
278 int newdemo_write( void *buffer, int elsize, int nelem )
280 int num_written, total_size;
282 total_size = elsize * nelem;
283 frame_bytes_written += total_size;
284 Newdemo_num_written += total_size;
285 Assert(outfile != NULL);
286 num_written = PHYSFS_write(outfile, buffer, elsize, nelem);
287 //if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) {
288 // Newdemo_no_space=1;
289 // newdemo_stop_recording();
292 if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space)
294 if (num_written == nelem && !Newdemo_no_space)
298 newdemo_stop_recording();
303 * The next bunch of files taken from Matt's gamesave.c. We have to modify
304 * these since the demo must save more information about objects that
308 static void nd_write_byte(sbyte b)
310 newdemo_write(&b, 1, 1);
313 static void nd_write_short(short s)
315 newdemo_write(&s, 2, 1);
318 static void nd_write_int(int i)
320 newdemo_write(&i, 4, 1);
323 static void nd_write_string(char *str)
325 nd_write_byte(strlen(str) + 1);
326 newdemo_write(str, strlen(str) + 1, 1);
329 static void nd_write_fix(fix f)
331 newdemo_write(&f, sizeof(fix), 1);
334 static void nd_write_fixang(fixang f)
336 newdemo_write(&f, sizeof(fixang), 1);
339 static void nd_write_vector(vms_vector *v)
346 static void nd_write_angvec(vms_angvec *v)
348 nd_write_fixang(v->p);
349 nd_write_fixang(v->b);
350 nd_write_fixang(v->h);
353 void nd_write_shortpos(object *obj)
359 create_shortpos(&sp, obj, 0);
361 render_type = obj->render_type;
362 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
363 for (i = 0; i < 9; i++)
364 nd_write_byte(sp.bytemat[i]);
365 for (i = 0; i < 9; i++) {
366 if (sp.bytemat[i] != 0)
370 Int3(); // contact Allender about this.
374 nd_write_short(sp.xo);
375 nd_write_short(sp.yo);
376 nd_write_short(sp.zo);
377 nd_write_short(sp.segment);
378 nd_write_short(sp.velx);
379 nd_write_short(sp.vely);
380 nd_write_short(sp.velz);
383 static void nd_read_byte(sbyte *b)
385 newdemo_read(b, 1, 1);
388 static void nd_read_short(short *s)
390 newdemo_read(s, 2, 1);
393 static void nd_read_int(int *i)
395 newdemo_read(i, 4, 1);
398 static void nd_read_string(char *str)
403 newdemo_read(str, len, 1);
406 static void nd_read_fix(fix *f)
408 newdemo_read(f, sizeof(fix), 1);
411 static void nd_read_fixang(fixang *f)
413 newdemo_read(f, sizeof(fixang), 1);
416 static void nd_read_vector(vms_vector *v)
418 nd_read_fix(&(v->x));
419 nd_read_fix(&(v->y));
420 nd_read_fix(&(v->z));
423 static void nd_read_angvec(vms_angvec *v)
425 nd_read_fixang(&(v->p));
426 nd_read_fixang(&(v->b));
427 nd_read_fixang(&(v->h));
430 static void nd_read_shortpos(object *obj)
436 render_type = obj->render_type;
437 if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) {
438 for (i = 0; i < 9; i++)
439 nd_read_byte(&(sp.bytemat[i]));
442 nd_read_short(&(sp.xo));
443 nd_read_short(&(sp.yo));
444 nd_read_short(&(sp.zo));
445 nd_read_short(&(sp.segment));
446 nd_read_short(&(sp.velx));
447 nd_read_short(&(sp.vely));
448 nd_read_short(&(sp.velz));
450 my_extract_shortpos(obj, &sp);
451 if ((obj->id == VCLIP_MORPHING_ROBOT) && (render_type == RT_FIREBALL) && (obj->control_type == CT_EXPLOSION))
452 extract_orient_from_segment(&obj->orient,&Segments[obj->segnum]);
456 object *prev_obj=NULL; //ptr to last object read in
458 void nd_read_object(object *obj)
460 memset(obj, 0, sizeof(object));
463 * Do render type first, since with render_type == RT_NONE, we
464 * blow by all other object information
466 nd_read_byte((sbyte *) &(obj->render_type));
467 nd_read_byte((sbyte *) &(obj->type));
468 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
471 nd_read_byte((sbyte *) &(obj->id));
472 nd_read_byte((sbyte *) &(obj->flags));
473 nd_read_short((short *)&(obj->signature));
474 nd_read_shortpos(obj);
476 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
479 obj->attached_obj = -1;
484 obj->control_type = CT_POWERUP;
485 obj->movement_type = MT_NONE;
486 obj->size = HOSTAGE_SIZE;
490 obj->control_type = CT_AI;
491 // (MarkA and MikeK said we should not do the crazy last secret stuff with multiple reactors...
492 // This necessary code is our vindication. --MK, 2/15/96)
493 if (obj->id != SPECIAL_REACTOR_ROBOT)
494 obj->movement_type = MT_PHYSICS;
496 obj->movement_type = MT_NONE;
497 obj->size = Polygon_models[Robot_info[obj->id].model_num].rad;
498 obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num;
499 obj->rtype.pobj_info.subobj_flags = 0;
500 obj->ctype.ai_info.CLOAKED = (Robot_info[obj->id].cloak_type?1:0);
504 obj->control_type = CT_POWERUP;
505 nd_read_byte((sbyte *) &(obj->movement_type)); // might have physics movement
506 obj->size = Powerup_info[obj->id].size;
510 obj->control_type = CT_NONE;
511 obj->movement_type = MT_PHYSICS;
512 obj->size = Polygon_models[Player_ship->model_num].rad;
513 obj->rtype.pobj_info.model_num = Player_ship->model_num;
514 obj->rtype.pobj_info.subobj_flags = 0;
518 obj->control_type = CT_NONE;
519 obj->movement_type = MT_NONE;
520 obj->size = Polygon_models[obj->id].rad;
521 obj->rtype.pobj_info.model_num = obj->id;
522 obj->rtype.pobj_info.subobj_flags = 0;
526 nd_read_byte((sbyte *) &(obj->control_type));
527 nd_read_byte((sbyte *) &(obj->movement_type));
528 nd_read_fix(&(obj->size));
533 nd_read_vector(&(obj->last_pos));
534 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
535 nd_read_fix(&(obj->lifeleft));
540 obj->lifeleft = (fix)b;
541 // MWA old way -- won't work with big endian machines nd_read_byte((ubyte *)&(obj->lifeleft));
542 obj->lifeleft = (fix)((int)obj->lifeleft << 12);
545 if (obj->type == OBJ_ROBOT) {
546 if (Robot_info[obj->id].boss_flag) {
549 nd_read_byte(&cloaked);
550 obj->ctype.ai_info.CLOAKED = cloaked;
554 switch (obj->movement_type) {
557 nd_read_vector(&(obj->mtype.phys_info.velocity));
558 nd_read_vector(&(obj->mtype.phys_info.thrust));
562 nd_read_vector(&(obj->mtype.spin_rate));
572 switch (obj->control_type) {
576 nd_read_fix(&(obj->ctype.expl_info.spawn_time));
577 nd_read_fix(&(obj->ctype.expl_info.delete_time));
578 nd_read_short(&(obj->ctype.expl_info.delete_objnum));
580 obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1;
582 if (obj->flags & OF_ATTACHED) { //attach to previous object
583 Assert(prev_obj!=NULL);
584 if (prev_obj->control_type == CT_EXPLOSION) {
585 if (prev_obj->flags & OF_ATTACHED && prev_obj->ctype.expl_info.attach_parent!=-1)
586 obj_attach(&Objects[prev_obj->ctype.expl_info.attach_parent],obj);
588 obj->flags &= ~OF_ATTACHED;
591 obj_attach(prev_obj,obj);
597 nd_read_fix(&(obj->ctype.light_info.intensity));
619 switch (obj->render_type) {
628 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
629 nd_read_int(&(obj->rtype.pobj_info.model_num));
630 nd_read_int(&(obj->rtype.pobj_info.subobj_flags));
633 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
635 for (i=0;i<MAX_SUBMODELS;i++)
636 nd_read_angvec(&(obj->pobj_info.anim_angles[i]));
638 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
639 nd_read_angvec(&obj->rtype.pobj_info.anim_angles[i]);
644 obj->rtype.pobj_info.tmap_override = tmo;
647 obj->rtype.pobj_info.tmap_override = -1;
649 int xlated_tmo = tmap_xlate_table[tmo];
650 if (xlated_tmo < 0) {
651 //mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->pobj_info.model_num));
655 obj->rtype.pobj_info.tmap_override = xlated_tmo;
663 case RT_WEAPON_VCLIP:
666 nd_read_int(&(obj->rtype.vclip_info.vclip_num));
667 nd_read_fix(&(obj->rtype.vclip_info.frametime));
668 nd_read_byte(&(obj->rtype.vclip_info.framenum));
682 void nd_write_object(object *obj)
686 if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT))
690 * Do render_type first so on read, we can make determination of
691 * what else to read in
693 nd_write_byte(obj->render_type);
694 nd_write_byte(obj->type);
695 if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA))
698 nd_write_byte(obj->id);
699 nd_write_byte(obj->flags);
700 nd_write_short((short)obj->signature);
701 nd_write_shortpos(obj);
703 if ((obj->type != OBJ_HOSTAGE) && (obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_POWERUP) && (obj->type != OBJ_CLUTTER)) {
704 nd_write_byte(obj->control_type);
705 nd_write_byte(obj->movement_type);
706 nd_write_fix(obj->size);
708 if (obj->type == OBJ_POWERUP)
709 nd_write_byte(obj->movement_type);
711 nd_write_vector(&obj->last_pos);
713 if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP))
714 nd_write_fix(obj->lifeleft);
716 life = (int)obj->lifeleft;
720 nd_write_byte((ubyte)life);
723 if (obj->type == OBJ_ROBOT) {
724 if (Robot_info[obj->id].boss_flag) {
725 if ((GameTime > Boss_cloak_start_time) && (GameTime < Boss_cloak_end_time))
732 switch (obj->movement_type) {
735 nd_write_vector(&obj->mtype.phys_info.velocity);
736 nd_write_vector(&obj->mtype.phys_info.thrust);
740 nd_write_vector(&obj->mtype.spin_rate);
750 switch (obj->control_type) {
756 nd_write_fix(obj->ctype.expl_info.spawn_time);
757 nd_write_fix(obj->ctype.expl_info.delete_time);
758 nd_write_short(obj->ctype.expl_info.delete_objnum);
766 nd_write_fix(obj->ctype.light_info.intensity);
773 case CT_SLEW: //the player is generally saved as slew
786 switch (obj->render_type) {
795 if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) {
796 nd_write_int(obj->rtype.pobj_info.model_num);
797 nd_write_int(obj->rtype.pobj_info.subobj_flags);
800 if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS))
802 for (i=0;i<MAX_SUBMODELS;i++)
803 nd_write_angvec(&obj->pobj_info.anim_angles[i]);
805 for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++)
806 nd_write_angvec(&obj->rtype.pobj_info.anim_angles[i]);
808 nd_write_int(obj->rtype.pobj_info.tmap_override);
814 case RT_WEAPON_VCLIP:
817 nd_write_int(obj->rtype.vclip_info.vclip_num);
818 nd_write_fix(obj->rtype.vclip_info.frametime);
819 nd_write_byte(obj->rtype.vclip_info.framenum);
832 int JustStartedRecording=0,JustStartedPlayback=0;
834 void newdemo_record_start_demo()
839 nd_write_byte(ND_EVENT_START_DEMO);
840 nd_write_byte(DEMO_VERSION);
841 nd_write_byte(DEMO_GAME_TYPE);
842 nd_write_fix(GameTime);
845 if (Game_mode & GM_MULTI)
846 nd_write_int(Game_mode | (Player_num << 16));
849 // NOTE LINK TO ABOVE!!!
850 nd_write_int(Game_mode);
853 if (Game_mode & GM_TEAM) {
854 nd_write_byte(Netgame.team_vector);
855 nd_write_string(Netgame.team_name[0]);
856 nd_write_string(Netgame.team_name[1]);
859 if (Game_mode & GM_MULTI) {
860 nd_write_byte((sbyte)N_players);
861 for (i = 0; i < N_players; i++) {
862 nd_write_string(Players[i].callsign);
863 nd_write_byte(Players[i].connected);
865 if (Game_mode & GM_MULTI_COOP) {
866 nd_write_int(Players[i].score);
868 nd_write_short((short)Players[i].net_killed_total);
869 nd_write_short((short)Players[i].net_kills_total);
874 // NOTE LINK TO ABOVE!!!
875 nd_write_int(Players[Player_num].score);
877 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
878 nd_write_short((short)Players[Player_num].primary_ammo[i]);
880 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
881 nd_write_short((short)Players[Player_num].secondary_ammo[i]);
883 nd_write_byte((sbyte)Players[Player_num].laser_level);
885 // Support for missions added here
887 nd_write_string(Current_mission_filename);
889 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
890 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
891 nd_write_int(Players[Player_num].flags); // be sure players flags are set
892 nd_write_byte((sbyte)Primary_weapon);
893 nd_write_byte((sbyte)Secondary_weapon);
894 Newdemo_start_frame = FrameCount;
895 JustStartedRecording=1;
897 newdemo_set_new_level(Current_level_num);
902 void newdemo_record_start_frame(int frame_number, fix frame_time )
906 if (Newdemo_no_space) {
907 newdemo_stop_playback();
913 for (i=0;i<MAX_OBJECTS;i++)
916 ViewWasRecorded[i]=0;
919 RenderingWasRecorded[i]=0;
921 frame_number -= Newdemo_start_frame;
923 Assert(frame_number >= 0 );
925 nd_write_byte(ND_EVENT_START_FRAME);
926 nd_write_short(frame_bytes_written - 1); // from previous frame
927 frame_bytes_written=3;
928 nd_write_int(frame_number);
929 nd_write_int(frame_time);
934 void newdemo_record_render_object(object * obj)
936 if (ViewWasRecorded[obj-Objects])
939 //if (obj==&Objects[Players[Player_num].objnum] && !Player_is_dead)
943 nd_write_byte(ND_EVENT_RENDER_OBJECT);
944 nd_write_object(obj);
948 extern ubyte RenderingType;
950 void newdemo_record_viewer_object(object * obj)
953 if (ViewWasRecorded[obj-Objects] && (ViewWasRecorded[obj-Objects]-1)==RenderingType)
955 //if (WasRecorded[obj-Objects])
957 if (RenderingWasRecorded[RenderingType])
960 ViewWasRecorded[obj-Objects]=RenderingType+1;
961 RenderingWasRecorded[RenderingType]=1;
963 nd_write_byte(ND_EVENT_VIEWER_OBJECT);
964 nd_write_byte(RenderingType);
965 nd_write_object(obj);
969 void newdemo_record_sound( int soundno )
972 nd_write_byte(ND_EVENT_SOUND);
973 nd_write_int( soundno );
977 //--unused-- void newdemo_record_sound_once( int soundno ) {
978 //--unused-- stop_time();
979 //--unused-- nd_write_byte( ND_EVENT_SOUND_ONCE );
980 //--unused-- nd_write_int( soundno );
981 //--unused-- start_time();
985 void newdemo_record_cockpit_change (int mode)
988 nd_write_byte (ND_EVENT_CHANGE_COCKPIT);
994 void newdemo_record_sound_3d( int soundno, int angle, int volume )
997 nd_write_byte( ND_EVENT_SOUND_3D );
998 nd_write_int( soundno );
999 nd_write_int( angle );
1000 nd_write_int( volume );
1004 void newdemo_record_sound_3d_once( int soundno, int angle, int volume )
1007 nd_write_byte( ND_EVENT_SOUND_3D_ONCE );
1008 nd_write_int( soundno );
1009 nd_write_int( angle );
1010 nd_write_int( volume );
1015 void newdemo_record_link_sound_to_object3( int soundno, short objnum, fix max_volume, fix max_distance, int loop_start, int loop_end )
1018 nd_write_byte( ND_EVENT_LINK_SOUND_TO_OBJ );
1019 nd_write_int( soundno );
1020 nd_write_int( Objects[objnum].signature );
1021 nd_write_int( max_volume );
1022 nd_write_int( max_distance );
1023 nd_write_int( loop_start );
1024 nd_write_int( loop_end );
1028 void newdemo_record_kill_sound_linked_to_object( int objnum )
1031 nd_write_byte( ND_EVENT_KILL_SOUND_TO_OBJ );
1032 nd_write_int( Objects[objnum].signature );
1037 void newdemo_record_wall_hit_process( int segnum, int side, int damage, int playernum )
1043 //playernum = playernum;
1044 nd_write_byte( ND_EVENT_WALL_HIT_PROCESS );
1045 nd_write_int( segnum );
1046 nd_write_int( side );
1047 nd_write_int( damage );
1048 nd_write_int( playernum );
1052 void newdemo_record_guided_start ()
1054 nd_write_byte (ND_EVENT_START_GUIDED);
1057 void newdemo_record_guided_end ()
1059 nd_write_byte (ND_EVENT_END_GUIDED);
1062 void newdemo_record_secret_exit_blown(int truth)
1065 nd_write_byte( ND_EVENT_SECRET_THINGY );
1066 nd_write_int( truth );
1070 void newdemo_record_trigger( int segnum, int side, int objnum,int shot )
1073 nd_write_byte( ND_EVENT_TRIGGER );
1074 nd_write_int( segnum );
1075 nd_write_int( side );
1076 nd_write_int( objnum );
1081 void newdemo_record_hostage_rescued( int hostage_number ) {
1083 nd_write_byte( ND_EVENT_HOSTAGE_RESCUED );
1084 nd_write_int( hostage_number );
1088 void newdemo_record_morph_frame(morph_data *md)
1092 nd_write_byte( ND_EVENT_MORPH_FRAME );
1094 newdemo_write( md->morph_vecs, sizeof(md->morph_vecs), 1 );
1095 newdemo_write( md->submodel_active, sizeof(md->submodel_active), 1 );
1096 newdemo_write( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 );
1098 nd_write_object( md->obj );
1102 void newdemo_record_wall_toggle( int segnum, int side )
1105 nd_write_byte( ND_EVENT_WALL_TOGGLE );
1106 nd_write_int( segnum );
1107 nd_write_int( side );
1111 void newdemo_record_control_center_destroyed()
1114 nd_write_byte( ND_EVENT_CONTROL_CENTER_DESTROYED );
1115 nd_write_int( Countdown_seconds_left );
1119 void newdemo_record_hud_message( char * message )
1122 nd_write_byte( ND_EVENT_HUD_MESSAGE );
1123 nd_write_string(message);
1127 void newdemo_record_palette_effect(short r, short g, short b )
1130 nd_write_byte( ND_EVENT_PALETTE_EFFECT );
1131 nd_write_short( r );
1132 nd_write_short( g );
1133 nd_write_short( b );
1137 void newdemo_record_player_energy(int old_energy, int energy)
1140 nd_write_byte( ND_EVENT_PLAYER_ENERGY );
1141 nd_write_byte((sbyte) old_energy);
1142 nd_write_byte((sbyte) energy);
1146 void newdemo_record_player_afterburner(fix old_afterburner, fix afterburner)
1149 nd_write_byte( ND_EVENT_PLAYER_AFTERBURNER );
1150 nd_write_byte((sbyte) (old_afterburner>>9));
1151 nd_write_byte((sbyte) (afterburner>>9));
1155 void newdemo_record_player_shields(int old_shield, int shield)
1158 nd_write_byte( ND_EVENT_PLAYER_SHIELD );
1159 nd_write_byte((sbyte)old_shield);
1160 nd_write_byte((sbyte)shield);
1164 void newdemo_record_player_flags(uint oflags, uint flags)
1167 nd_write_byte( ND_EVENT_PLAYER_FLAGS );
1168 nd_write_int(((short)oflags << 16) | (short)flags);
1172 void newdemo_record_player_weapon(int weapon_type, int weapon_num)
1175 nd_write_byte( ND_EVENT_PLAYER_WEAPON );
1176 nd_write_byte((sbyte)weapon_type);
1177 nd_write_byte((sbyte)weapon_num);
1179 nd_write_byte((sbyte)Secondary_weapon);
1181 nd_write_byte((sbyte)Primary_weapon);
1185 void newdemo_record_effect_blowup(short segment, int side, vms_vector *pnt)
1188 nd_write_byte (ND_EVENT_EFFECT_BLOWUP);
1189 nd_write_short(segment);
1190 nd_write_byte((sbyte)side);
1191 nd_write_vector(pnt);
1195 void newdemo_record_homing_distance(fix distance)
1198 nd_write_byte(ND_EVENT_HOMING_DISTANCE);
1199 nd_write_short((short)(distance>>16));
1203 void newdemo_record_letterbox(void)
1206 nd_write_byte(ND_EVENT_LETTERBOX);
1210 void newdemo_record_rearview(void)
1213 nd_write_byte(ND_EVENT_REARVIEW);
1217 void newdemo_record_restore_cockpit(void)
1220 nd_write_byte(ND_EVENT_RESTORE_COCKPIT);
1224 void newdemo_record_restore_rearview(void)
1227 nd_write_byte(ND_EVENT_RESTORE_REARVIEW);
1231 void newdemo_record_wall_set_tmap_num1(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1234 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM1);
1235 nd_write_short(seg);
1236 nd_write_byte(side);
1237 nd_write_short(cseg);
1238 nd_write_byte(cside);
1239 nd_write_short(tmap);
1243 void newdemo_record_wall_set_tmap_num2(short seg,ubyte side,short cseg,ubyte cside,short tmap)
1246 nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM2);
1247 nd_write_short(seg);
1248 nd_write_byte(side);
1249 nd_write_short(cseg);
1250 nd_write_byte(cside);
1251 nd_write_short(tmap);
1255 void newdemo_record_multi_cloak(int pnum)
1258 nd_write_byte(ND_EVENT_MULTI_CLOAK);
1259 nd_write_byte((sbyte)pnum);
1263 void newdemo_record_multi_decloak(int pnum)
1266 nd_write_byte(ND_EVENT_MULTI_DECLOAK);
1267 nd_write_byte((sbyte)pnum);
1271 void newdemo_record_multi_death(int pnum)
1274 nd_write_byte(ND_EVENT_MULTI_DEATH);
1275 nd_write_byte((sbyte)pnum);
1279 void newdemo_record_multi_kill(int pnum, sbyte kill)
1282 nd_write_byte(ND_EVENT_MULTI_KILL);
1283 nd_write_byte((sbyte)pnum);
1284 nd_write_byte(kill);
1288 void newdemo_record_multi_connect(int pnum, int new_player, char *new_callsign)
1291 nd_write_byte(ND_EVENT_MULTI_CONNECT);
1292 nd_write_byte((sbyte)pnum);
1293 nd_write_byte((sbyte)new_player);
1295 nd_write_string(Players[pnum].callsign);
1296 nd_write_int(Players[pnum].net_killed_total);
1297 nd_write_int(Players[pnum].net_kills_total);
1299 nd_write_string(new_callsign);
1303 void newdemo_record_multi_reconnect(int pnum)
1306 nd_write_byte(ND_EVENT_MULTI_RECONNECT);
1307 nd_write_byte((sbyte)pnum);
1311 void newdemo_record_multi_disconnect(int pnum)
1314 nd_write_byte(ND_EVENT_MULTI_DISCONNECT);
1315 nd_write_byte((sbyte)pnum);
1319 void newdemo_record_player_score(int score)
1322 nd_write_byte(ND_EVENT_PLAYER_SCORE);
1323 nd_write_int(score);
1327 void newdemo_record_multi_score(int pnum, int score)
1330 nd_write_byte(ND_EVENT_MULTI_SCORE);
1331 nd_write_byte((sbyte)pnum);
1332 nd_write_int(score - Players[pnum].score); // called before score is changed!!!!
1336 void newdemo_record_primary_ammo(int old_ammo, int new_ammo)
1339 nd_write_byte(ND_EVENT_PRIMARY_AMMO);
1341 nd_write_short((short)new_ammo);
1343 nd_write_short((short)old_ammo);
1344 nd_write_short((short)new_ammo);
1348 void newdemo_record_secondary_ammo(int old_ammo, int new_ammo)
1351 nd_write_byte(ND_EVENT_SECONDARY_AMMO);
1353 nd_write_short((short)new_ammo);
1355 nd_write_short((short)old_ammo);
1356 nd_write_short((short)new_ammo);
1360 void newdemo_record_door_opening(int segnum, int side)
1363 nd_write_byte(ND_EVENT_DOOR_OPENING);
1364 nd_write_short((short)segnum);
1365 nd_write_byte((sbyte)side);
1369 void newdemo_record_laser_level(sbyte old_level, sbyte new_level)
1372 nd_write_byte(ND_EVENT_LASER_LEVEL);
1373 nd_write_byte(old_level);
1374 nd_write_byte(new_level);
1378 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)
1380 Assert(front_wall_num <= 255 && back_wall_num <= 255);
1383 nd_write_byte(ND_EVENT_CLOAKING_WALL);
1384 nd_write_byte(front_wall_num);
1385 nd_write_byte(back_wall_num);
1386 nd_write_byte(type);
1387 nd_write_byte(state);
1388 nd_write_byte(cloak_value);
1389 nd_write_short(l0>>8);
1390 nd_write_short(l1>>8);
1391 nd_write_short(l2>>8);
1392 nd_write_short(l3>>8);
1396 void newdemo_set_new_level(int level_num)
1403 nd_write_byte(ND_EVENT_NEW_LEVEL);
1404 nd_write_byte((sbyte)level_num);
1405 nd_write_byte((sbyte)Current_level_num);
1407 if (JustStartedRecording==1)
1409 nd_write_int(Num_walls);
1410 for (i=0;i<Num_walls;i++)
1412 nd_write_byte (Walls[i].type);
1413 nd_write_byte (Walls[i].flags);
1414 nd_write_byte (Walls[i].state);
1416 seg = &Segments[Walls[i].segnum];
1417 side = Walls[i].sidenum;
1418 nd_write_short (seg->sides[side].tmap_num);
1419 nd_write_short (seg->sides[side].tmap_num2);
1420 JustStartedRecording=0;
1427 int newdemo_read_demo_start(int rnd_demo)
1429 sbyte i, version, game_type, laser_level;
1430 sbyte c, energy, shield;
1431 char text[128], current_mission[9];
1434 if ((c != ND_EVENT_START_DEMO) || nd_bad_read) {
1437 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_CORRUPT);
1438 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1439 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1442 nd_read_byte(&version);
1443 nd_read_byte(&game_type);
1444 if (game_type < DEMO_GAME_TYPE) {
1447 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1448 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1449 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Descent: First Strike";
1451 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1454 if (game_type != DEMO_GAME_TYPE) {
1457 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED);
1458 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1459 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Unknown Descent version";
1461 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1464 if (version < DEMO_VERSION) {
1467 sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_OLD);
1468 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1469 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1473 nd_read_fix(&GameTime);
1474 Boss_cloak_start_time=Boss_cloak_end_time=GameTime;
1475 JasonPlaybackTotal=0;
1477 nd_read_int(&Newdemo_game_mode);
1480 change_playernum_to((Newdemo_game_mode >> 16) & 0x7);
1481 if (Newdemo_game_mode & GM_TEAM) {
1482 nd_read_byte(&(Netgame.team_vector));
1483 nd_read_string(Netgame.team_name[0]);
1484 nd_read_string(Netgame.team_name[1]);
1486 if (Newdemo_game_mode & GM_MULTI) {
1491 // changed this to above two lines -- breaks on the mac because of
1493 // nd_read_byte((sbyte *)&N_players);
1494 for (i = 0 ; i < N_players; i++) {
1495 Players[i].cloak_time = 0;
1496 Players[i].invulnerable_time = 0;
1497 nd_read_string(Players[i].callsign);
1498 nd_read_byte(&(Players[i].connected));
1500 if (Newdemo_game_mode & GM_MULTI_COOP) {
1501 nd_read_int(&(Players[i].score));
1503 nd_read_short((short *)&(Players[i].net_killed_total));
1504 nd_read_short((short *)&(Players[i].net_kills_total));
1507 Game_mode = Newdemo_game_mode;
1508 multi_sort_kill_list();
1509 Game_mode = GM_NORMAL;
1512 nd_read_int(&(Players[Player_num].score)); // Note link to above if!
1514 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
1515 nd_read_short((short*)&(Players[Player_num].primary_ammo[i]));
1517 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
1518 nd_read_short((short*)&(Players[Player_num].secondary_ammo[i]));
1520 nd_read_byte(&laser_level);
1521 if (laser_level != Players[Player_num].laser_level) {
1522 Players[Player_num].laser_level = laser_level;
1523 update_laser_weapon_info();
1526 // Support for missions
1528 nd_read_string(current_mission);
1529 if (!load_mission_by_name(current_mission)) {
1533 sprintf(text, TXT_NOMISSION4DEMO, current_mission);
1534 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text;
1535 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
1540 nd_recorded_total = 0;
1541 nd_playback_total = 0;
1542 nd_read_byte(&energy);
1543 nd_read_byte(&shield);
1545 nd_read_int((int *)&(Players[Player_num].flags));
1546 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
1547 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1548 Newdemo_players_cloaked |= (1 << Player_num);
1550 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
1551 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1553 nd_read_byte((sbyte *)&Primary_weapon);
1554 nd_read_byte((sbyte *)&Secondary_weapon);
1556 // Next bit of code to fix problem that I introduced between 1.0 and 1.1
1557 // check the next byte -- it _will_ be a load_new_level event. If it is
1558 // not, then we must shift all bytes up by one.
1560 Players[Player_num].energy = i2f(energy);
1561 Players[Player_num].shields = i2f(shield);
1562 JustStartedPlayback=1;
1566 void newdemo_pop_ctrlcen_triggers()
1570 segment *seg, *csegp;
1572 for (i = 0; i < ControlCenterTriggers.num_links; i++) {
1573 seg = &Segments[ControlCenterTriggers.seg[i]];
1574 side = ControlCenterTriggers.side[i];
1575 csegp = &Segments[seg->children[side]];
1576 cside = find_connect_side(seg, csegp);
1577 anim_num = Walls[seg->sides[side].wall_num].clip_num;
1578 n = WallAnims[anim_num].num_frames;
1579 if (WallAnims[anim_num].flags & WCF_TMAP1) {
1580 seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[n-1];
1582 seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[n-1];
1587 #define N_PLAYER_SHIP_TEXTURES 6
1589 void nd_render_extras (ubyte,object *);
1590 extern void multi_apply_goal_textures ();
1591 ubyte Newdemo_flying_guided=0;
1593 int newdemo_read_frame_information()
1595 int done, segnum, side, objnum, soundno, angle, volume, i,shot;
1597 sbyte c,WhichWindow;
1598 static sbyte saved_letter_cockpit;
1599 static sbyte saved_rearview_cockpit;
1601 static char LastReadValue=101;
1606 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1607 for (segnum=0; segnum <= Highest_segment_index; segnum++)
1608 Segments[segnum].objects = -1;
1611 Players[Player_num].homing_object_dist = -F1_0;
1617 if (nd_bad_read) { done = -1; break; }
1621 case ND_EVENT_START_FRAME: { // Followed by an integer frame number, then a fix FrameTime
1622 short last_frame_length;
1625 nd_read_short(&last_frame_length);
1626 nd_read_int(&NewdemoFrameCount);
1627 nd_read_int((int *)&nd_recorded_time);
1628 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1629 nd_recorded_total += nd_recorded_time;
1630 NewdemoFrameCount--;
1632 if (nd_bad_read) { done = -1; break; }
1636 case ND_EVENT_VIEWER_OBJECT: // Followed by an object structure
1637 nd_read_byte (&WhichWindow);
1640 //mprintf ((0,"Reading extra!\n"));
1641 nd_read_object (&extraobj);
1642 if (Newdemo_vcr_state!=ND_STATE_PAUSED)
1644 if (nd_bad_read) { done = -1; break; }
1646 nd_render_extras (WhichWindow,&extraobj);
1651 //mprintf ((0,"Reading viewer!\n"));
1652 //Viewer=&Objects[0];
1653 nd_read_object(Viewer);
1655 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1656 if (nd_bad_read) { done = -1; break; }
1657 segnum = Viewer->segnum;
1658 Viewer->next = Viewer->prev = Viewer->segnum = -1;
1660 // HACK HACK HACK -- since we have multiple level recording, it can be the case
1661 // HACK HACK HACK -- that when rewinding the demo, the viewer is in a segment
1662 // HACK HACK HACK -- that is greater than the highest index of segments. Bash
1663 // HACK HACK HACK -- the viewer to segment 0 for bogus view.
1665 if (segnum > Highest_segment_index)
1667 obj_link(Viewer-Objects,segnum);
1672 case ND_EVENT_RENDER_OBJECT: // Followed by an object structure
1673 objnum = obj_allocate();
1676 obj = &Objects[objnum];
1677 nd_read_object(obj);
1678 if (nd_bad_read) { done = -1; break; }
1679 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1680 segnum = obj->segnum;
1681 obj->next = obj->prev = obj->segnum = -1;
1683 // HACK HACK HACK -- don't render objects is segments greater than Highest_segment_index
1684 // HACK HACK HACK -- (see above)
1686 if (segnum > Highest_segment_index)
1689 obj_link(obj-Objects,segnum);
1691 if ((obj->type == OBJ_PLAYER) && (Newdemo_game_mode & GM_MULTI)) {
1694 if (Newdemo_game_mode & GM_TEAM)
1695 player = get_team(obj->id);
1702 for (i=0;i<N_PLAYER_SHIP_TEXTURES;i++)
1703 multi_player_textures[player][i] = ObjBitmaps[ObjBitmapPtrs[Polygon_models[obj->rtype.pobj_info.model_num].first_texture+i]];
1705 multi_player_textures[player][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2]];
1706 multi_player_textures[player][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2+1]];
1707 obj->rtype.pobj_info.alt_textures = player+1;
1713 case ND_EVENT_SOUND:
1714 nd_read_int(&soundno);
1715 if (nd_bad_read) {done = -1; break; }
1716 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1717 digi_play_sample( soundno, F1_0 );
1720 //--unused case ND_EVENT_SOUND_ONCE:
1721 //--unused nd_read_int(&soundno);
1722 //--unused if (nd_bad_read) { done = -1; break; }
1723 //--unused if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1724 //--unused digi_play_sample_once( soundno, F1_0 );
1727 case ND_EVENT_SOUND_3D:
1728 nd_read_int(&soundno);
1729 nd_read_int(&angle);
1730 nd_read_int(&volume);
1731 if (nd_bad_read) { done = -1; break; }
1732 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1733 digi_play_sample_3d( soundno, angle, volume, 0 );
1736 case ND_EVENT_SOUND_3D_ONCE:
1737 nd_read_int(&soundno);
1738 nd_read_int(&angle);
1739 nd_read_int(&volume);
1740 if (nd_bad_read) { done = -1; break; }
1741 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
1742 digi_play_sample_3d( soundno, angle, volume, 1 );
1745 case ND_EVENT_LINK_SOUND_TO_OBJ:
1747 int soundno, objnum, max_volume, max_distance, loop_start, loop_end;
1749 nd_read_int( &soundno );
1750 nd_read_int( &signature );
1751 nd_read_int( &max_volume );
1752 nd_read_int( &max_distance );
1753 nd_read_int( &loop_start );
1754 nd_read_int( &loop_end );
1755 objnum = newdemo_find_object( signature );
1756 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1757 digi_link_sound_to_object3( soundno, objnum, 1, max_volume, max_distance, loop_start, loop_end );
1762 case ND_EVENT_KILL_SOUND_TO_OBJ:
1764 int objnum, signature;
1765 nd_read_int( &signature );
1766 objnum = newdemo_find_object( signature );
1767 if ( objnum > -1 ) { // @mk, 2/22/96, John told me to.
1768 digi_kill_sound_linked_to_object(objnum);
1773 case ND_EVENT_WALL_HIT_PROCESS: {
1777 nd_read_int(&segnum);
1779 nd_read_fix(&damage);
1780 nd_read_int(&player);
1781 if (nd_bad_read) { done = -1; break; }
1782 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1783 wall_hit_process(&Segments[segnum], side, damage, player, &(Objects[0]) );
1787 case ND_EVENT_TRIGGER:
1788 nd_read_int(&segnum);
1790 nd_read_int(&objnum);
1792 if (nd_bad_read) { done = -1; break; }
1793 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1795 mprintf ((0,"EVENT TRIGGER! shot=%d\n",shot));
1797 if (Triggers[Walls[Segments[segnum].sides[side].wall_num].trigger].type == TT_SECRET_EXIT) {
1801 Assert(c == ND_EVENT_SECRET_THINGY);
1802 nd_read_int(&truth);
1804 check_trigger(&Segments[segnum], side, objnum,shot);
1806 check_trigger(&Segments[segnum], side, objnum,shot);
1810 case ND_EVENT_HOSTAGE_RESCUED: {
1813 nd_read_int(&hostage_number);
1814 if (nd_bad_read) { done = -1; break; }
1815 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1816 hostage_rescue( hostage_number );
1820 case ND_EVENT_MORPH_FRAME: {
1824 md = &morph_objects[0];
1825 if (newdemo_read( md->morph_vecs, sizeof(md->morph_vecs), 1 )!=1) { done=-1; break; }
1826 if (newdemo_read( md->submodel_active, sizeof(md->submodel_active), 1 )!=1) { done=-1; break; }
1827 if (newdemo_read( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 )!=1) { done=-1; break; }
1829 objnum = obj_allocate();
1832 obj = &Objects[objnum];
1833 nd_read_object(obj);
1834 obj->render_type = RT_POLYOBJ;
1835 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1836 if (nd_bad_read) { done = -1; break; }
1837 if (Newdemo_vcr_state != ND_STATE_PAUSED) {
1838 segnum = obj->segnum;
1839 obj->next = obj->prev = obj->segnum = -1;
1840 obj_link(obj-Objects,segnum);
1846 case ND_EVENT_WALL_TOGGLE:
1847 nd_read_int(&segnum);
1849 if (nd_bad_read) {done = -1; break; }
1850 if (Newdemo_vcr_state != ND_STATE_PAUSED)
1851 wall_toggle(&Segments[segnum], side);
1854 case ND_EVENT_CONTROL_CENTER_DESTROYED:
1855 nd_read_int(&Countdown_seconds_left);
1856 Control_center_destroyed = 1;
1857 if (nd_bad_read) { done = -1; break; }
1858 if (!Newdemo_cntrlcen_destroyed) {
1859 newdemo_pop_ctrlcen_triggers();
1860 Newdemo_cntrlcen_destroyed = 1;
1861 //do_controlcen_destroyed_stuff(NULL);
1865 case ND_EVENT_HUD_MESSAGE: {
1868 nd_read_string(&(hud_msg[0]));
1869 if (nd_bad_read) { done = -1; break; }
1870 HUD_init_message( hud_msg );
1873 case ND_EVENT_START_GUIDED:
1874 Newdemo_flying_guided=1;
1875 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1876 Newdemo_flying_guided=0;
1878 case ND_EVENT_END_GUIDED:
1879 Newdemo_flying_guided=0;
1880 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
1881 Newdemo_flying_guided=1;
1884 case ND_EVENT_PALETTE_EFFECT: {
1890 if (nd_bad_read) { done = -1; break; }
1891 PALETTE_FLASH_SET(r,g,b);
1895 case ND_EVENT_PLAYER_ENERGY: {
1899 nd_read_byte(&old_energy);
1900 nd_read_byte(&energy);
1901 if (nd_bad_read) {done = -1; break; }
1902 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1903 Players[Player_num].energy = i2f(energy);
1904 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1905 if (old_energy != -128)
1906 Players[Player_num].energy = i2f(old_energy);
1911 case ND_EVENT_PLAYER_AFTERBURNER: {
1913 sbyte old_afterburner;
1915 nd_read_byte(&old_afterburner);
1916 nd_read_byte(&afterburner);
1917 if (nd_bad_read) {done = -1; break; }
1918 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1919 Afterburner_charge = afterburner<<9;
1920 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1921 if (old_afterburner != -128)
1922 Afterburner_charge = old_afterburner<<9;
1927 case ND_EVENT_PLAYER_SHIELD: {
1931 nd_read_byte(&old_shield);
1932 nd_read_byte(&shield);
1933 if (nd_bad_read) {done = -1; break; }
1934 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1935 Players[Player_num].shields = i2f(shield);
1936 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1937 if (old_shield != -128)
1938 Players[Player_num].shields = i2f(old_shield);
1943 case ND_EVENT_PLAYER_FLAGS: {
1946 nd_read_int((int *)&(Players[Player_num].flags));
1947 if (nd_bad_read) {done = -1; break; }
1949 oflags = Players[Player_num].flags >> 16;
1950 Players[Player_num].flags &= 0xffff;
1952 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || ((Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) && (oflags != 0xffff)) ) {
1953 if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1954 Players[Player_num].cloak_time = 0;
1955 Newdemo_players_cloaked &= ~(1 << Player_num);
1957 if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1958 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
1959 Newdemo_players_cloaked |= (1 << Player_num);
1961 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1962 Players[Player_num].invulnerable_time = 0;
1963 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1964 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1965 Players[Player_num].flags = oflags;
1966 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
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_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) {
1972 Players[Player_num].cloak_time = 0;
1973 Newdemo_players_cloaked &= ~(1 << Player_num);
1975 if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1976 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
1977 if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE))
1978 Players[Player_num].invulnerable_time = 0;
1980 update_laser_weapon_info(); // in case of quad laser change
1984 case ND_EVENT_PLAYER_WEAPON: {
1985 sbyte weapon_type, weapon_num;
1988 nd_read_byte(&weapon_type);
1989 nd_read_byte(&weapon_num);
1990 nd_read_byte(&old_weapon);
1991 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
1992 if (weapon_type == 0)
1993 Primary_weapon = (int)weapon_num;
1995 Secondary_weapon = (int)weapon_num;
1996 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
1997 if (weapon_type == 0)
1998 Primary_weapon = (int)old_weapon;
2000 Secondary_weapon = (int)old_weapon;
2005 case ND_EVENT_EFFECT_BLOWUP: {
2011 //create a dummy object which will be the weapon that hits
2012 //the monitor. the blowup code wants to know who the parent of the
2013 //laser is, so create a laser whose parent is the player
2014 dummy.ctype.laser_info.parent_type = OBJ_PLAYER;
2016 nd_read_short(&segnum);
2017 nd_read_byte(&side);
2018 nd_read_vector(&pnt);
2019 if (Newdemo_vcr_state != ND_STATE_PAUSED)
2020 check_effect_blowup(&(Segments[segnum]), side, &pnt, &dummy, 0);
2024 case ND_EVENT_HOMING_DISTANCE: {
2027 nd_read_short(&distance);
2028 Players[Player_num].homing_object_dist = i2f((int)(distance << 16));
2032 case ND_EVENT_LETTERBOX:
2033 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2034 saved_letter_cockpit = Cockpit_mode;
2035 select_cockpit(CM_LETTERBOX);
2036 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2037 select_cockpit(saved_letter_cockpit);
2040 case ND_EVENT_CHANGE_COCKPIT: {
2043 nd_read_int (&dummy);
2044 select_cockpit (dummy);
2048 case ND_EVENT_REARVIEW:
2049 if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2050 saved_rearview_cockpit = Cockpit_mode;
2051 if (Cockpit_mode == CM_FULL_COCKPIT)
2052 select_cockpit(CM_REAR_VIEW);
2054 } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2055 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2056 saved_rearview_cockpit = CM_FULL_COCKPIT;
2057 select_cockpit(saved_rearview_cockpit);
2062 case ND_EVENT_RESTORE_COCKPIT:
2063 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2064 saved_letter_cockpit = Cockpit_mode;
2065 select_cockpit(CM_LETTERBOX);
2066 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2067 select_cockpit(saved_letter_cockpit);
2071 case ND_EVENT_RESTORE_REARVIEW:
2072 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2073 saved_rearview_cockpit = Cockpit_mode;
2074 if (Cockpit_mode == CM_FULL_COCKPIT)
2075 select_cockpit(CM_REAR_VIEW);
2077 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2078 if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore
2079 saved_rearview_cockpit = CM_FULL_COCKPIT;
2080 select_cockpit(saved_rearview_cockpit);
2086 case ND_EVENT_WALL_SET_TMAP_NUM1: {
2087 short seg, cseg, tmap;
2090 nd_read_short(&seg);
2091 nd_read_byte(&side);
2092 nd_read_short(&cseg);
2093 nd_read_byte(&cside);
2094 nd_read_short( &tmap );
2095 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD))
2096 Segments[seg].sides[side].tmap_num = Segments[cseg].sides[cside].tmap_num = tmap;
2100 case ND_EVENT_WALL_SET_TMAP_NUM2: {
2101 short seg, cseg, tmap;
2104 nd_read_short(&seg);
2105 nd_read_byte(&side);
2106 nd_read_short(&cseg);
2107 nd_read_byte(&cside);
2108 nd_read_short( &tmap );
2109 if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) {
2110 Assert(tmap!=0 && Segments[seg].sides[side].tmap_num2!=0);
2111 Segments[seg].sides[side].tmap_num2 = Segments[cseg].sides[cside].tmap_num2 = tmap;
2116 case ND_EVENT_MULTI_CLOAK: {
2119 nd_read_byte(&pnum);
2120 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2121 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2122 Players[pnum].cloak_time = 0;
2123 Newdemo_players_cloaked &= ~(1 << pnum);
2124 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2125 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2126 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2127 Newdemo_players_cloaked |= (1 << pnum);
2132 case ND_EVENT_MULTI_DECLOAK: {
2135 nd_read_byte(&pnum);
2137 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2138 Players[pnum].flags |= PLAYER_FLAGS_CLOAKED;
2139 Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2140 Newdemo_players_cloaked |= (1 << pnum);
2141 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2142 Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED;
2143 Players[pnum].cloak_time = 0;
2144 Newdemo_players_cloaked &= ~(1 << pnum);
2149 case ND_EVENT_MULTI_DEATH: {
2152 nd_read_byte(&pnum);
2153 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2154 Players[pnum].net_killed_total--;
2155 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2156 Players[pnum].net_killed_total++;
2161 case ND_EVENT_MULTI_KILL: {
2164 nd_read_byte(&pnum);
2165 nd_read_byte(&kill);
2166 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2167 Players[pnum].net_kills_total -= kill;
2168 if (Newdemo_game_mode & GM_TEAM)
2169 team_kills[get_team(pnum)] -= kill;
2170 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2171 Players[pnum].net_kills_total += kill;
2172 if (Newdemo_game_mode & GM_TEAM)
2173 team_kills[get_team(pnum)] += kill;
2175 Game_mode = Newdemo_game_mode;
2176 multi_sort_kill_list();
2177 Game_mode = GM_NORMAL;
2181 case ND_EVENT_MULTI_CONNECT: {
2182 sbyte pnum, new_player;
2183 int killed_total, kills_total;
2184 char new_callsign[CALLSIGN_LEN+1], old_callsign[CALLSIGN_LEN+1];
2186 nd_read_byte(&pnum);
2187 nd_read_byte(&new_player);
2189 nd_read_string(old_callsign);
2190 nd_read_int(&killed_total);
2191 nd_read_int(&kills_total);
2193 nd_read_string(new_callsign);
2194 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2195 Players[pnum].connected = 0;
2197 memcpy(Players[pnum].callsign, old_callsign, CALLSIGN_LEN+1);
2198 Players[pnum].net_killed_total = killed_total;
2199 Players[pnum].net_kills_total = kills_total;
2203 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2204 Players[pnum].connected = 1;
2205 Players[pnum].net_kills_total = 0;
2206 Players[pnum].net_killed_total = 0;
2207 memcpy(Players[pnum].callsign, new_callsign, CALLSIGN_LEN+1);
2214 case ND_EVENT_MULTI_RECONNECT: {
2217 nd_read_byte(&pnum);
2218 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2219 Players[pnum].connected = 0;
2220 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2221 Players[pnum].connected = 1;
2225 case ND_EVENT_MULTI_DISCONNECT: {
2228 nd_read_byte(&pnum);
2229 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2230 Players[pnum].connected = 1;
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 = 0;
2236 case ND_EVENT_MULTI_SCORE: {
2240 nd_read_byte(&pnum);
2241 nd_read_int(&score);
2242 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2243 Players[pnum].score -= score;
2244 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2245 Players[pnum].score += score;
2246 Game_mode = Newdemo_game_mode;
2247 multi_sort_kill_list();
2248 Game_mode = GM_NORMAL;
2253 case ND_EVENT_PLAYER_SCORE: {
2256 nd_read_int(&score);
2257 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2258 Players[Player_num].score -= score;
2259 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2260 Players[Player_num].score += score;
2265 case ND_EVENT_PRIMARY_AMMO: {
2266 short old_ammo, new_ammo;
2268 nd_read_short(&old_ammo);
2269 nd_read_short(&new_ammo);
2271 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2272 Players[Player_num].primary_ammo[Primary_weapon] = old_ammo;
2273 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2274 Players[Player_num].primary_ammo[Primary_weapon] = new_ammo;
2278 case ND_EVENT_SECONDARY_AMMO: {
2279 short old_ammo, new_ammo;
2281 nd_read_short(&old_ammo);
2282 nd_read_short(&new_ammo);
2284 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2285 Players[Player_num].secondary_ammo[Secondary_weapon] = old_ammo;
2286 else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD))
2287 Players[Player_num].secondary_ammo[Secondary_weapon] = new_ammo;
2291 case ND_EVENT_DOOR_OPENING: {
2295 nd_read_short(&segnum);
2296 nd_read_byte(&side);
2297 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2300 segment *segp, *csegp;
2302 segp = &Segments[segnum];
2303 csegp = &Segments[segp->children[side]];
2304 cside = find_connect_side(segp, csegp);
2305 anim_num = Walls[segp->sides[side].wall_num].clip_num;
2307 if (WallAnims[anim_num].flags & WCF_TMAP1) {
2308 segp->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[0];
2310 segp->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[0];
2316 case ND_EVENT_LASER_LEVEL: {
2317 sbyte old_level, new_level;
2319 nd_read_byte(&old_level);
2320 nd_read_byte(&new_level);
2321 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) {
2322 Players[Player_num].laser_level = old_level;
2323 update_laser_weapon_info();
2324 } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) {
2325 Players[Player_num].laser_level = new_level;
2326 update_laser_weapon_info();
2331 case ND_EVENT_CLOAKING_WALL: {
2332 sbyte back_wall_num,front_wall_num,type,state,cloak_value;
2337 nd_read_byte(&front_wall_num);
2338 nd_read_byte(&back_wall_num);
2339 nd_read_byte(&type);
2340 nd_read_byte(&state);
2341 nd_read_byte(&cloak_value);
2347 Walls[front_wall_num].type = type;
2348 Walls[front_wall_num].state = state;
2349 Walls[front_wall_num].cloak_value = cloak_value;
2350 segp = &Segments[Walls[front_wall_num].segnum];
2351 sidenum = Walls[front_wall_num].sidenum;
2352 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2353 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2354 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2355 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2357 Walls[back_wall_num].type = type;
2358 Walls[back_wall_num].state = state;
2359 Walls[back_wall_num].cloak_value = cloak_value;
2360 segp = &Segments[Walls[back_wall_num].segnum];
2361 sidenum = Walls[back_wall_num].sidenum;
2362 segp->sides[sidenum].uvls[0].l = ((int) l0) << 8;
2363 segp->sides[sidenum].uvls[1].l = ((int) l1) << 8;
2364 segp->sides[sidenum].uvls[2].l = ((int) l2) << 8;
2365 segp->sides[sidenum].uvls[3].l = ((int) l3) << 8;
2370 case ND_EVENT_NEW_LEVEL: {
2371 sbyte new_level, old_level, loaded_level;
2373 nd_read_byte (&new_level);
2374 nd_read_byte (&old_level);
2375 if (Newdemo_vcr_state == ND_STATE_PAUSED)
2379 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2380 loaded_level = old_level;
2382 loaded_level = new_level;
2383 for (i = 0; i < MAX_PLAYERS; i++) {
2384 Players[i].cloak_time = 0;
2385 Players[i].flags &= ~PLAYER_FLAGS_CLOAKED;
2388 if ((loaded_level < Last_secret_level) || (loaded_level > Last_level)) {
2391 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2392 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2393 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2394 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2398 LoadLevel((int)loaded_level,1);
2399 Newdemo_cntrlcen_destroyed = 0;
2401 if (JustStartedPlayback)
2403 nd_read_int (&Num_walls);
2404 for (i=0;i<Num_walls;i++) // restore the walls
2406 nd_read_byte ((signed char *)&Walls[i].type);
2407 nd_read_byte ((signed char *)&Walls[i].flags);
2408 nd_read_byte ((signed char *)&Walls[i].state);
2410 seg = &Segments[Walls[i].segnum];
2411 side = Walls[i].sidenum;
2412 nd_read_short (&seg->sides[side].tmap_num);
2413 nd_read_short (&seg->sides[side].tmap_num2);
2416 if (Newdemo_game_mode & GM_CAPTURE)
2417 multi_apply_goal_textures ();
2419 JustStartedPlayback=0;
2423 // so says Rob H.!!! if (Newdemo_game_mode & GM_MULTI) {
2424 // so says Rob H.!!! for (i = 0; i < Num_walls; i++) {
2425 // so says Rob H.!!! if (Walls[i].type == WALL_BLASTABLE)
2426 // so says Rob H.!!! {
2427 // so says Rob H.!!! int a, n;
2428 // so says Rob H.!!! int side;
2429 // so says Rob H.!!! segment *seg;
2430 // so says Rob H.!!!
2431 // so says Rob H.!!! seg = &Segments[Walls[i].segnum];
2432 // so says Rob H.!!! side = Walls[i].sidenum;
2433 // so says Rob H.!!! a = Walls[i].clip_num;
2434 // so says Rob H.!!! n = WallAnims[a].num_frames;
2435 // so says Rob H.!!! seg->sides[side].tmap_num = WallAnims[a].frames[n-1];
2436 // so says Rob H.!!! Walls[i].flags |= WALL_BLASTED;
2437 // so says Rob H.!!! }
2438 // so says Rob H.!!! }
2439 // so says Rob H.!!! }
2441 reset_palette_add(); // get palette back to normal
2446 case ND_EVENT_EOF: {
2448 PHYSFS_seek(infile, PHYSFS_tell(infile) - 1); // get back to the EOF marker
2450 NewdemoFrameCount++;
2464 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_ERR_READING;
2465 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_DEMO_OLD_CORRUPT;
2466 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2472 void newdemo_goto_beginning()
2474 //if (NewdemoFrameCount == 0)
2476 PHYSFS_seek(infile, 0);
2477 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2478 if (newdemo_read_demo_start(0))
2479 newdemo_stop_playback();
2480 if (newdemo_read_frame_information() == -1)
2481 newdemo_stop_playback();
2482 if (newdemo_read_frame_information() == -1)
2483 newdemo_stop_playback();
2484 Newdemo_vcr_state = ND_STATE_PAUSED;
2488 void newdemo_goto_end()
2490 short frame_length, byte_count, bshort;
2491 sbyte level, bbyte, laser_level;
2492 sbyte energy, shield, c;
2495 PHYSFS_seek(infile, PHYSFS_tell(infile) - 2);
2496 nd_read_byte(&level);
2498 if ((level < Last_secret_level) || (level > Last_level)) {
2501 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK;
2502 m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD;
2503 m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT;
2504 newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL );
2505 newdemo_stop_playback();
2508 if (level != Current_level_num)
2511 PHYSFS_seek(infile, PHYSFS_tell(infile) - 4);
2512 nd_read_short(&byte_count);
2513 PHYSFS_seek(infile, PHYSFS_tell(infile) - 2 - byte_count);
2515 nd_read_short(&frame_length);
2516 loc = PHYSFS_tell(infile);
2517 if (Newdemo_game_mode & GM_MULTI)
2518 nd_read_byte(&Newdemo_players_cloaked);
2520 nd_read_byte(&bbyte);
2521 nd_read_byte(&bbyte);
2522 nd_read_short(&bshort);
2525 nd_read_byte(&energy);
2526 nd_read_byte(&shield);
2527 Players[Player_num].energy = i2f(energy);
2528 Players[Player_num].shields = i2f(shield);
2529 nd_read_int((int *)&(Players[Player_num].flags));
2530 if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) {
2531 Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2532 Newdemo_players_cloaked |= (1 << Player_num);
2534 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2535 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2536 nd_read_byte((sbyte *)&Primary_weapon);
2537 nd_read_byte((sbyte *)&Secondary_weapon);
2538 for (i = 0; i < MAX_PRIMARY_WEAPONS; i++)
2539 nd_read_short((short *)&(Players[Player_num].primary_ammo[i]));
2540 for (i = 0; i < MAX_SECONDARY_WEAPONS; i++)
2541 nd_read_short((short *)&(Players[Player_num].secondary_ammo[i]));
2542 nd_read_byte(&laser_level);
2543 if (laser_level != Players[Player_num].laser_level) {
2544 Players[Player_num].laser_level = laser_level;
2545 update_laser_weapon_info();
2548 if (Newdemo_game_mode & GM_MULTI) {
2551 // see newdemo_read_start_demo for explanation of
2552 // why this is commented out
2553 // nd_read_byte((sbyte *)&N_players);
2554 for (i = 0; i < N_players; i++) {
2555 nd_read_string(Players[i].callsign);
2556 nd_read_byte(&(Players[i].connected));
2557 if (Newdemo_game_mode & GM_MULTI_COOP) {
2558 nd_read_int(&(Players[i].score));
2560 nd_read_short((short *)&(Players[i].net_killed_total));
2561 nd_read_short((short *)&(Players[i].net_kills_total));
2565 nd_read_int(&(Players[Player_num].score));
2568 PHYSFS_seek(infile, loc);
2569 PHYSFS_seek(infile, PHYSFS_tell(infile) - frame_length);
2570 nd_read_int(&NewdemoFrameCount); // get the frame count
2571 NewdemoFrameCount--;
2572 PHYSFS_seek(infile, PHYSFS_tell(infile) + 4);
2573 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2574 newdemo_read_frame_information(); // then the frame information
2575 Newdemo_vcr_state = ND_STATE_PAUSED;
2579 void newdemo_back_frames(int frames)
2581 short last_frame_length;
2584 for (i = 0; i < frames; i++)
2586 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
2587 nd_read_short(&last_frame_length);
2588 PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
2590 if (!Newdemo_at_eof && newdemo_read_frame_information() == -1) {
2591 newdemo_stop_playback();
2597 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
2598 nd_read_short(&last_frame_length);
2599 PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
2605 * routine to interpolate the viewer position. the current position is
2606 * stored in the Viewer object. Save this position, and read the next
2607 * frame to get all objects read in. Calculate the delta playback and
2608 * the delta recording frame times between the two frames, then intepolate
2609 * the viewers position accordingly. nd_recorded_time is the time that it
2610 * took the recording to render the frame that we are currently looking
2614 void interpolate_frame(fix d_play, fix d_recorded)
2616 int i, j, num_cur_objs;
2620 factor = fixdiv(d_play, d_recorded);
2624 num_cur_objs = Highest_object_index;
2625 cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1));
2626 if (cur_objs == NULL) {
2627 mprintf((0,"Couldn't get %d bytes for cur_objs in interpolate_frame\n", sizeof(object) * num_cur_objs));
2631 for (i = 0; i <= num_cur_objs; i++)
2632 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2634 Newdemo_vcr_state = ND_STATE_PAUSED;
2635 if (newdemo_read_frame_information() == -1) {
2637 newdemo_stop_playback();
2641 for (i = 0; i <= num_cur_objs; i++) {
2642 for (j = 0; j <= Highest_object_index; j++) {
2643 if (cur_objs[i].signature == Objects[j].signature) {
2644 sbyte render_type = cur_objs[i].render_type;
2645 //fix delta_p, delta_h, delta_b;
2646 fix delta_x, delta_y, delta_z;
2647 //vms_angvec cur_angles, dest_angles;
2649 // Extract the angles from the object orientation matrix.
2650 // Some of this code taken from ai_turn_towards_vector
2651 // Don't do the interpolation on certain render types which don't use an orientation matrix
2653 if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) {
2655 vms_vector fvec1, fvec2, rvec1, rvec2;
2658 fvec1 = cur_objs[i].orient.fvec;
2659 vm_vec_scale(&fvec1, F1_0-factor);
2660 fvec2 = Objects[j].orient.fvec;
2661 vm_vec_scale(&fvec2, factor);
2662 vm_vec_add2(&fvec1, &fvec2);
2663 mag1 = vm_vec_normalize_quick(&fvec1);
2664 if (mag1 > F1_0/256) {
2665 rvec1 = cur_objs[i].orient.rvec;
2666 vm_vec_scale(&rvec1, F1_0-factor);
2667 rvec2 = Objects[j].orient.rvec;
2668 vm_vec_scale(&rvec2, factor);
2669 vm_vec_add2(&rvec1, &rvec2);
2670 vm_vec_normalize_quick(&rvec1); // Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1
2671 vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2674 //--old new way -- vms_vector fvec1, fvec2, rvec1, rvec2;
2676 //--old new way -- fvec1 = cur_objs[i].orient.fvec;
2677 //--old new way -- vm_vec_scale(&fvec1, F1_0-factor);
2678 //--old new way -- fvec2 = Objects[j].orient.fvec;
2679 //--old new way -- vm_vec_scale(&fvec2, factor);
2680 //--old new way -- vm_vec_add2(&fvec1, &fvec2);
2681 //--old new way -- vm_vec_normalize_quick(&fvec1);
2683 //--old new way -- rvec1 = cur_objs[i].orient.rvec;
2684 //--old new way -- vm_vec_scale(&rvec1, F1_0-factor);
2685 //--old new way -- rvec2 = Objects[j].orient.rvec;
2686 //--old new way -- vm_vec_scale(&rvec2, factor);
2687 //--old new way -- vm_vec_add2(&rvec1, &rvec2);
2688 //--old new way -- vm_vec_normalize_quick(&rvec1);
2690 //--old new way -- vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1);
2692 // -- old fashioned way -- vm_extract_angles_matrix(&cur_angles, &(cur_objs[i].orient));
2693 // -- old fashioned way -- vm_extract_angles_matrix(&dest_angles, &(Objects[j].orient));
2694 // -- old fashioned way --
2695 // -- old fashioned way -- delta_p = (dest_angles.p - cur_angles.p);
2696 // -- old fashioned way -- delta_h = (dest_angles.h - cur_angles.h);
2697 // -- old fashioned way -- delta_b = (dest_angles.b - cur_angles.b);
2698 // -- old fashioned way --
2699 // -- old fashioned way -- if (delta_p != 0) {
2700 // -- old fashioned way -- if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0;
2701 // -- old fashioned way -- if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0;
2702 // -- old fashioned way -- delta_p = fixmul(delta_p, factor);
2703 // -- old fashioned way -- cur_angles.p += delta_p;
2704 // -- old fashioned way -- }
2705 // -- old fashioned way -- if (delta_h != 0) {
2706 // -- old fashioned way -- if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0;
2707 // -- old fashioned way -- if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0;
2708 // -- old fashioned way -- delta_h = fixmul(delta_h, factor);
2709 // -- old fashioned way -- cur_angles.h += delta_h;
2710 // -- old fashioned way -- }
2711 // -- old fashioned way -- if (delta_b != 0) {
2712 // -- old fashioned way -- if (delta_b > F1_0/2) delta_b = dest_angles.b - cur_angles.b - F1_0;
2713 // -- old fashioned way -- if (delta_b < -F1_0/2) delta_b = dest_angles.b - cur_angles.b + F1_0;
2714 // -- old fashioned way -- delta_b = fixmul(delta_b, factor);
2715 // -- old fashioned way -- cur_angles.b += delta_b;
2716 // -- old fashioned way -- }
2719 // Interpolate the object position. This is just straight linear
2722 delta_x = Objects[j].pos.x - cur_objs[i].pos.x;
2723 delta_y = Objects[j].pos.y - cur_objs[i].pos.y;
2724 delta_z = Objects[j].pos.z - cur_objs[i].pos.z;
2726 delta_x = fixmul(delta_x, factor);
2727 delta_y = fixmul(delta_y, factor);
2728 delta_z = fixmul(delta_z, factor);
2730 cur_objs[i].pos.x += delta_x;
2731 cur_objs[i].pos.y += delta_y;
2732 cur_objs[i].pos.z += delta_z;
2734 // -- old fashioned way --// stuff the new angles back into the object structure
2735 // -- old fashioned way -- vm_angles_2_matrix(&(cur_objs[i].orient), &cur_angles);
2740 // get back to original position in the demo file. Reread the current
2741 // frame information again to reset all of the object stuff not covered
2742 // with Highest_object_index and the object array (previously rendered
2743 // objects, etc....)
2745 newdemo_back_frames(1);
2746 newdemo_back_frames(1);
2747 if (newdemo_read_frame_information() == -1)
2748 newdemo_stop_playback();
2749 Newdemo_vcr_state = ND_STATE_PLAYBACK;
2751 for (i = 0; i <= num_cur_objs; i++)
2752 memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object));
2753 Highest_object_index = num_cur_objs;
2757 void newdemo_playback_one_frame()
2759 int frames_back, i, level;
2760 static fix base_interpol_time = 0;
2761 static fix d_recorded = 0;
2763 for (i = 0; i < MAX_PLAYERS; i++)
2764 if (Newdemo_players_cloaked & (1 << i))
2765 Players[i].cloak_time = GameTime - (CLOAK_TIME_MAX / 2);
2767 if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)
2768 Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2);
2770 if (Newdemo_vcr_state == ND_STATE_PAUSED) // render a frame or not
2773 if (Newdemo_vcr_state == ND_STATE_PLAYBACK)
2774 DoJasonInterpolate(nd_recorded_time);
2776 Control_center_destroyed = 0;
2777 Countdown_seconds_left = -1;
2778 PALETTE_FLASH_SET(0,0,0); //clear flash
2780 if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD))
2782 level = Current_level_num;
2783 if (NewdemoFrameCount == 0)
2785 else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (NewdemoFrameCount < 10)) {
2786 newdemo_goto_beginning();
2789 if (Newdemo_vcr_state == ND_STATE_REWINDING)
2793 if (Newdemo_at_eof) {
2794 PHYSFS_seek(infile, PHYSFS_tell(infile) + 11);
2796 newdemo_back_frames(frames_back);
2798 if (level != Current_level_num)
2799 newdemo_pop_ctrlcen_triggers();
2801 if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) {
2802 if (level != Current_level_num)
2803 newdemo_back_frames(1);
2804 Newdemo_vcr_state = ND_STATE_PAUSED;
2807 else if (Newdemo_vcr_state == ND_STATE_FASTFORWARD) {
2808 if (!Newdemo_at_eof)
2810 for (i = 0; i < 10; i++)
2812 if (newdemo_read_frame_information() == -1)
2815 Newdemo_vcr_state = ND_STATE_PAUSED;
2817 newdemo_stop_playback();
2823 Newdemo_vcr_state = ND_STATE_PAUSED;
2825 else if (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD) {
2826 if (!Newdemo_at_eof) {
2827 level = Current_level_num;
2828 if (newdemo_read_frame_information() == -1) {
2829 if (!Newdemo_at_eof)
2830 newdemo_stop_playback();
2832 if (level != Current_level_num) {
2833 if (newdemo_read_frame_information() == -1) {
2834 if (!Newdemo_at_eof)
2835 newdemo_stop_playback();
2838 Newdemo_vcr_state = ND_STATE_PAUSED;
2840 Newdemo_vcr_state = ND_STATE_PAUSED;
2844 // First, uptate the total playback time to date. Then we check to see
2845 // if we need to change the playback style to interpolate frames or
2846 // skip frames based on where the playback time is relative to the
2849 if (NewdemoFrameCount <= 0)
2850 nd_playback_total = nd_recorded_total; // baseline total playback time
2852 nd_playback_total += FrameTime;
2853 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2854 if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) {
2855 playback_style = INTERPOLATE_PLAYBACK;
2856 nd_playback_total = nd_recorded_total + FrameTime; // baseline playback time
2857 base_interpol_time = nd_recorded_total;
2858 d_recorded = nd_recorded_time; // baseline delta recorded
2860 if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10))
2861 if (nd_playback_total > nd_recorded_total)
2862 playback_style = SKIP_PLAYBACK;
2865 if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) {
2868 if (nd_recorded_total - nd_playback_total < FrameTime) {
2869 d_recorded = nd_recorded_total - nd_playback_total;
2871 while (nd_recorded_total - nd_playback_total < FrameTime) {
2873 int i, j, num_objs, level;
2875 num_objs = Highest_object_index;
2876 cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1));
2877 if (cur_objs == NULL) {
2878 Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs);
2881 for (i = 0; i <= num_objs; i++)
2882 memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object));
2884 level = Current_level_num;
2885 if (newdemo_read_frame_information() == -1) {
2887 newdemo_stop_playback();
2890 if (level != Current_level_num) {
2892 if (newdemo_read_frame_information() == -1)
2893 newdemo_stop_playback();
2897 // for each new object in the frame just read in, determine if there is
2898 // a corresponding object that we have been interpolating. If so, then
2899 // copy that interpolated object to the new Objects array so that the
2900 // interpolated position and orientation can be preserved.
2902 for (i = 0; i <= num_objs; i++) {
2903 for (j = 0; j <= Highest_object_index; j++) {
2904 if (cur_objs[i].signature == Objects[j].signature) {
2905 memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix));
2906 memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector));
2912 d_recorded += nd_recorded_time;
2913 base_interpol_time = nd_playback_total - FrameTime;
2917 d_play = nd_playback_total - base_interpol_time;
2918 interpolate_frame(d_play, d_recorded);
2922 //mprintf ((0, "*"));
2923 if (newdemo_read_frame_information() == -1) {
2924 newdemo_stop_playback();
2927 if (playback_style == SKIP_PLAYBACK) {
2928 //mprintf ((0, "."));
2929 while (nd_playback_total > nd_recorded_total) {
2930 if (newdemo_read_frame_information() == -1) {
2931 newdemo_stop_playback();
2940 void newdemo_start_recording()
2942 Newdemo_size = PHYSFSX_getFreeDiskSpace();
2943 con_printf(CON_VERBOSE, "Free space = %d\n", Newdemo_size);
2945 Newdemo_size -= 100000;
2947 if ((Newdemo_size+100000) < 2000000000) {
2948 if (((int)(Newdemo_size)) < 500000) {
2950 nm_messagebox(NULL, 1, TXT_OK, TXT_DEMO_NO_SPACE);
2952 nm_messagebox(NULL, 1, TXT_OK, "Not enough space on current\ndrive to start demo recording.");
2958 Newdemo_num_written = 0;
2960 Newdemo_state = ND_STATE_RECORDING;
2961 outfile = PHYSFS_openWrite(DEMO_FILENAME);
2963 #if !defined(MACINTOSH) && !defined(_WIN32_WCE)
2964 if (outfile == NULL && errno == ENOENT) { //dir doesn't exist?
2966 if (outfile == NULL) { //dir doesn't exist and no errno on mac!
2968 PHYSFS_mkdir(DEMO_DIR); //try making directory
2969 outfile = PHYSFS_openWrite(DEMO_FILENAME);
2972 if (outfile == NULL)
2974 nm_messagebox(NULL, 1, TXT_OK, "Cannot open demo temp file");
2975 Newdemo_state = ND_STATE_NORMAL;
2978 newdemo_record_start_demo();
2982 char demoname_allowed_chars[] = "azAZ09__--";
2983 void newdemo_stop_recording()
2987 static char filename[15] = "", *s;
2988 static sbyte tmpcnt = 0;
2990 char fullname[15+FILENAME_LEN] = DEMO_DIR;
2991 unsigned short byte_count = 0;
2995 nd_write_byte(ND_EVENT_EOF);
2996 nd_write_short(frame_bytes_written - 1);
2997 if (Game_mode & GM_MULTI) {
2998 for (l = 0; l < N_players; l++) {
2999 if (Players[l].flags & PLAYER_FLAGS_CLOAKED)
3000 cloaked |= (1 << l);
3002 nd_write_byte(cloaked);
3003 nd_write_byte(ND_EVENT_EOF);
3005 nd_write_short(ND_EVENT_EOF);
3007 nd_write_short(ND_EVENT_EOF);
3008 nd_write_int(ND_EVENT_EOF);
3010 byte_count += 10; // from frame_bytes_written
3012 nd_write_byte((sbyte)(f2ir(Players[Player_num].energy)));
3013 nd_write_byte((sbyte)(f2ir(Players[Player_num].shields)));
3014 nd_write_int(Players[Player_num].flags); // be sure players flags are set
3015 nd_write_byte((sbyte)Primary_weapon);
3016 nd_write_byte((sbyte)Secondary_weapon);
3019 for (l = 0; l < MAX_PRIMARY_WEAPONS; l++)
3020 nd_write_short((short)Players[Player_num].primary_ammo[l]);
3022 for (l = 0; l < MAX_SECONDARY_WEAPONS; l++)
3023 nd_write_short((short)Players[Player_num].secondary_ammo[l]);
3024 byte_count += (sizeof(short) * (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS));
3026 nd_write_byte(Players[Player_num].laser_level);
3029 if (Game_mode & GM_MULTI) {
3030 nd_write_byte((sbyte)N_players);
3032 for (l = 0; l < N_players; l++) {
3033 nd_write_string(Players[l].callsign);
3034 byte_count += (strlen(Players[l].callsign) + 2);
3035 nd_write_byte(Players[l].connected);
3036 if (Game_mode & GM_MULTI_COOP) {
3037 nd_write_int(Players[l].score);
3040 nd_write_short((short)Players[l].net_killed_total);
3041 nd_write_short((short)Players[l].net_kills_total);
3046 nd_write_int(Players[Player_num].score);
3049 nd_write_short(byte_count);
3051 nd_write_byte(Current_level_num);
3052 nd_write_byte(ND_EVENT_EOF);
3054 l = PHYSFS_tell(outfile);
3055 PHYSFS_close(outfile);
3057 Newdemo_state = ND_STATE_NORMAL;
3058 gr_palette_load( gr_palette );
3060 if (filename[0] != '\0') {
3061 int num, i = strlen(filename) - 1;
3064 while (isdigit(filename[i])) {
3070 num = atoi(&(filename[i]));
3073 sprintf (newfile, "%s%d", filename, num);
3074 strncpy(filename, newfile, 8);
3081 Newmenu_allowed_chars = demoname_allowed_chars;
3082 if (!Newdemo_no_space) {
3083 m[0].type=NM_TYPE_INPUT; m[0].text_len = 8; m[0].text = filename;
3084 exit = newmenu_do( NULL, TXT_SAVE_DEMO_AS, 1, &(m[0]), NULL );
3085 } else if (Newdemo_no_space == 1) {
3086 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_BAD;
3087 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3088 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3089 } else if (Newdemo_no_space == 2) {
3090 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_NOSPACE;
3091 m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename;
3092 exit = newmenu_do( NULL, NULL, 2, m, NULL );
3094 Newmenu_allowed_chars = NULL;
3096 if (exit == -2) { // got bumped out from network menu
3097 char save_file[7+FILENAME_LEN];
3099 if (filename[0] != '\0') {
3100 strcpy(save_file, DEMO_DIR);
3101 strcat(save_file, filename);
3102 strcat(save_file, ".dem");
3104 sprintf (save_file, "%stmp%d.dem", DEMO_DIR, tmpcnt++);
3106 PHYSFSX_rename(DEMO_FILENAME, save_file);
3109 if (exit == -1) { // pressed ESC
3110 PHYSFS_delete(DEMO_FILENAME); // might as well remove the file
3111 return; // return without doing anything
3114 if (filename[0]==0) //null string
3117 //check to make sure name is ok
3118 for (s=filename;*s;s++)
3119 if (!isalnum(*s) && *s!='_') {
3120 nm_messagebox1(NULL, NULL,1,TXT_CONTINUE, TXT_DEMO_USE_LETTERS);
3124 if (Newdemo_no_space)
3125 strcat(fullname, m[1].text);
3127 strcat(fullname, m[0].text);
3128 strcat(fullname, ".dem");
3129 PHYSFS_delete(fullname);
3130 PHYSFSX_rename(DEMO_FILENAME, fullname);
3134 extern char AltHogDir[64];
3135 extern char AltHogdir_initialized;
3137 //returns the number of demo files on the disk
3138 int newdemo_count_demos()
3143 find = PHYSFS_enumerateFiles(DEMO_DIR);
3145 for (i = find; *i != NULL; i++)
3148 PHYSFS_freeList(find);
3153 void newdemo_start_playback(char * filename)
3155 char **find = NULL, **i;
3157 char filename2[PATH_MAX+FILENAME_LEN] = DEMO_DIR;
3160 change_playernum_to(0);
3162 First_time_playback=1;
3163 JasonPlaybackTotal=0;
3166 strcat(filename2, filename);
3169 // Randomly pick a filename
3170 int NumFiles = 0, RandFileNum;
3173 NumFiles = newdemo_count_demos();
3175 if ( NumFiles == 0 ) {
3176 return; // No files found!
3178 RandFileNum = d_rand() % NumFiles;
3181 find = PHYSFS_enumerateFiles(DEMO_DIR);
3183 for (i = find; *i != NULL; i++)
3185 if (NumFiles == RandFileNum)
3187 strcat(filename2, *i);
3193 PHYSFS_freeList(find);
3195 if (NumFiles > RandFileNum)
3199 infile = PHYSFS_openRead(filename2);
3202 mprintf( (0, "Error reading '%s'\n", filename ));
3208 change_playernum_to(0); // force playernum to 0
3210 strncpy(nd_save_callsign, Players[Player_num].callsign, CALLSIGN_LEN);
3211 Viewer = ConsoleObject = &Objects[0]; // play properly as if console player
3212 if (newdemo_read_demo_start(rnd_demo)) {
3213 PHYSFS_close(infile);
3217 Game_mode = GM_NORMAL;
3218 Newdemo_state = ND_STATE_PLAYBACK;
3219 Newdemo_vcr_state = ND_STATE_PLAYBACK;
3220 Newdemo_old_cockpit = Cockpit_mode;
3221 Newdemo_size = PHYSFS_fileLength(infile);
3224 NewdemoFrameCount = 0;
3225 Newdemo_players_cloaked = 0;
3226 playback_style = NORMAL_PLAYBACK;
3227 Function_mode = FMODE_GAME;
3228 Cockpit_3d_view[0] = CV_NONE; //turn off 3d views on cockpit
3229 Cockpit_3d_view[1] = CV_NONE; //turn off 3d views on cockpit
3230 newdemo_playback_one_frame(); // this one loads new level
3231 newdemo_playback_one_frame(); // get all of the objects to renderb game
3234 void newdemo_stop_playback()
3236 PHYSFS_close(infile);
3237 Newdemo_state = ND_STATE_NORMAL;
3239 change_playernum_to(0); //this is reality
3241 strncpy(Players[Player_num].callsign, nd_save_callsign, CALLSIGN_LEN);
3242 Cockpit_mode = Newdemo_old_cockpit;
3243 Game_mode = GM_GAME_OVER;
3244 Function_mode = FMODE_MENU;
3245 longjmp(LeaveGame,0); // Exit game loop
3251 #define BUF_SIZE 16384
3253 void newdemo_strip_frames(char *outname, int bytes_to_strip)
3255 PHYSFS_file *outfile;
3257 int total_size, bytes_done, read_elems, bytes_back;
3258 int trailer_start, loc1, loc2, stop_loc, bytes_to_read;
3259 short last_frame_length;
3262 total_size = PHYSFS_fileLength(infile);
3263 outfile = PHYSFS_openWrite(outname);
3264 if (outfile == NULL) {
3267 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't open output file";
3268 newmenu_do( NULL, NULL, 1, m, NULL );
3269 newdemo_stop_playback();
3272 buf = d_malloc(BUF_SIZE);
3276 m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't malloc output buffer";
3277 newmenu_do( NULL, NULL, 1, m, NULL );
3278 PHYSFS_close(outfile);
3279 newdemo_stop_playback();
3283 trailer_start = PHYSFS_tell(infile);
3284 PHYSFS_seek(infile, PHYSFS_tell(infile) + 11);
3286 while (bytes_back < bytes_to_strip) {
3287 loc1 = PHYSFS_tell(infile);
3288 //PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
3289 //nd_read_short(&last_frame_length);
3290 //PHYSFS_seek(infile, PHYSFS_tell(infile) + 8 - last_frame_length);
3291 newdemo_back_frames(1);
3292 loc2 = PHYSFS_tell(infile);
3293 bytes_back += (loc1 - loc2);
3295 PHYSFS_seek(infile, PHYSFS_tell(infile) - 10);
3296 nd_read_short(&last_frame_length);
3297 PHYSFS_seek(infile, PHYSFS_tell(infile) - 3);
3298 stop_loc = PHYSFS_tell(infile);
3299 PHYSFS_seek(infile, 0);
3300 while (stop_loc > 0) {
3301 if (stop_loc < BUF_SIZE)
3302 bytes_to_read = stop_loc;
3304 bytes_to_read = BUF_SIZE;
3305 read_elems = PHYSFS_read(infile, buf, 1, bytes_to_read);
3306 PHYSFS_write(outfile, buf, 1, read_elems);
3307 stop_loc -= read_elems;
3309 stop_loc = PHYSFS_tell(outfile);
3310 PHYSFS_seek(infile, trailer_start);
3311 while ((read_elems = PHYSFS_read(infile, buf, 1, BUF_SIZE)) != 0)
3312 PHYSFS_write(outfile, buf, 1, read_elems);
3313 PHYSFS_seek(outfile, stop_loc);
3314 PHYSFS_seek(outfile, PHYSFS_tell(infile) + 1);
3315 PHYSFS_write(outfile, &last_frame_length, 2, 1);
3316 PHYSFS_close(outfile);
3317 newdemo_stop_playback();
3323 object DemoRightExtra,DemoLeftExtra;
3324 ubyte DemoDoRight=0,DemoDoLeft=0;
3326 void nd_render_extras (ubyte which,object *obj)
3329 ubyte type=which&15;
3333 Int3(); // how'd we get here?
3334 do_cockpit_window_view(w,NULL,0,WBU_WEAPON,NULL);
3340 memcpy (&DemoRightExtra,obj,sizeof(object)); DemoDoRight=type;
3344 memcpy (&DemoLeftExtra,obj,sizeof(object)); DemoDoLeft=type;
3349 void DoJasonInterpolate (fix recorded_time)
3353 JasonPlaybackTotal+=FrameTime;
3355 if (!First_time_playback)
3357 // get the difference between the recorded time and the playback time
3358 the_delay=(recorded_time - FrameTime);
3359 //mprintf ((0,"The delay=%d\n", f2i(the_delay)));
3360 if (the_delay >= f0_0)
3363 timer_delay(the_delay);
3368 while (JasonPlaybackTotal > nd_recorded_total)
3369 if (newdemo_read_frame_information() == -1)
3371 newdemo_stop_playback();
3375 //the_delay = nd_recorded_total - JasonPlaybackTotal;
3376 //if (the_delay > f0_0)
3377 // timer_delay(the_delay);
3382 First_time_playback=0;
3386 #pragma global_optimizer reset