/* $Id: newdemo.c,v 1.15 2003-11-26 12:26:31 btb Exp $ */ /* THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX SOFTWARE CORPORATION ("PARALLAX"). PARALLAX, IN DISTRIBUTING THE CODE TO END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS IN USING, DISPLAYING, AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE FREE PURPOSES. IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES. THE END-USER UNDERSTANDS AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE. COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION. ALL RIGHTS RESERVED. */ /* * * Code to make a complete demo playback system. * * Old Log: * Revision 1.12 1995/10/31 10:19:43 allender * shareware stuff * * Revision 1.11 1995/10/17 13:19:16 allender * close boxes for demo save * * Revision 1.10 1995/10/05 10:36:07 allender * fixed calls to digi_play_sample to account for differing volume and angle calculations * * Revision 1.9 1995/09/12 15:49:13 allender * define __useAppleExts__ if not already defined * * Revision 1.8 1995/09/05 14:28:32 allender * fixed previous N_players bug in newdemo_goto_end * * Revision 1.7 1995/09/05 14:16:51 allender * added space to allowable demo filename characters * and fixed bug with netgame demos N_players got getting * set correctly * * Revision 1.6 1995/09/01 16:10:47 allender * fixed bug with reading in N_players variable on * netgame demo playback * * Revision 1.5 1995/08/24 16:04:11 allender * fix signed byte problem * * Revision 1.4 1995/08/12 12:21:59 allender * made call to create_shortpos not swap the shortpos * elements * * Revision 1.3 1995/08/01 16:04:19 allender * made random picking of demo work * * Revision 1.2 1995/08/01 13:56:56 allender * demo system working on the mac * * Revision 1.1 1995/05/16 15:28:59 allender * Initial revision * * Revision 2.7 1995/05/26 16:16:06 john * Split SATURN into define's for requiring cd, using cd, etc. * Also started adding all the Rockwell stuff. * * Revision 2.6 1995/03/21 14:39:38 john * Ifdef'd out the NETWORK code. * * Revision 2.5 1995/03/14 18:24:31 john * Force Destination Saturn to use CD-ROM drive. * * Revision 2.4 1995/03/14 16:22:29 john * Added cdrom alternate directory stuff. * * Revision 2.3 1995/03/10 12:58:33 allender * only display rear view cockpit when cockpit mode was CM_FULL_COCKPIT. * * Revision 2.2 1995/03/08 16:12:15 allender * changes for Destination Saturn * * Revision 2.1 1995/03/08 12:11:26 allender * fix shortpos reading * * Revision 2.0 1995/02/27 11:29:40 john * New version 2.0, which has no anonymous unions, builds with * Watcom 10.0, and doesn't require parsing BITMAPS.TBL. * * Revision 1.189 1995/02/22 14:53:42 allender * missed some anonymous stuff * * Revision 1.188 1995/02/22 13:24:53 john * Removed the vecmat anonymous unions. * * Revision 1.187 1995/02/22 13:13:54 allender * remove anonymous unions from object structure * * Revision 1.186 1995/02/14 15:36:41 allender * fix fix for morph effect * * Revision 1.185 1995/02/14 11:25:48 allender * save cockpit mode and restore after playback. get orientation for morph * effect when object is morph vclip * * Revision 1.184 1995/02/13 12:18:14 allender * change to decide about interpolating or not * * Revision 1.183 1995/02/12 00:46:23 adam * don't decide to skip frames until after at least 10 frames have * passed -- allender * * Revision 1.182 1995/02/11 22:34:01 john * Made textures page in for newdemos before playback time. * * Revision 1.181 1995/02/11 17:28:32 allender * strip frames from end of demo * * Revision 1.180 1995/02/11 16:40:35 allender * start of frame stripping debug code * * Revision 1.179 1995/02/10 17:40:06 allender * put back in wall_hit_process code to fix door problem * * Revision 1.178 1995/02/10 17:17:24 adam * allender } fix * * Revision 1.177 1995/02/10 17:16:24 allender * fix possible tmap problems * * Revision 1.176 1995/02/10 15:54:37 allender * changes for out of space on device. * * Revision 1.175 1995/02/09 19:55:00 allender * fix bug with morph recording -- force rendertype to RT_POLYOBJ when * playing back since it won't render until fully morphed otherwise * * Revision 1.174 1995/02/07 17:15:35 allender * DOH!!!!! * * Revision 1.173 1995/02/07 17:14:21 allender * immediately return when loading bogus level stuff when reading a frame * * Revision 1.172 1995/02/02 11:15:03 allender * after loading new level, read next frame (forward or back) always because * of co-op ships showing up when level is loaded * * Revision 1.171 1995/02/02 10:24:16 allender * removed cfile stuff. Use standard FILE functions for demo playback * * Revision 1.170 1995/01/30 13:54:32 allender * support for missions * * Revision 1.169 1995/01/27 16:27:35 allender * put game mode to demo_game_mode when sorting multiplayer kill (and score) * list * * Revision 1.168 1995/01/27 09:52:25 allender * minor changes because of object/segment linking problems * * Revision 1.167 1995/01/27 09:22:28 allender * changed way multi-player score is recorded. Record difference, not * actual * * Revision 1.166 1995/01/25 14:32:44 allender * changed with recorded player flags. More checks for paused state * during interpolation reading of objects * * Revision 1.165 1995/01/25 11:23:32 allender * found bug with out of disk space problem * * Revision 1.164 1995/01/25 11:11:33 allender * coupla' things. Fix problem with objects apparently being linked in * the wrong segment. Put an Int3 in to check why demos will write to * end of space on drive. close demo file if demo doens't start playing * back. Put obj->type == OBJ_ROBOT around checking for boss cloaking * * Revision 1.163 1995/01/24 19:44:30 allender * fix obscure problem with rewinding and having the wrong object linked * to the wrong segments. will investigate further. * * Revision 1.162 1995/01/23 09:31:28 allender * add team score in team mode playback * * Revision 1.161 1995/01/20 22:47:39 matt * Mission system implemented, though imcompletely * * Revision 1.160 1995/01/20 09:30:37 allender * don't call LoadLevel with bogus data * * Revision 1.159 1995/01/20 09:13:23 allender * *&^%&*%$ typo * * Revision 1.158 1995/01/20 09:12:04 allender * record team names during demo recoring in GM_TEAM * * Revision 1.157 1995/01/19 16:31:09 allender * forgot to bump demo version for new weapon change stuff * * Revision 1.156 1995/01/19 16:29:33 allender * added new byte for weapon change (old weapon) so rewinding works * correctly for weapon changes in registered * * Revision 1.155 1995/01/19 15:00:05 allender * remove code to take away blastable walls in multiplayer demo playbacks * * Revision 1.154 1995/01/19 11:07:05 allender * put in psuedo cloaking for boss robots. Problem is that cloaking is * time based, and that don't get done in demos, so bosses just disappear. * oh well * * Revision 1.153 1995/01/19 09:42:29 allender * record laser levels in demos * * Revision 1.152 1995/01/18 20:43:12 allender * fix laser level stuff on goto-beginning and goto-end * * Revision 1.151 1995/01/18 20:28:18 allender * cloak robots now cloak (except maybe for boss........) Put in function * to deal with control center triggers * * Revision 1.150 1995/01/18 18:55:07 allender * bug fix * * Revision 1.149 1995/01/18 18:49:03 allender * lots 'o stuff....record laser level. Record beginning of door opening * sequence. Fix some problems with control center stuff. Control center * triggers now work in reverse * * Revision 1.148 1995/01/18 08:51:40 allender * forgot to record ammo counts at beginning of demo * * Revision 1.147 1995/01/17 17:42:07 allender * added primary and secondary ammo counts. Changed goto_end routine * to be more efficient * * Revision 1.146 1995/01/17 13:46:35 allender * fix problem with destroyed control center and rewinding a demo. * Save callsign and restore after demo playback * * Revision 1.145 1995/01/12 10:21:53 allender * fixes for 1.0 to 1.1 demo incompatibility * * Revision 1.144 1995/01/05 13:51:43 allender * fixed type of player num variable * * Revision 1.143 1995/01/04 16:58:28 allender * bumped up demo version number * * Revision 1.142 1995/01/04 14:59:02 allender * added more information to end of demo for registered. Forced game mode * to be GM_NORMAL on demo playback * * Revision 1.141 1995/01/03 17:30:47 allender * fixed logic problem with cloak stuf * * Revision 1.140 1995/01/03 17:12:23 allender * fix for getting cloak stuff at end of demo for shareware * * Revision 1.139 1995/01/03 15:20:24 allender * fix goto_end for shareware -- changes to goto_end for registered * * Revision 1.138 1995/01/03 13:13:26 allender * add } I forgot * * Revision 1.137 1995/01/03 13:10:29 allender * make score work forwards and backwards * * Revision 1.136 1995/01/03 11:45:20 allender * added code to record players scores * * Revision 1.135 1994/12/30 10:03:57 allender * put cloak stuff at end of demo for fast forward to the end * * Revision 1.134 1994/12/29 17:02:55 allender * spelling fix on SHAREWARE * * Revision 1.133 1994/12/29 16:43:41 allender * lots of new multiplayer stuff. wrapped much code with SHAREWARE defines * * Revision 1.132 1994/12/28 14:15:01 allender * added routines to deal with connecting and reconnecting players when * recording multiplayer demos * * Revision 1.131 1994/12/21 12:57:59 allender * bug fix * * Revision 1.130 1994/12/21 12:46:53 allender * record multi player deaths and kills * * Revision 1.129 1994/12/19 16:37:27 allender * pick good filename when trying to save in network play and player * gets bumped out of menu by multi-player code * * Revision 1.128 1994/12/14 10:49:01 allender * reset bad_read variable when starting demo playback * * Revision 1.127 1994/12/14 08:53:06 allender * lowered watermark for out of space * * Revision 1.126 1994/12/14 08:49:52 allender * put up warning when starting demo recording if not enough space and * not let them record * * Revision 1.125 1994/12/13 00:01:37 allender * CLOAK FIX -- (I'm tempted to take cloak out of game because I can't * seem to get it right in demo playback) * * Revision 1.124 1994/12/12 14:51:21 allender * more fixed to multiplayer cloak stuff * * Revision 1.123 1994/12/12 11:33:11 allender * fixed rearview mode to work again * * Revision 1.122 1994/12/12 11:00:16 matt * Added code to handle confusion with attached objects * * Revision 1.121 1994/12/12 00:31:29 allender * give better warning when out of space when recording. Don't record * until no space left. We have 500K watermark when we now stop recording * * Revision 1.120 1994/12/10 16:44:54 matt * Added debugging code to track down door that turns into rock * * Revision 1.119 1994/12/09 18:46:15 matt * Added code to handle odd error condition * * Revision 1.118 1994/12/09 17:27:37 allender * force playernum to 0 when demo is done playing * * Revision 1.117 1994/12/09 16:40:39 allender * yet more cloak stuff. Assign cloak/invuln time when starting demo * if flags are set. Check cloak and invuln time when demo * even when paused * * Revision 1.116 1994/12/09 14:59:22 matt * Added system to attach a fireball to another object for rendering purposes, * so the fireball always renders on top of (after) the object. * * Revision 1.115 1994/12/09 12:21:45 allender * only allow valid chars when typing in demo filename * * Revision 1.114 1994/12/08 23:19:02 allender * final(?) fix for getting cloak gauge to work on demo playback * with forward and reverse * * Revision 1.113 1994/12/08 21:34:38 allender * record old and new player flags to accuratedly record cloaking and * decloaking * ./ * * Revision 1.112 1994/12/08 18:04:47 allender * bashed playernum right after reading it in demo header so shields * and energy are put in right place * * Revision 1.111 1994/12/08 17:10:07 allender * encode playernum in demo header. Bash viewer segment to 0 if in * bogus segnum. Don't link render objs for same reason * * Revision 1.110 1994/12/08 15:36:12 allender * cloak stuff works forwards and backwards * * Revision 1.109 1994/12/08 13:46:03 allender * don't record rearview anymore, but leave in case statement for playback * purposes. change the way letterbox <--> cockpit transitions happen * * Revision 1.108 1994/12/08 12:36:06 matt * Added new object allocation & deallocation functions so other code * could stop messing around with internal object data structures. * * Revision 1.107 1994/12/08 11:19:04 allender * handle out of space (more) gracefully then before * * Revision 1.106 1994/12/08 00:29:49 allender * fixed bug that didn't load level on goto_beginning * * Revision 1.105 1994/12/08 00:11:51 mike * change matrix interpolation. * * Revision 1.104 1994/12/07 23:46:37 allender * changed invulnerability and cloak to work (almost) correctly both * in single and multi player * * Revision 1.103 1994/12/07 11:48:49 adam * BY ALLENDER -- added dampening of interpolation factor to 1 if greater * than 1 (although I have not seen this happen). this is attempt to * get wobbling problem solved * * Revision 1.102 1994/12/07 11:23:56 allender * attempt at getting rid of wobbling on demo playback * * Revision 1.101 1994/12/06 19:31:17 allender * moved blastable wall stuff code to where we load level during demo * playback * * Revision 1.100 1994/12/06 19:21:51 allender * multi games, destroy blastable walls. Do wall toggle when control center * destroyed * * Revision 1.99 1994/12/06 16:54:48 allender * fixed code so if demo automatically started from menu, don't bring up * message if demo is too old * * Revision 1.98 1994/12/06 13:55:15 matt * Use new rounding func, f2ir() * * Revision 1.97 1994/12/06 13:44:45 allender * suppressed compiler warnings * * Revision 1.96 1994/12/06 13:38:03 allender * removed recording of wall hit process. I think that all bases are covered * elsewhere * * Revision 1.95 1994/12/06 12:57:35 allender * added recording of multi_decloaking. Fixed some other cloaking code so * that cloak should last as long as player was cloaked. We will lose the * guage effect, but the time is probably more important on playback * * Revision 1.94 1994/12/05 23:37:17 matt * Took out calls to warning() function * * Revision 1.93 1994/12/03 17:52:04 yuan * Localization 380ish * * Revision 1.92 1994/12/02 12:53:39 allender * fixed goto_beginning and goto_end on demo playback * * Revision 1.91 1994/12/01 12:01:49 allender * added multi player cloak stuff * * Revision 1.90 1994/11/30 09:33:58 allender * added field in header to tell what version (shareware or registered) * demo was recorded with. Don't allow demo recorded on one to playback * on the other * * Revision 1.89 1994/11/29 00:31:01 allender * major changes -- added level recording feature which records level * advancement. Changes to internal code to handle this. * * Revision 1.88 1994/11/27 23:13:54 matt * Made changes for new mprintf calling convention * * Revision 1.87 1994/11/27 23:07:35 allender * starting on code to get all level transitions recorded. not done yet * * Revision 1.86 1994/11/27 17:39:47 matt * Don't xlate tmap numbers when editor compiled out * * Revision 1.85 1994/11/23 09:27:21 allender * put up info box with message if demo version is too old or level * cannot be loaded * * Revision 1.84 1994/11/22 19:37:39 allender * fix array mistake * * Revision 1.83 1994/11/22 19:35:09 allender * record player ship colors in multiplayer demo recordings * * Revision 1.82 1994/11/19 15:36:42 mike * fix fix. * * Revision 1.81 1994/11/19 15:23:21 mike * rip out unused code * * Revision 1.80 1994/11/16 14:51:49 rob * Fixed network/demo incompatibility. * * Revision 1.79 1994/11/15 10:55:48 allender * made start of demo playback read initial demo information so * level will get loaded. Made demo record to single file which * will get renamed. Added numerics after old filename so * sequential filenames would be defaulted to * * Revision 1.78 1994/11/15 09:46:06 allender * added versioning. Fixed problems with trying to interpolating a completely * 0 orientation matrix * * Revision 1.77 1994/11/14 14:34:31 matt * Fixed up handling when textures can't be found during remap * * Revision 1.76 1994/11/14 09:15:29 allender * make ESC from file save menu exit w/o saving. Fix letterbox, rear view, * to normal cockpit mode transition to work correctly when skipping and * interpolating frames * * Revision 1.75 1994/11/11 16:22:07 allender * made morphing objects record only the object being morphed. * * Revision 1.74 1994/11/08 14:59:19 john * Added code to respond to network while in menus. * * Revision 1.73 1994/11/08 14:52:20 adam * *** empty log message *** * * Revision 1.72 1994/11/07 15:47:04 allender * prompt for filename when done recording demo * * Revision 1.71 1994/11/07 11:47:19 allender * when interpolating frames, delete weapon, fireball, and debris objects * from an inpolated frame if they don't appear in the next recorded * frame * * Revision 1.70 1994/11/07 11:02:41 allender * more with interpolation. I believe that I have it right now * * Revision 1.69 1994/11/07 08:47:40 john * Made wall state record. * * Revision 1.68 1994/11/05 17:22:51 john * Fixed lots of sequencing problems with newdemo stuff. * * Revision 1.67 1994/11/04 20:11:52 john * Neatening up palette stuff with demos. * * Revision 1.66 1994/11/04 16:49:44 allender * changed newdemo_do_interpolate to default to on * * Revision 1.65 1994/11/04 16:44:51 allender * added filename support for demo recording. more auto demo stuff * * Revision 1.64 1994/11/04 13:05:31 allender * fixing the lifeleft variable again. (I think I got it right this time) * * Revision 1.63 1994/11/04 11:37:37 allender * commented out fprintfs and fixed compiler warning * * Revision 1.62 1994/11/04 11:33:50 allender * added OBJ_FLARE and OBJ_LIGHT to obj->lifeleft recording * * Revision 1.61 1994/11/04 11:29:21 allender * more interpolation stuff -- not done yet. Fixed so hostage vclips * render correctly. Changed lifeleft to full precision, but only * write it when object is fireball or weapon type of object * * Revision 1.60 1994/11/03 10:00:11 allender * fixed divide by zero in calculating render time. more interpolation * stuff which isn't quite done * * Revision 1.59 1994/11/02 17:10:59 allender * never play recorded frames when interpolation is occuring * * Revision 1.58 1994/11/02 14:28:58 allender * profile total playback time and average frame render time * * Revision 1.57 1994/11/02 14:09:03 allender * record rear view. start of playback interpolation code -- this * is not yet done * * Revision 1.56 1994/11/01 13:25:30 allender * drop frames if playing back demo on slower machine * * Revision 1.55 1994/10/31 16:10:40 allender * record letterbox mode on death seq, and then restore * * Revision 1.54 1994/10/29 16:01:38 allender * added ND_STATE_NODEMOS to indicate that there are no demos currently * available for playback * * Revision 1.53 1994/10/29 15:38:42 allender * in newdemo_start_playback, make Newdemo_at_eof = 0 * * Revision 1.52 1994/10/28 14:45:28 john * fixed typo from last checkin. * * Revision 1.51 1994/10/28 14:42:55 john * Added sound volumes to all sound calls. * * Revision 1.50 1994/10/28 14:31:57 allender * homing missle and autodemo stuff * * Revision 1.49 1994/10/28 12:42:14 allender * record homing distance * * Revision 1.48 1994/10/27 16:57:54 allender * changed demo vcr to be able to play any number of frames by storing * frame length (in bytes) in the demo file. Added blowing up monitors * * Revision 1.47 1994/10/26 16:50:50 allender * put two functions inside of VCR_MODE ifdef * * Revision 1.46 1994/10/26 15:20:32 allender * added CT_REMOTE as valid control type for recording * * Revision 1.45 1994/10/26 14:45:35 allender * completed hacked in vcr demo playback stuff * * Revision 1.44 1994/10/26 13:40:52 allender * vcr playback of demo stuff * * Revision 1.43 1994/10/26 08:51:57 allender * record player weapon change * * Revision 1.42 1994/10/25 15:48:01 allender * add shields, energy, and player flags to demo recording. * , * * Revision 1.41 1994/10/24 08:19:35 allender * fixed compilation errors * * Revision 1.40 1994/10/23 19:17:08 matt * Fixed bug with "no key" messages * * Revision 1.39 1994/10/22 14:15:08 mike * Suppress compiler warnings. * * Revision 1.38 1994/10/21 15:24:55 allender * compressed writing of object structures with specialized code * to write out only pertinent object structures. * * Revision 1.37 1994/10/20 13:03:17 matt * Replaced old save files (MIN/SAV/HOT) with new LVL files * * Revision 1.36 1994/09/28 23:13:10 matt * Macroized palette flash system * * Revision 1.35 1994/09/26 17:28:32 matt * Made new multiple-object morph code work with the demo system * * Revision 1.34 1994/09/10 13:31:54 matt * Made exploding walls a type of blastable walls. * Cleaned up blastable walls, making them tmap2 bitmaps. * * Revision 1.33 1994/08/15 18:05:28 john * *** empty log message *** * * Revision 1.32 1994/08/15 17:56:38 john * , * * Revision 1.31 1994/08/10 09:44:54 john * *** empty log message *** * * Revision 1.30 1994/07/22 12:35:48 matt * Cleaned up editor/game interactions some more. * * Revision 1.29 1994/07/21 13:06:45 matt * Ripped out remants of old demo system, and added demo only system that * disables object movement and game options from menu. * * Revision 1.28 1994/07/18 16:22:44 john * Made all file read/writes call the same routine. * * Revision 1.27 1994/07/14 22:38:27 matt * Added exploding doors * * Revision 1.26 1994/07/05 12:49:04 john * Put functionality of New Hostage spec into code. * * Revision 1.25 1994/06/29 11:05:38 john * Made demos read in compressed. * * Revision 1.24 1994/06/29 09:14:06 john * Made files write out uncompressed and read in compressed. * * Revision 1.23 1994/06/28 11:55:28 john * Made newdemo system record/play directly to/from disk, so * we don't need the 4 MB buffer anymore. * * Revision 1.22 1994/06/27 15:52:38 john * #define'd out the newdemo stuff * * * Revision 1.21 1994/06/22 00:29:04 john * Fixed bug with playing demo then playing game without * loading new mine. * * Revision 1.20 1994/06/22 00:14:23 john * Attempted to fix sign. * * Revision 1.19 1994/06/21 23:57:54 john * Hopefully fixed bug with negative countdowns. * * Revision 1.18 1994/06/21 23:47:44 john * MAde Malloc always 4*1024*1024. * * Revision 1.17 1994/06/21 22:58:47 john * Added error if out of memory. * * Revision 1.16 1994/06/21 22:15:48 john * Added % done to demo recording. * * * Revision 1.15 1994/06/21 19:45:55 john * Added palette effects to demo recording. * * Revision 1.14 1994/06/21 15:08:54 john * Made demo record HUD message and cleaned up the HUD code. * * Revision 1.13 1994/06/21 14:20:08 john * Put in hooks to record HUD messages. * * Revision 1.12 1994/06/20 11:50:15 john * Made demo record flash effect, and control center triggers. * * Revision 1.11 1994/06/17 18:01:33 john * A bunch of new stuff by John * * Revision 1.10 1994/06/17 12:13:31 john * More newdemo stuff; made editor->game transition start in slew mode. * * Revision 1.9 1994/06/16 13:14:36 matt * Fixed typo * * Revision 1.8 1994/06/16 13:02:07 john * Added morph hooks. * * Revision 1.7 1994/06/15 19:01:33 john * Added the capability to make 3d sounds play just once for the * laser hit wall effects. * * Revision 1.6 1994/06/15 14:56:59 john * Added triggers to demo recording. * * Revision 1.5 1994/06/14 20:42:15 john * Made robot matztn cntr not work until no robots or player are * in the segment. * * Revision 1.4 1994/06/14 14:43:27 john * Made doors work with newdemo system. * * Revision 1.3 1994/06/14 11:32:29 john * Made Newdemo record & restore the current mine. * * Revision 1.2 1994/06/13 21:02:43 john * Initial version of new demo recording system. * * Revision 1.1 1994/06/13 11:09:00 john * Initial revision * * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include // for memset #ifndef _WIN32_WCE #include #endif #include /* for isdigit */ #include #ifdef __unix__ #include #include #endif #include "u_mem.h" #include "inferno.h" #include "game.h" #include "gr.h" #include "stdlib.h" #include "bm.h" //#include "error.h" #include "mono.h" #include "3d.h" #include "segment.h" #include "texmap.h" #include "laser.h" #include "key.h" #include "gameseg.h" #include "object.h" #include "physics.h" #include "slew.h" #include "render.h" #include "wall.h" #include "vclip.h" #include "polyobj.h" #include "fireball.h" #include "laser.h" #include "error.h" #include "ai.h" #include "hostage.h" #include "morph.h" #include "powerup.h" #include "fuelcen.h" #include "sounds.h" #include "collide.h" #include "lighting.h" #include "newdemo.h" #include "gameseq.h" #include "gamesave.h" #include "gamemine.h" #include "switch.h" #include "gauges.h" #include "player.h" #include "vecmat.h" #include "newmenu.h" #include "args.h" #include "palette.h" #include "multi.h" #ifdef NETWORK #include "network.h" #endif #include "text.h" #include "cntrlcen.h" #include "aistruct.h" #include "mission.h" #include "piggy.h" #include "controls.h" #include "d_io.h" #include "timer.h" #include "findfile.h" #ifdef EDITOR #include "editor/editor.h" #endif #ifdef MACINTOSH #pragma global_optimizer off // pretty much sucks...need to look into this #endif void DoJasonInterpolate (fix recorded_time); //#include "nocfile.h" //Does demo start automatically? int Auto_demo = 0; sbyte WasRecorded [MAX_OBJECTS]; sbyte ViewWasRecorded[MAX_OBJECTS]; sbyte RenderingWasRecorded[32]; #define ND_EVENT_EOF 0 // EOF #define ND_EVENT_START_DEMO 1 // Followed by 16 character, NULL terminated filename of .SAV file to use #define ND_EVENT_START_FRAME 2 // Followed by integer frame number, then a fix FrameTime #define ND_EVENT_VIEWER_OBJECT 3 // Followed by an object structure #define ND_EVENT_RENDER_OBJECT 4 // Followed by an object structure #define ND_EVENT_SOUND 5 // Followed by int soundum #define ND_EVENT_SOUND_ONCE 6 // Followed by int soundum #define ND_EVENT_SOUND_3D 7 // Followed by int soundum, int angle, int volume #define ND_EVENT_WALL_HIT_PROCESS 8 // Followed by int segnum, int side, fix damage #define ND_EVENT_TRIGGER 9 // Followed by int segnum, int side, int objnum #define ND_EVENT_HOSTAGE_RESCUED 10 // Followed by int hostage_type #define ND_EVENT_SOUND_3D_ONCE 11 // Followed by int soundum, int angle, int volume #define ND_EVENT_MORPH_FRAME 12 // Followed by ? data #define ND_EVENT_WALL_TOGGLE 13 // Followed by int seg, int side #define ND_EVENT_HUD_MESSAGE 14 // Followed by char size, char * string (+null) #define ND_EVENT_CONTROL_CENTER_DESTROYED 15 // Just a simple flag #define ND_EVENT_PALETTE_EFFECT 16 // Followed by short r,g,b #define ND_EVENT_PLAYER_ENERGY 17 // followed by byte energy #define ND_EVENT_PLAYER_SHIELD 18 // followed by byte shields #define ND_EVENT_PLAYER_FLAGS 19 // followed by player flags #define ND_EVENT_PLAYER_WEAPON 20 // followed by weapon type and weapon number #define ND_EVENT_EFFECT_BLOWUP 21 // followed by segment, side, and pnt #define ND_EVENT_HOMING_DISTANCE 22 // followed by homing distance #define ND_EVENT_LETTERBOX 23 // letterbox mode for death seq. #define ND_EVENT_RESTORE_COCKPIT 24 // restore cockpit after death #define ND_EVENT_REARVIEW 25 // going to rear view mode #define ND_EVENT_WALL_SET_TMAP_NUM1 26 // Wall changed #define ND_EVENT_WALL_SET_TMAP_NUM2 27 // Wall changed #define ND_EVENT_NEW_LEVEL 28 // followed by level number #define ND_EVENT_MULTI_CLOAK 29 // followed by player num #define ND_EVENT_MULTI_DECLOAK 30 // followed by player num #define ND_EVENT_RESTORE_REARVIEW 31 // restore cockpit after rearview mode #define ND_EVENT_MULTI_DEATH 32 // with player number #define ND_EVENT_MULTI_KILL 33 // with player number #define ND_EVENT_MULTI_CONNECT 34 // with player number #define ND_EVENT_MULTI_RECONNECT 35 // with player number #define ND_EVENT_MULTI_DISCONNECT 36 // with player number #define ND_EVENT_MULTI_SCORE 37 // playernum / score #define ND_EVENT_PLAYER_SCORE 38 // followed by score #define ND_EVENT_PRIMARY_AMMO 39 // with old/new ammo count #define ND_EVENT_SECONDARY_AMMO 40 // with old/new ammo count #define ND_EVENT_DOOR_OPENING 41 // with segment/side #define ND_EVENT_LASER_LEVEL 42 // no data #define ND_EVENT_PLAYER_AFTERBURNER 43 // followed by byte old ab, current ab #define ND_EVENT_CLOAKING_WALL 44 // info changing while wall cloaking #define ND_EVENT_CHANGE_COCKPIT 45 // change the cockpit #define ND_EVENT_START_GUIDED 46 // switch to guided view #define ND_EVENT_END_GUIDED 47 // stop guided view/return to ship #define ND_EVENT_SECRET_THINGY 48 // 0/1 = secret exit functional/non-functional #define ND_EVENT_LINK_SOUND_TO_OBJ 49 // record digi_link_sound_to_object3 #define ND_EVENT_KILL_SOUND_TO_OBJ 50 // record digi_kill_sound_linked_to_object #define NORMAL_PLAYBACK 0 #define SKIP_PLAYBACK 1 #define INTERPOLATE_PLAYBACK 2 #define INTERPOL_FACTOR (F1_0 + (F1_0/5)) #define DEMO_VERSION 15 // last D1 version was 13 #define DEMO_GAME_TYPE 3 // 1 was shareware, 2 registered #define DEMO_FILENAME DEMO_DIR "tmpdemo.dem" #define DEMO_MAX_LEVELS 29 char nd_save_callsign[CALLSIGN_LEN+1]; int Newdemo_state = 0; int Newdemo_vcr_state = 0; int Newdemo_start_frame = -1; unsigned int Newdemo_size; int Newdemo_num_written; int Newdemo_game_mode; int Newdemo_old_cockpit; sbyte Newdemo_no_space; sbyte Newdemo_at_eof; sbyte Newdemo_do_interpolate = 0; // 1 sbyte Newdemo_players_cloaked; sbyte Newdemo_warning_given = 0; sbyte Newdemo_cntrlcen_destroyed = 0; static sbyte nd_bad_read; int NewdemoFrameCount; short frame_bytes_written = 0; fix nd_playback_total; fix nd_recorded_total; fix nd_recorded_time; sbyte playback_style; sbyte First_time_playback=1; fix JasonPlaybackTotal=0; CFILE *infile; CFILE *outfile = NULL; int newdemo_get_percent_done() { if ( Newdemo_state == ND_STATE_PLAYBACK ) { return (cftell(infile) * 100) / Newdemo_size; } if ( Newdemo_state == ND_STATE_RECORDING ) { return cftell(outfile); } return 0; } #define VEL_PRECISION 12 void my_extract_shortpos(object *objp, shortpos *spp) { int segnum; sbyte *sp; sp = spp->bytemat; objp->orient.rvec.x = *sp++ << MATRIX_PRECISION; objp->orient.uvec.x = *sp++ << MATRIX_PRECISION; objp->orient.fvec.x = *sp++ << MATRIX_PRECISION; objp->orient.rvec.y = *sp++ << MATRIX_PRECISION; objp->orient.uvec.y = *sp++ << MATRIX_PRECISION; objp->orient.fvec.y = *sp++ << MATRIX_PRECISION; objp->orient.rvec.z = *sp++ << MATRIX_PRECISION; objp->orient.uvec.z = *sp++ << MATRIX_PRECISION; objp->orient.fvec.z = *sp++ << MATRIX_PRECISION; segnum = spp->segment; objp->segnum = segnum; objp->pos.x = (spp->xo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].x; objp->pos.y = (spp->yo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].y; objp->pos.z = (spp->zo << RELPOS_PRECISION) + Vertices[Segments[segnum].verts[0]].z; objp->mtype.phys_info.velocity.x = (spp->velx << VEL_PRECISION); objp->mtype.phys_info.velocity.y = (spp->vely << VEL_PRECISION); objp->mtype.phys_info.velocity.z = (spp->velz << VEL_PRECISION); } int newdemo_read( void *buffer, int elsize, int nelem ) { int num_read; num_read = cfread(buffer, elsize, nelem, infile); if (cferror(infile) || cfeof(infile)) nd_bad_read = -1; return num_read; } int newdemo_find_object( int signature ) { int i; object * objp; objp = Objects; for (i=0; i<=Highest_object_index; i++, objp++ ) { if ( (objp->type != OBJ_NONE) && (objp->signature == signature)) return i; } return -1; } int newdemo_write( void *buffer, int elsize, int nelem ) { int num_written, total_size; total_size = elsize * nelem; frame_bytes_written += total_size; Newdemo_num_written += total_size; Assert(outfile != NULL); num_written = cfwrite(buffer, elsize, nelem, outfile); //if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) { // Newdemo_no_space=1; // newdemo_stop_recording(); // return -1; //} if ((Newdemo_num_written > Newdemo_size) && !Newdemo_no_space) Newdemo_no_space=1; if (num_written == nelem && !Newdemo_no_space) return num_written; Newdemo_no_space=2; newdemo_stop_recording(); return -1; } /* * The next bunch of files taken from Matt's gamesave.c. We have to modify * these since the demo must save more information about objects that * just a gamesave */ static void nd_write_byte(sbyte b) { newdemo_write(&b, 1, 1); } static void nd_write_short(short s) { newdemo_write(&s, 2, 1); } static void nd_write_int(int i) { newdemo_write(&i, 4, 1); } static void nd_write_string(char *str) { nd_write_byte(strlen(str) + 1); newdemo_write(str, strlen(str) + 1, 1); } static void nd_write_fix(fix f) { newdemo_write(&f, sizeof(fix), 1); } static void nd_write_fixang(fixang f) { newdemo_write(&f, sizeof(fixang), 1); } static void nd_write_vector(vms_vector *v) { nd_write_fix(v->x); nd_write_fix(v->y); nd_write_fix(v->z); } static void nd_write_angvec(vms_angvec *v) { nd_write_fixang(v->p); nd_write_fixang(v->b); nd_write_fixang(v->h); } void nd_write_shortpos(object *obj) { int i; shortpos sp; ubyte render_type; create_shortpos(&sp, obj, 0); render_type = obj->render_type; if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) { for (i = 0; i < 9; i++) nd_write_byte(sp.bytemat[i]); for (i = 0; i < 9; i++) { if (sp.bytemat[i] != 0) break; } if (i == 9) { Int3(); // contact Allender about this. } } nd_write_short(sp.xo); nd_write_short(sp.yo); nd_write_short(sp.zo); nd_write_short(sp.segment); nd_write_short(sp.velx); nd_write_short(sp.vely); nd_write_short(sp.velz); } static void nd_read_byte(sbyte *b) { newdemo_read(b, 1, 1); } static void nd_read_short(short *s) { newdemo_read(s, 2, 1); } static void nd_read_int(int *i) { newdemo_read(i, 4, 1); } static void nd_read_string(char *str) { sbyte len; nd_read_byte(&len); newdemo_read(str, len, 1); } static void nd_read_fix(fix *f) { newdemo_read(f, sizeof(fix), 1); } static void nd_read_fixang(fixang *f) { newdemo_read(f, sizeof(fixang), 1); } static void nd_read_vector(vms_vector *v) { nd_read_fix(&(v->x)); nd_read_fix(&(v->y)); nd_read_fix(&(v->z)); } static void nd_read_angvec(vms_angvec *v) { nd_read_fixang(&(v->p)); nd_read_fixang(&(v->b)); nd_read_fixang(&(v->h)); } static void nd_read_shortpos(object *obj) { shortpos sp; int i; ubyte render_type; render_type = obj->render_type; if (((render_type == RT_POLYOBJ) || (render_type == RT_HOSTAGE) || (render_type == RT_MORPH)) || (obj->type == OBJ_CAMERA)) { for (i = 0; i < 9; i++) nd_read_byte(&(sp.bytemat[i])); } nd_read_short(&(sp.xo)); nd_read_short(&(sp.yo)); nd_read_short(&(sp.zo)); nd_read_short(&(sp.segment)); nd_read_short(&(sp.velx)); nd_read_short(&(sp.vely)); nd_read_short(&(sp.velz)); my_extract_shortpos(obj, &sp); if ((obj->id == VCLIP_MORPHING_ROBOT) && (render_type == RT_FIREBALL) && (obj->control_type == CT_EXPLOSION)) extract_orient_from_segment(&obj->orient,&Segments[obj->segnum]); } object *prev_obj=NULL; //ptr to last object read in void nd_read_object(object *obj) { memset(obj, 0, sizeof(object)); /* * Do render type first, since with render_type == RT_NONE, we * blow by all other object information */ nd_read_byte(&(obj->render_type)); nd_read_byte(&(obj->type)); if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA)) return; nd_read_byte(&(obj->id)); nd_read_byte(&(obj->flags)); nd_read_short((short *)&(obj->signature)); nd_read_shortpos(obj); if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT)) Int3(); obj->attached_obj = -1; switch(obj->type) { case OBJ_HOSTAGE: obj->control_type = CT_POWERUP; obj->movement_type = MT_NONE; obj->size = HOSTAGE_SIZE; break; case OBJ_ROBOT: obj->control_type = CT_AI; // (MarkA and MikeK said we should not do the crazy last secret stuff with multiple reactors... // This necessary code is our vindication. --MK, 2/15/96) if (obj->id != SPECIAL_REACTOR_ROBOT) obj->movement_type = MT_PHYSICS; else obj->movement_type = MT_NONE; obj->size = Polygon_models[Robot_info[obj->id].model_num].rad; obj->rtype.pobj_info.model_num = Robot_info[obj->id].model_num; obj->rtype.pobj_info.subobj_flags = 0; obj->ctype.ai_info.CLOAKED = (Robot_info[obj->id].cloak_type?1:0); break; case OBJ_POWERUP: obj->control_type = CT_POWERUP; nd_read_byte(&(obj->movement_type)); // might have physics movement obj->size = Powerup_info[obj->id].size; break; case OBJ_PLAYER: obj->control_type = CT_NONE; obj->movement_type = MT_PHYSICS; obj->size = Polygon_models[Player_ship->model_num].rad; obj->rtype.pobj_info.model_num = Player_ship->model_num; obj->rtype.pobj_info.subobj_flags = 0; break; case OBJ_CLUTTER: obj->control_type = CT_NONE; obj->movement_type = MT_NONE; obj->size = Polygon_models[obj->id].rad; obj->rtype.pobj_info.model_num = obj->id; obj->rtype.pobj_info.subobj_flags = 0; break; default: nd_read_byte(&(obj->control_type)); nd_read_byte(&(obj->movement_type)); nd_read_fix(&(obj->size)); break; } nd_read_vector(&(obj->last_pos)); if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP)) nd_read_fix(&(obj->lifeleft)); else { ubyte b; nd_read_byte(&b); obj->lifeleft = (fix)b; // MWA old way -- won't work with big endian machines nd_read_byte((ubyte *)&(obj->lifeleft)); obj->lifeleft = (fix)((int)obj->lifeleft << 12); } if (obj->type == OBJ_ROBOT) { if (Robot_info[obj->id].boss_flag) { sbyte cloaked; nd_read_byte(&cloaked); obj->ctype.ai_info.CLOAKED = cloaked; } } switch (obj->movement_type) { case MT_PHYSICS: nd_read_vector(&(obj->mtype.phys_info.velocity)); nd_read_vector(&(obj->mtype.phys_info.thrust)); break; case MT_SPINNING: nd_read_vector(&(obj->mtype.spin_rate)); break; case MT_NONE: break; default: Int3(); } switch (obj->control_type) { case CT_EXPLOSION: nd_read_fix(&(obj->ctype.expl_info.spawn_time)); nd_read_fix(&(obj->ctype.expl_info.delete_time)); nd_read_short(&(obj->ctype.expl_info.delete_objnum)); obj->ctype.expl_info.next_attach = obj->ctype.expl_info.prev_attach = obj->ctype.expl_info.attach_parent = -1; if (obj->flags & OF_ATTACHED) { //attach to previous object Assert(prev_obj!=NULL); if (prev_obj->control_type == CT_EXPLOSION) { if (prev_obj->flags & OF_ATTACHED && prev_obj->ctype.expl_info.attach_parent!=-1) obj_attach(&Objects[prev_obj->ctype.expl_info.attach_parent],obj); else obj->flags &= ~OF_ATTACHED; } else obj_attach(prev_obj,obj); } break; case CT_LIGHT: nd_read_fix(&(obj->ctype.light_info.intensity)); break; case CT_AI: case CT_WEAPON: case CT_NONE: case CT_FLYING: case CT_DEBRIS: case CT_POWERUP: case CT_SLEW: case CT_CNTRLCEN: case CT_REMOTE: case CT_MORPH: break; case CT_FLYTHROUGH: case CT_REPAIRCEN: default: Int3(); } switch (obj->render_type) { case RT_NONE: break; case RT_MORPH: case RT_POLYOBJ: { int i, tmo; if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) { nd_read_int(&(obj->rtype.pobj_info.model_num)); nd_read_int(&(obj->rtype.pobj_info.subobj_flags)); } if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS)) #if 0 for (i=0;ipobj_info.anim_angles[i])); #endif for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++) nd_read_angvec(&obj->rtype.pobj_info.anim_angles[i]); nd_read_int(&tmo); #ifndef EDITOR obj->rtype.pobj_info.tmap_override = tmo; #else if (tmo==-1) obj->rtype.pobj_info.tmap_override = -1; else { int xlated_tmo = tmap_xlate_table[tmo]; if (xlated_tmo < 0) { //mprintf( (0, "Couldn't find texture for demo object, model_num = %d\n", obj->pobj_info.model_num)); Int3(); xlated_tmo = 0; } obj->rtype.pobj_info.tmap_override = xlated_tmo; } #endif break; } case RT_POWERUP: case RT_WEAPON_VCLIP: case RT_FIREBALL: case RT_HOSTAGE: nd_read_int(&(obj->rtype.vclip_info.vclip_num)); nd_read_fix(&(obj->rtype.vclip_info.frametime)); nd_read_byte(&(obj->rtype.vclip_info.framenum)); break; case RT_LASER: break; default: Int3(); } prev_obj = obj; } void nd_write_object(object *obj) { int life; if ((obj->type == OBJ_ROBOT) && (obj->id == SPECIAL_REACTOR_ROBOT)) Int3(); /* * Do render_type first so on read, we can make determination of * what else to read in */ nd_write_byte(obj->render_type); nd_write_byte(obj->type); if ((obj->render_type == RT_NONE) && (obj->type != OBJ_CAMERA)) return; nd_write_byte(obj->id); nd_write_byte(obj->flags); nd_write_short((short)obj->signature); nd_write_shortpos(obj); if ((obj->type != OBJ_HOSTAGE) && (obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_POWERUP) && (obj->type != OBJ_CLUTTER)) { nd_write_byte(obj->control_type); nd_write_byte(obj->movement_type); nd_write_fix(obj->size); } if (obj->type == OBJ_POWERUP) nd_write_byte(obj->movement_type); nd_write_vector(&obj->last_pos); if ((obj->type == OBJ_WEAPON) && (obj->render_type == RT_WEAPON_VCLIP)) nd_write_fix(obj->lifeleft); else { life = (int)obj->lifeleft; life = life >> 12; if (life > 255) life = 255; nd_write_byte((ubyte)life); } if (obj->type == OBJ_ROBOT) { if (Robot_info[obj->id].boss_flag) { if ((GameTime > Boss_cloak_start_time) && (GameTime < Boss_cloak_end_time)) nd_write_byte(1); else nd_write_byte(0); } } switch (obj->movement_type) { case MT_PHYSICS: nd_write_vector(&obj->mtype.phys_info.velocity); nd_write_vector(&obj->mtype.phys_info.thrust); break; case MT_SPINNING: nd_write_vector(&obj->mtype.spin_rate); break; case MT_NONE: break; default: Int3(); } switch (obj->control_type) { case CT_AI: break; case CT_EXPLOSION: nd_write_fix(obj->ctype.expl_info.spawn_time); nd_write_fix(obj->ctype.expl_info.delete_time); nd_write_short(obj->ctype.expl_info.delete_objnum); break; case CT_WEAPON: break; case CT_LIGHT: nd_write_fix(obj->ctype.light_info.intensity); break; case CT_NONE: case CT_FLYING: case CT_DEBRIS: case CT_POWERUP: case CT_SLEW: //the player is generally saved as slew case CT_CNTRLCEN: case CT_REMOTE: case CT_MORPH: break; case CT_REPAIRCEN: case CT_FLYTHROUGH: default: Int3(); } switch (obj->render_type) { case RT_NONE: break; case RT_MORPH: case RT_POLYOBJ: { int i; if ((obj->type != OBJ_ROBOT) && (obj->type != OBJ_PLAYER) && (obj->type != OBJ_CLUTTER)) { nd_write_int(obj->rtype.pobj_info.model_num); nd_write_int(obj->rtype.pobj_info.subobj_flags); } if ((obj->type != OBJ_PLAYER) && (obj->type != OBJ_DEBRIS)) #if 0 for (i=0;ipobj_info.anim_angles[i]); #endif for (i = 0; i < Polygon_models[obj->rtype.pobj_info.model_num].n_models; i++) nd_write_angvec(&obj->rtype.pobj_info.anim_angles[i]); nd_write_int(obj->rtype.pobj_info.tmap_override); break; } case RT_POWERUP: case RT_WEAPON_VCLIP: case RT_FIREBALL: case RT_HOSTAGE: nd_write_int(obj->rtype.vclip_info.vclip_num); nd_write_fix(obj->rtype.vclip_info.frametime); nd_write_byte(obj->rtype.vclip_info.framenum); break; case RT_LASER: break; default: Int3(); } } int JustStartedRecording=0,JustStartedPlayback=0; void newdemo_record_start_demo() { int i; stop_time(); nd_write_byte(ND_EVENT_START_DEMO); nd_write_byte(DEMO_VERSION); nd_write_byte(DEMO_GAME_TYPE); nd_write_fix(GameTime); #ifdef NETWORK if (Game_mode & GM_MULTI) nd_write_int(Game_mode | (Player_num << 16)); else #endif // NOTE LINK TO ABOVE!!! nd_write_int(Game_mode); #ifdef NETWORK if (Game_mode & GM_TEAM) { nd_write_byte(Netgame.team_vector); nd_write_string(Netgame.team_name[0]); nd_write_string(Netgame.team_name[1]); } if (Game_mode & GM_MULTI) { nd_write_byte((sbyte)N_players); for (i = 0; i < N_players; i++) { nd_write_string(Players[i].callsign); nd_write_byte(Players[i].connected); if (Game_mode & GM_MULTI_COOP) { nd_write_int(Players[i].score); } else { nd_write_short((short)Players[i].net_killed_total); nd_write_short((short)Players[i].net_kills_total); } } } else #endif // NOTE LINK TO ABOVE!!! nd_write_int(Players[Player_num].score); for (i = 0; i < MAX_PRIMARY_WEAPONS; i++) nd_write_short((short)Players[Player_num].primary_ammo[i]); for (i = 0; i < MAX_SECONDARY_WEAPONS; i++) nd_write_short((short)Players[Player_num].secondary_ammo[i]); nd_write_byte((sbyte)Players[Player_num].laser_level); // Support for missions added here nd_write_string(Current_mission_filename); nd_write_byte((sbyte)(f2ir(Players[Player_num].energy))); nd_write_byte((sbyte)(f2ir(Players[Player_num].shields))); nd_write_int(Players[Player_num].flags); // be sure players flags are set nd_write_byte((sbyte)Primary_weapon); nd_write_byte((sbyte)Secondary_weapon); Newdemo_start_frame = FrameCount; JustStartedRecording=1; newdemo_set_new_level(Current_level_num); start_time(); } void newdemo_record_start_frame(int frame_number, fix frame_time ) { int i; if (Newdemo_no_space) { newdemo_stop_playback(); return; } stop_time(); for (i=0;i= 0 ); nd_write_byte(ND_EVENT_START_FRAME); nd_write_short(frame_bytes_written - 1); // from previous frame frame_bytes_written=3; nd_write_int(frame_number); nd_write_int(frame_time); start_time(); } void newdemo_record_render_object(object * obj) { if (ViewWasRecorded[obj-Objects]) return; //if (obj==&Objects[Players[Player_num].objnum] && !Player_is_dead) // return; stop_time(); nd_write_byte(ND_EVENT_RENDER_OBJECT); nd_write_object(obj); start_time(); } extern ubyte RenderingType; void newdemo_record_viewer_object(object * obj) { if (ViewWasRecorded[obj-Objects] && (ViewWasRecorded[obj-Objects]-1)==RenderingType) return; //if (WasRecorded[obj-Objects]) // return; if (RenderingWasRecorded[RenderingType]) return; ViewWasRecorded[obj-Objects]=RenderingType+1; RenderingWasRecorded[RenderingType]=1; stop_time(); nd_write_byte(ND_EVENT_VIEWER_OBJECT); nd_write_byte(RenderingType); nd_write_object(obj); start_time(); } void newdemo_record_sound( int soundno ) { stop_time(); nd_write_byte(ND_EVENT_SOUND); nd_write_int( soundno ); start_time(); } //--unused-- void newdemo_record_sound_once( int soundno ) { //--unused-- stop_time(); //--unused-- nd_write_byte( ND_EVENT_SOUND_ONCE ); //--unused-- nd_write_int( soundno ); //--unused-- start_time(); //--unused-- } //--unused-- void newdemo_record_cockpit_change (int mode) { stop_time(); nd_write_byte (ND_EVENT_CHANGE_COCKPIT); nd_write_int(mode); start_time(); } void newdemo_record_sound_3d( int soundno, int angle, int volume ) { stop_time(); nd_write_byte( ND_EVENT_SOUND_3D ); nd_write_int( soundno ); nd_write_int( angle ); nd_write_int( volume ); start_time(); } void newdemo_record_sound_3d_once( int soundno, int angle, int volume ) { stop_time(); nd_write_byte( ND_EVENT_SOUND_3D_ONCE ); nd_write_int( soundno ); nd_write_int( angle ); nd_write_int( volume ); start_time(); } void newdemo_record_link_sound_to_object3( int soundno, short objnum, fix max_volume, fix max_distance, int loop_start, int loop_end ) { stop_time(); nd_write_byte( ND_EVENT_LINK_SOUND_TO_OBJ ); nd_write_int( soundno ); nd_write_int( Objects[objnum].signature ); nd_write_int( max_volume ); nd_write_int( max_distance ); nd_write_int( loop_start ); nd_write_int( loop_end ); start_time(); } void newdemo_record_kill_sound_linked_to_object( int objnum ) { stop_time(); nd_write_byte( ND_EVENT_KILL_SOUND_TO_OBJ ); nd_write_int( Objects[objnum].signature ); start_time(); } void newdemo_record_wall_hit_process( int segnum, int side, int damage, int playernum ) { stop_time(); //segnum = segnum; //side = side; //damage = damage; //playernum = playernum; nd_write_byte( ND_EVENT_WALL_HIT_PROCESS ); nd_write_int( segnum ); nd_write_int( side ); nd_write_int( damage ); nd_write_int( playernum ); start_time(); } void newdemo_record_guided_start () { nd_write_byte (ND_EVENT_START_GUIDED); } void newdemo_record_guided_end () { nd_write_byte (ND_EVENT_END_GUIDED); } void newdemo_record_secret_exit_blown(int truth) { stop_time(); nd_write_byte( ND_EVENT_SECRET_THINGY ); nd_write_int( truth ); start_time(); } void newdemo_record_trigger( int segnum, int side, int objnum,int shot ) { stop_time(); nd_write_byte( ND_EVENT_TRIGGER ); nd_write_int( segnum ); nd_write_int( side ); nd_write_int( objnum ); nd_write_int(shot); start_time(); } void newdemo_record_hostage_rescued( int hostage_number ) { stop_time(); nd_write_byte( ND_EVENT_HOSTAGE_RESCUED ); nd_write_int( hostage_number ); start_time(); } void newdemo_record_morph_frame(morph_data *md) { stop_time(); nd_write_byte( ND_EVENT_MORPH_FRAME ); #if 0 newdemo_write( md->morph_vecs, sizeof(md->morph_vecs), 1 ); newdemo_write( md->submodel_active, sizeof(md->submodel_active), 1 ); newdemo_write( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 ); #endif nd_write_object( md->obj ); start_time(); } void newdemo_record_wall_toggle( int segnum, int side ) { stop_time(); nd_write_byte( ND_EVENT_WALL_TOGGLE ); nd_write_int( segnum ); nd_write_int( side ); start_time(); } void newdemo_record_control_center_destroyed() { stop_time(); nd_write_byte( ND_EVENT_CONTROL_CENTER_DESTROYED ); nd_write_int( Countdown_seconds_left ); start_time(); } void newdemo_record_hud_message( char * message ) { stop_time(); nd_write_byte( ND_EVENT_HUD_MESSAGE ); nd_write_string(message); start_time(); } void newdemo_record_palette_effect(short r, short g, short b ) { stop_time(); nd_write_byte( ND_EVENT_PALETTE_EFFECT ); nd_write_short( r ); nd_write_short( g ); nd_write_short( b ); start_time(); } void newdemo_record_player_energy(int old_energy, int energy) { stop_time(); nd_write_byte( ND_EVENT_PLAYER_ENERGY ); nd_write_byte((sbyte) old_energy); nd_write_byte((sbyte) energy); start_time(); } void newdemo_record_player_afterburner(fix old_afterburner, fix afterburner) { stop_time(); nd_write_byte( ND_EVENT_PLAYER_AFTERBURNER ); nd_write_byte((sbyte) (old_afterburner>>9)); nd_write_byte((sbyte) (afterburner>>9)); start_time(); } void newdemo_record_player_shields(int old_shield, int shield) { stop_time(); nd_write_byte( ND_EVENT_PLAYER_SHIELD ); nd_write_byte((sbyte)old_shield); nd_write_byte((sbyte)shield); start_time(); } void newdemo_record_player_flags(uint oflags, uint flags) { stop_time(); nd_write_byte( ND_EVENT_PLAYER_FLAGS ); nd_write_int(((short)oflags << 16) | (short)flags); start_time(); } void newdemo_record_player_weapon(int weapon_type, int weapon_num) { stop_time(); nd_write_byte( ND_EVENT_PLAYER_WEAPON ); nd_write_byte((sbyte)weapon_type); nd_write_byte((sbyte)weapon_num); if (weapon_type) nd_write_byte((sbyte)Secondary_weapon); else nd_write_byte((sbyte)Primary_weapon); start_time(); } void newdemo_record_effect_blowup(short segment, int side, vms_vector *pnt) { stop_time(); nd_write_byte (ND_EVENT_EFFECT_BLOWUP); nd_write_short(segment); nd_write_byte((sbyte)side); nd_write_vector(pnt); start_time(); } void newdemo_record_homing_distance(fix distance) { stop_time(); nd_write_byte(ND_EVENT_HOMING_DISTANCE); nd_write_short((short)(distance>>16)); start_time(); } void newdemo_record_letterbox(void) { stop_time(); nd_write_byte(ND_EVENT_LETTERBOX); start_time(); } void newdemo_record_rearview(void) { stop_time(); nd_write_byte(ND_EVENT_REARVIEW); start_time(); } void newdemo_record_restore_cockpit(void) { stop_time(); nd_write_byte(ND_EVENT_RESTORE_COCKPIT); start_time(); } void newdemo_record_restore_rearview(void) { stop_time(); nd_write_byte(ND_EVENT_RESTORE_REARVIEW); start_time(); } void newdemo_record_wall_set_tmap_num1(short seg,ubyte side,short cseg,ubyte cside,short tmap) { stop_time(); nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM1); nd_write_short(seg); nd_write_byte(side); nd_write_short(cseg); nd_write_byte(cside); nd_write_short(tmap); start_time(); } void newdemo_record_wall_set_tmap_num2(short seg,ubyte side,short cseg,ubyte cside,short tmap) { stop_time(); nd_write_byte(ND_EVENT_WALL_SET_TMAP_NUM2); nd_write_short(seg); nd_write_byte(side); nd_write_short(cseg); nd_write_byte(cside); nd_write_short(tmap); start_time(); } void newdemo_record_multi_cloak(int pnum) { stop_time(); nd_write_byte(ND_EVENT_MULTI_CLOAK); nd_write_byte((sbyte)pnum); start_time(); } void newdemo_record_multi_decloak(int pnum) { stop_time(); nd_write_byte(ND_EVENT_MULTI_DECLOAK); nd_write_byte((sbyte)pnum); start_time(); } void newdemo_record_multi_death(int pnum) { stop_time(); nd_write_byte(ND_EVENT_MULTI_DEATH); nd_write_byte((sbyte)pnum); start_time(); } void newdemo_record_multi_kill(int pnum, sbyte kill) { stop_time(); nd_write_byte(ND_EVENT_MULTI_KILL); nd_write_byte((sbyte)pnum); nd_write_byte(kill); start_time(); } void newdemo_record_multi_connect(int pnum, int new_player, char *new_callsign) { stop_time(); nd_write_byte(ND_EVENT_MULTI_CONNECT); nd_write_byte((sbyte)pnum); nd_write_byte((sbyte)new_player); if (!new_player) { nd_write_string(Players[pnum].callsign); nd_write_int(Players[pnum].net_killed_total); nd_write_int(Players[pnum].net_kills_total); } nd_write_string(new_callsign); start_time(); } void newdemo_record_multi_reconnect(int pnum) { stop_time(); nd_write_byte(ND_EVENT_MULTI_RECONNECT); nd_write_byte((sbyte)pnum); start_time(); } void newdemo_record_multi_disconnect(int pnum) { stop_time(); nd_write_byte(ND_EVENT_MULTI_DISCONNECT); nd_write_byte((sbyte)pnum); start_time(); } void newdemo_record_player_score(int score) { stop_time(); nd_write_byte(ND_EVENT_PLAYER_SCORE); nd_write_int(score); start_time(); } void newdemo_record_multi_score(int pnum, int score) { stop_time(); nd_write_byte(ND_EVENT_MULTI_SCORE); nd_write_byte((sbyte)pnum); nd_write_int(score - Players[pnum].score); // called before score is changed!!!! start_time(); } void newdemo_record_primary_ammo(int old_ammo, int new_ammo) { stop_time(); nd_write_byte(ND_EVENT_PRIMARY_AMMO); if (old_ammo < 0) nd_write_short((short)new_ammo); else nd_write_short((short)old_ammo); nd_write_short((short)new_ammo); start_time(); } void newdemo_record_secondary_ammo(int old_ammo, int new_ammo) { stop_time(); nd_write_byte(ND_EVENT_SECONDARY_AMMO); if (old_ammo < 0) nd_write_short((short)new_ammo); else nd_write_short((short)old_ammo); nd_write_short((short)new_ammo); start_time(); } void newdemo_record_door_opening(int segnum, int side) { stop_time(); nd_write_byte(ND_EVENT_DOOR_OPENING); nd_write_short((short)segnum); nd_write_byte((sbyte)side); start_time(); } void newdemo_record_laser_level(sbyte old_level, sbyte new_level) { stop_time(); nd_write_byte(ND_EVENT_LASER_LEVEL); nd_write_byte(old_level); nd_write_byte(new_level); start_time(); } 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) { Assert(front_wall_num <= 255 && back_wall_num <= 255); stop_time(); nd_write_byte(ND_EVENT_CLOAKING_WALL); nd_write_byte(front_wall_num); nd_write_byte(back_wall_num); nd_write_byte(type); nd_write_byte(state); nd_write_byte(cloak_value); nd_write_short(l0>>8); nd_write_short(l1>>8); nd_write_short(l2>>8); nd_write_short(l3>>8); start_time(); } void newdemo_set_new_level(int level_num) { int i; int side; segment *seg; stop_time(); nd_write_byte(ND_EVENT_NEW_LEVEL); nd_write_byte((sbyte)level_num); nd_write_byte((sbyte)Current_level_num); if (JustStartedRecording==1) { nd_write_int(Num_walls); for (i=0;isides[side].tmap_num); nd_write_short (seg->sides[side].tmap_num2); JustStartedRecording=0; } } start_time(); } int newdemo_read_demo_start(int rnd_demo) { sbyte i, version, game_type, laser_level; char c, energy, shield; char text[128], current_mission[9]; nd_read_byte(&c); if ((c != ND_EVENT_START_DEMO) || nd_bad_read) { newmenu_item m[1]; sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_CORRUPT); m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text; newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL ); return 1; } nd_read_byte(&version); nd_read_byte(&game_type); if (game_type < DEMO_GAME_TYPE) { newmenu_item m[2]; sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED); m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text; m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Descent: First Strike"; newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL ); return 1; } if (game_type != DEMO_GAME_TYPE) { newmenu_item m[2]; sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_RECORDED); m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text; m[ 1].type = NM_TYPE_TEXT; m[ 1].text = " In Unknown Descent version"; newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL ); return 1; } if (version < DEMO_VERSION) { if (!rnd_demo) { newmenu_item m[1]; sprintf(text, "%s %s", TXT_CANT_PLAYBACK, TXT_DEMO_OLD); m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text; newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL ); } return 1; } nd_read_fix(&GameTime); Boss_cloak_start_time=Boss_cloak_end_time=GameTime; JasonPlaybackTotal=0; nd_read_int(&Newdemo_game_mode); #ifdef NETWORK change_playernum_to((Newdemo_game_mode >> 16) & 0x7); if (Newdemo_game_mode & GM_TEAM) { nd_read_byte(&(Netgame.team_vector)); nd_read_string(Netgame.team_name[0]); nd_read_string(Netgame.team_name[1]); } if (Newdemo_game_mode & GM_MULTI) { multi_new_game(); nd_read_byte(&c); N_players = (int)c; // changed this to above two lines -- breaks on the mac because of // endian issues // nd_read_byte((sbyte *)&N_players); for (i = 0 ; i < N_players; i++) { Players[i].cloak_time = 0; Players[i].invulnerable_time = 0; nd_read_string(Players[i].callsign); nd_read_byte(&(Players[i].connected)); if (Newdemo_game_mode & GM_MULTI_COOP) { nd_read_int(&(Players[i].score)); } else { nd_read_short((short *)&(Players[i].net_killed_total)); nd_read_short((short *)&(Players[i].net_kills_total)); } } Game_mode = Newdemo_game_mode; multi_sort_kill_list(); Game_mode = GM_NORMAL; } else #endif nd_read_int(&(Players[Player_num].score)); // Note link to above if! for (i = 0; i < MAX_PRIMARY_WEAPONS; i++) nd_read_short((short*)&(Players[Player_num].primary_ammo[i])); for (i = 0; i < MAX_SECONDARY_WEAPONS; i++) nd_read_short((short*)&(Players[Player_num].secondary_ammo[i])); nd_read_byte(&laser_level); if (laser_level != Players[Player_num].laser_level) { Players[Player_num].laser_level = laser_level; update_laser_weapon_info(); } // Support for missions nd_read_string(current_mission); if (!load_mission_by_name(current_mission)) { if (!rnd_demo) { newmenu_item m[1]; sprintf(text, TXT_NOMISSION4DEMO, current_mission); m[ 0].type = NM_TYPE_TEXT; m[ 0].text = text; newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL ); } return 1; } nd_recorded_total = 0; nd_playback_total = 0; nd_read_byte(&energy); nd_read_byte(&shield); nd_read_int((int *)&(Players[Player_num].flags)); if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) { Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2); Newdemo_players_cloaked |= (1 << Player_num); } if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE) Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2); nd_read_byte((sbyte *)&Primary_weapon); nd_read_byte((sbyte *)&Secondary_weapon); // Next bit of code to fix problem that I introduced between 1.0 and 1.1 // check the next byte -- it _will_ be a load_new_level event. If it is // not, then we must shift all bytes up by one. Players[Player_num].energy = i2f(energy); Players[Player_num].shields = i2f(shield); JustStartedPlayback=1; return 0; } void newdemo_pop_ctrlcen_triggers() { int anim_num, n, i; int side, cside; segment *seg, *csegp; for (i = 0; i < ControlCenterTriggers.num_links; i++) { seg = &Segments[ControlCenterTriggers.seg[i]]; side = ControlCenterTriggers.side[i]; csegp = &Segments[seg->children[side]]; cside = find_connect_side(seg, csegp); anim_num = Walls[seg->sides[side].wall_num].clip_num; n = WallAnims[anim_num].num_frames; if (WallAnims[anim_num].flags & WCF_TMAP1) { seg->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[n-1]; } else { seg->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[n-1]; } } } #define N_PLAYER_SHIP_TEXTURES 6 void nd_render_extras (ubyte,object *); extern void multi_apply_goal_textures (); ubyte Newdemo_flying_guided=0; int newdemo_read_frame_information() { int done, segnum, side, objnum, soundno, angle, volume, i,shot; object *obj; ubyte c,WhichWindow; static sbyte saved_letter_cockpit; static sbyte saved_rearview_cockpit; object extraobj; static char LastReadValue=101; segment *seg; done = 0; if (Newdemo_vcr_state != ND_STATE_PAUSED) for (segnum=0; segnum <= Highest_segment_index; segnum++) Segments[segnum].objects = -1; reset_objects(1); Players[Player_num].homing_object_dist = -F1_0; prev_obj = NULL; while( !done ) { nd_read_byte(&c); if (nd_bad_read) { done = -1; break; } switch( c ) { case ND_EVENT_START_FRAME: { // Followed by an integer frame number, then a fix FrameTime short last_frame_length; done=1; nd_read_short(&last_frame_length); nd_read_int(&NewdemoFrameCount); nd_read_int((int *)&nd_recorded_time); if (Newdemo_vcr_state == ND_STATE_PLAYBACK) nd_recorded_total += nd_recorded_time; NewdemoFrameCount--; if (nd_bad_read) { done = -1; break; } break; } case ND_EVENT_VIEWER_OBJECT: // Followed by an object structure nd_read_byte (&WhichWindow); if (WhichWindow&15) { //mprintf ((0,"Reading extra!\n")); nd_read_object (&extraobj); if (Newdemo_vcr_state!=ND_STATE_PAUSED) { if (nd_bad_read) { done = -1; break; } nd_render_extras (WhichWindow,&extraobj); } } else { //mprintf ((0,"Reading viewer!\n")); //Viewer=&Objects[0]; nd_read_object(Viewer); if (Newdemo_vcr_state != ND_STATE_PAUSED) { if (nd_bad_read) { done = -1; break; } segnum = Viewer->segnum; Viewer->next = Viewer->prev = Viewer->segnum = -1; // HACK HACK HACK -- since we have multiple level recording, it can be the case // HACK HACK HACK -- that when rewinding the demo, the viewer is in a segment // HACK HACK HACK -- that is greater than the highest index of segments. Bash // HACK HACK HACK -- the viewer to segment 0 for bogus view. if (segnum > Highest_segment_index) segnum = 0; obj_link(Viewer-Objects,segnum); } } break; case ND_EVENT_RENDER_OBJECT: // Followed by an object structure objnum = obj_allocate(); if (objnum==-1) break; obj = &Objects[objnum]; nd_read_object(obj); if (nd_bad_read) { done = -1; break; } if (Newdemo_vcr_state != ND_STATE_PAUSED) { segnum = obj->segnum; obj->next = obj->prev = obj->segnum = -1; // HACK HACK HACK -- don't render objects is segments greater than Highest_segment_index // HACK HACK HACK -- (see above) if (segnum > Highest_segment_index) break; obj_link(obj-Objects,segnum); #ifdef NETWORK if ((obj->type == OBJ_PLAYER) && (Newdemo_game_mode & GM_MULTI)) { int player; if (Newdemo_game_mode & GM_TEAM) player = get_team(obj->id); else player = obj->id; if (player == 0) break; player--; for (i=0;irtype.pobj_info.model_num].first_texture+i]]; multi_player_textures[player][4] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2]]; multi_player_textures[player][5] = ObjBitmaps[ObjBitmapPtrs[First_multi_bitmap_num+(player)*2+1]]; obj->rtype.pobj_info.alt_textures = player+1; } #endif } break; case ND_EVENT_SOUND: nd_read_int(&soundno); if (nd_bad_read) {done = -1; break; } if (Newdemo_vcr_state == ND_STATE_PLAYBACK) digi_play_sample( soundno, F1_0 ); break; //--unused case ND_EVENT_SOUND_ONCE: //--unused nd_read_int(&soundno); //--unused if (nd_bad_read) { done = -1; break; } //--unused if (Newdemo_vcr_state == ND_STATE_PLAYBACK) //--unused digi_play_sample_once( soundno, F1_0 ); //--unused break; case ND_EVENT_SOUND_3D: nd_read_int(&soundno); nd_read_int(&angle); nd_read_int(&volume); if (nd_bad_read) { done = -1; break; } if (Newdemo_vcr_state == ND_STATE_PLAYBACK) digi_play_sample_3d( soundno, angle, volume, 0 ); break; case ND_EVENT_SOUND_3D_ONCE: nd_read_int(&soundno); nd_read_int(&angle); nd_read_int(&volume); if (nd_bad_read) { done = -1; break; } if (Newdemo_vcr_state == ND_STATE_PLAYBACK) digi_play_sample_3d( soundno, angle, volume, 1 ); break; case ND_EVENT_LINK_SOUND_TO_OBJ: { int soundno, objnum, max_volume, max_distance, loop_start, loop_end; int signature; nd_read_int( &soundno ); nd_read_int( &signature ); nd_read_int( &max_volume ); nd_read_int( &max_distance ); nd_read_int( &loop_start ); nd_read_int( &loop_end ); objnum = newdemo_find_object( signature ); if ( objnum > -1 ) { // @mk, 2/22/96, John told me to. digi_link_sound_to_object3( soundno, objnum, 1, max_volume, max_distance, loop_start, loop_end ); } } break; case ND_EVENT_KILL_SOUND_TO_OBJ: { int objnum, signature; nd_read_int( &signature ); objnum = newdemo_find_object( signature ); if ( objnum > -1 ) { // @mk, 2/22/96, John told me to. digi_kill_sound_linked_to_object(objnum); } } break; case ND_EVENT_WALL_HIT_PROCESS: { int player, segnum; fix damage; nd_read_int(&segnum); nd_read_int(&side); nd_read_fix(&damage); nd_read_int(&player); if (nd_bad_read) { done = -1; break; } if (Newdemo_vcr_state != ND_STATE_PAUSED) wall_hit_process(&Segments[segnum], side, damage, player, &(Objects[0]) ); break; } case ND_EVENT_TRIGGER: nd_read_int(&segnum); nd_read_int(&side); nd_read_int(&objnum); nd_read_int(&shot); if (nd_bad_read) { done = -1; break; } if (Newdemo_vcr_state != ND_STATE_PAUSED) { mprintf ((0,"EVENT TRIGGER! shot=%d\n",shot)); if (Triggers[Walls[Segments[segnum].sides[side].wall_num].trigger].type == TT_SECRET_EXIT) { int truth; nd_read_byte(&c); Assert(c == ND_EVENT_SECRET_THINGY); nd_read_int(&truth); if (!truth) check_trigger(&Segments[segnum], side, objnum,shot); } else check_trigger(&Segments[segnum], side, objnum,shot); } break; case ND_EVENT_HOSTAGE_RESCUED: { int hostage_number; nd_read_int(&hostage_number); if (nd_bad_read) { done = -1; break; } if (Newdemo_vcr_state != ND_STATE_PAUSED) hostage_rescue( hostage_number ); break; } case ND_EVENT_MORPH_FRAME: { #if 0 morph_data *md; md = &morph_objects[0]; if (newdemo_read( md->morph_vecs, sizeof(md->morph_vecs), 1 )!=1) { done=-1; break; } if (newdemo_read( md->submodel_active, sizeof(md->submodel_active), 1 )!=1) { done=-1; break; } if (newdemo_read( md->submodel_startpoints, sizeof(md->submodel_startpoints), 1 )!=1) { done=-1; break; } #endif objnum = obj_allocate(); if (objnum==-1) break; obj = &Objects[objnum]; nd_read_object(obj); obj->render_type = RT_POLYOBJ; if (Newdemo_vcr_state != ND_STATE_PAUSED) { if (nd_bad_read) { done = -1; break; } if (Newdemo_vcr_state != ND_STATE_PAUSED) { segnum = obj->segnum; obj->next = obj->prev = obj->segnum = -1; obj_link(obj-Objects,segnum); } } break; } case ND_EVENT_WALL_TOGGLE: nd_read_int(&segnum); nd_read_int(&side); if (nd_bad_read) {done = -1; break; } if (Newdemo_vcr_state != ND_STATE_PAUSED) wall_toggle(&Segments[segnum], side); break; case ND_EVENT_CONTROL_CENTER_DESTROYED: nd_read_int(&Countdown_seconds_left); Control_center_destroyed = 1; if (nd_bad_read) { done = -1; break; } if (!Newdemo_cntrlcen_destroyed) { newdemo_pop_ctrlcen_triggers(); Newdemo_cntrlcen_destroyed = 1; //do_controlcen_destroyed_stuff(NULL); } break; case ND_EVENT_HUD_MESSAGE: { char hud_msg[60]; nd_read_string(&(hud_msg[0])); if (nd_bad_read) { done = -1; break; } HUD_init_message( hud_msg ); break; } case ND_EVENT_START_GUIDED: Newdemo_flying_guided=1; if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) Newdemo_flying_guided=0; break; case ND_EVENT_END_GUIDED: Newdemo_flying_guided=0; if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) Newdemo_flying_guided=1; break; case ND_EVENT_PALETTE_EFFECT: { short r, g, b; nd_read_short(&r); nd_read_short(&g); nd_read_short(&b); if (nd_bad_read) { done = -1; break; } PALETTE_FLASH_SET(r,g,b); break; } case ND_EVENT_PLAYER_ENERGY: { ubyte energy; ubyte old_energy; nd_read_byte(&old_energy); nd_read_byte(&energy); if (nd_bad_read) {done = -1; break; } if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { Players[Player_num].energy = i2f(energy); } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { if (old_energy != 255) Players[Player_num].energy = i2f(old_energy); } break; } case ND_EVENT_PLAYER_AFTERBURNER: { ubyte afterburner; ubyte old_afterburner; nd_read_byte(&old_afterburner); nd_read_byte(&afterburner); if (nd_bad_read) {done = -1; break; } if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { Afterburner_charge = afterburner<<9; } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { if (old_afterburner != 255) Afterburner_charge = old_afterburner<<9; } break; } case ND_EVENT_PLAYER_SHIELD: { ubyte shield; ubyte old_shield; nd_read_byte(&old_shield); nd_read_byte(&shield); if (nd_bad_read) {done = -1; break; } if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { Players[Player_num].shields = i2f(shield); } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { if (old_shield != 255) Players[Player_num].shields = i2f(old_shield); } break; } case ND_EVENT_PLAYER_FLAGS: { uint oflags; nd_read_int((int *)&(Players[Player_num].flags)); if (nd_bad_read) {done = -1; break; } oflags = Players[Player_num].flags >> 16; Players[Player_num].flags &= 0xffff; if ((Newdemo_vcr_state == ND_STATE_REWINDING) || ((Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) && (oflags != 0xffff)) ) { if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) { Players[Player_num].cloak_time = 0; Newdemo_players_cloaked &= ~(1 << Player_num); } if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) { Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2); Newdemo_players_cloaked |= (1 << Player_num); } if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)) Players[Player_num].invulnerable_time = 0; if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)) Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2); Players[Player_num].flags = oflags; } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { if (!(oflags & PLAYER_FLAGS_CLOAKED) && (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) { Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2); Newdemo_players_cloaked |= (1 << Player_num); } if ((oflags & PLAYER_FLAGS_CLOAKED) && !(Players[Player_num].flags & PLAYER_FLAGS_CLOAKED)) { Players[Player_num].cloak_time = 0; Newdemo_players_cloaked &= ~(1 << Player_num); } if (!(oflags & PLAYER_FLAGS_INVULNERABLE) && (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)) Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2); if ((oflags & PLAYER_FLAGS_INVULNERABLE) && !(Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE)) Players[Player_num].invulnerable_time = 0; } update_laser_weapon_info(); // in case of quad laser change break; } case ND_EVENT_PLAYER_WEAPON: { sbyte weapon_type, weapon_num; sbyte old_weapon; nd_read_byte(&weapon_type); nd_read_byte(&weapon_num); nd_read_byte(&old_weapon); if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { if (weapon_type == 0) Primary_weapon = (int)weapon_num; else Secondary_weapon = (int)weapon_num; } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { if (weapon_type == 0) Primary_weapon = (int)old_weapon; else Secondary_weapon = (int)old_weapon; } break; } case ND_EVENT_EFFECT_BLOWUP: { short segnum; sbyte side; vms_vector pnt; object dummy; //create a dummy object which will be the weapon that hits //the monitor. the blowup code wants to know who the parent of the //laser is, so create a laser whose parent is the player dummy.ctype.laser_info.parent_type = OBJ_PLAYER; nd_read_short(&segnum); nd_read_byte(&side); nd_read_vector(&pnt); if (Newdemo_vcr_state != ND_STATE_PAUSED) check_effect_blowup(&(Segments[segnum]), side, &pnt, &dummy, 0); break; } case ND_EVENT_HOMING_DISTANCE: { short distance; nd_read_short(&distance); Players[Player_num].homing_object_dist = i2f((int)(distance << 16)); break; } case ND_EVENT_LETTERBOX: if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { saved_letter_cockpit = Cockpit_mode; select_cockpit(CM_LETTERBOX); } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) select_cockpit(saved_letter_cockpit); break; case ND_EVENT_CHANGE_COCKPIT: { int dummy; nd_read_int (&dummy); select_cockpit (dummy); break; } case ND_EVENT_REARVIEW: if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { saved_rearview_cockpit = Cockpit_mode; if (Cockpit_mode == CM_FULL_COCKPIT) select_cockpit(CM_REAR_VIEW); Rear_view=1; } else if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore saved_rearview_cockpit = CM_FULL_COCKPIT; select_cockpit(saved_rearview_cockpit); Rear_view=0; } break; case ND_EVENT_RESTORE_COCKPIT: if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { saved_letter_cockpit = Cockpit_mode; select_cockpit(CM_LETTERBOX); } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) select_cockpit(saved_letter_cockpit); break; case ND_EVENT_RESTORE_REARVIEW: if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { saved_rearview_cockpit = Cockpit_mode; if (Cockpit_mode == CM_FULL_COCKPIT) select_cockpit(CM_REAR_VIEW); Rear_view=1; } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { if (saved_rearview_cockpit == CM_REAR_VIEW) // hack to be sure we get a good cockpit on restore saved_rearview_cockpit = CM_FULL_COCKPIT; select_cockpit(saved_rearview_cockpit); Rear_view=0; } break; case ND_EVENT_WALL_SET_TMAP_NUM1: { short seg, cseg, tmap; ubyte side,cside; nd_read_short(&seg); nd_read_byte(&side); nd_read_short(&cseg); nd_read_byte(&cside); nd_read_short( &tmap ); if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) Segments[seg].sides[side].tmap_num = Segments[cseg].sides[cside].tmap_num = tmap; break; } case ND_EVENT_WALL_SET_TMAP_NUM2: { short seg, cseg, tmap; ubyte side,cside; nd_read_short(&seg); nd_read_byte(&side); nd_read_short(&cseg); nd_read_byte(&cside); nd_read_short( &tmap ); if ((Newdemo_vcr_state != ND_STATE_PAUSED) && (Newdemo_vcr_state != ND_STATE_REWINDING) && (Newdemo_vcr_state != ND_STATE_ONEFRAMEBACKWARD)) { Assert(tmap!=0 && Segments[seg].sides[side].tmap_num2!=0); Segments[seg].sides[side].tmap_num2 = Segments[cseg].sides[cside].tmap_num2 = tmap; } break; } case ND_EVENT_MULTI_CLOAK: { sbyte pnum; nd_read_byte(&pnum); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED; Players[pnum].cloak_time = 0; Newdemo_players_cloaked &= ~(1 << pnum); } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { Players[pnum].flags |= PLAYER_FLAGS_CLOAKED; Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2); Newdemo_players_cloaked |= (1 << pnum); } break; } case ND_EVENT_MULTI_DECLOAK: { sbyte pnum; nd_read_byte(&pnum); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { Players[pnum].flags |= PLAYER_FLAGS_CLOAKED; Players[pnum].cloak_time = GameTime - (CLOAK_TIME_MAX / 2); Newdemo_players_cloaked |= (1 << pnum); } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { Players[pnum].flags &= ~PLAYER_FLAGS_CLOAKED; Players[pnum].cloak_time = 0; Newdemo_players_cloaked &= ~(1 << pnum); } break; } case ND_EVENT_MULTI_DEATH: { sbyte pnum; nd_read_byte(&pnum); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) Players[pnum].net_killed_total--; else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) Players[pnum].net_killed_total++; break; } #ifdef NETWORK case ND_EVENT_MULTI_KILL: { sbyte pnum, kill; nd_read_byte(&pnum); nd_read_byte(&kill); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { Players[pnum].net_kills_total -= kill; if (Newdemo_game_mode & GM_TEAM) team_kills[get_team(pnum)] -= kill; } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { Players[pnum].net_kills_total += kill; if (Newdemo_game_mode & GM_TEAM) team_kills[get_team(pnum)] += kill; } Game_mode = Newdemo_game_mode; multi_sort_kill_list(); Game_mode = GM_NORMAL; break; } case ND_EVENT_MULTI_CONNECT: { sbyte pnum, new_player; int killed_total, kills_total; char new_callsign[CALLSIGN_LEN+1], old_callsign[CALLSIGN_LEN+1]; nd_read_byte(&pnum); nd_read_byte(&new_player); if (!new_player) { nd_read_string(old_callsign); nd_read_int(&killed_total); nd_read_int(&kills_total); } nd_read_string(new_callsign); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { Players[pnum].connected = 0; if (!new_player) { memcpy(Players[pnum].callsign, old_callsign, CALLSIGN_LEN+1); Players[pnum].net_killed_total = killed_total; Players[pnum].net_kills_total = kills_total; } else { N_players--; } } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { Players[pnum].connected = 1; Players[pnum].net_kills_total = 0; Players[pnum].net_killed_total = 0; memcpy(Players[pnum].callsign, new_callsign, CALLSIGN_LEN+1); if (new_player) N_players++; } break; } case ND_EVENT_MULTI_RECONNECT: { sbyte pnum; nd_read_byte(&pnum); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) Players[pnum].connected = 0; else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) Players[pnum].connected = 1; break; } case ND_EVENT_MULTI_DISCONNECT: { sbyte pnum; nd_read_byte(&pnum); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) Players[pnum].connected = 1; else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) Players[pnum].connected = 0; break; } case ND_EVENT_MULTI_SCORE: { int score; sbyte pnum; nd_read_byte(&pnum); nd_read_int(&score); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) Players[pnum].score -= score; else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) Players[pnum].score += score; Game_mode = Newdemo_game_mode; multi_sort_kill_list(); Game_mode = GM_NORMAL; break; } #endif // NETWORK case ND_EVENT_PLAYER_SCORE: { int score; nd_read_int(&score); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) Players[Player_num].score -= score; else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) Players[Player_num].score += score; break; } case ND_EVENT_PRIMARY_AMMO: { short old_ammo, new_ammo; nd_read_short(&old_ammo); nd_read_short(&new_ammo); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) Players[Player_num].primary_ammo[Primary_weapon] = old_ammo; else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) Players[Player_num].primary_ammo[Primary_weapon] = new_ammo; break; } case ND_EVENT_SECONDARY_AMMO: { short old_ammo, new_ammo; nd_read_short(&old_ammo); nd_read_short(&new_ammo); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) Players[Player_num].secondary_ammo[Secondary_weapon] = old_ammo; else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) Players[Player_num].secondary_ammo[Secondary_weapon] = new_ammo; break; } case ND_EVENT_DOOR_OPENING: { short segnum; sbyte side; nd_read_short(&segnum); nd_read_byte(&side); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { int anim_num; int cside; segment *segp, *csegp; segp = &Segments[segnum]; csegp = &Segments[segp->children[side]]; cside = find_connect_side(segp, csegp); anim_num = Walls[segp->sides[side].wall_num].clip_num; if (WallAnims[anim_num].flags & WCF_TMAP1) { segp->sides[side].tmap_num = csegp->sides[cside].tmap_num = WallAnims[anim_num].frames[0]; } else { segp->sides[side].tmap_num2 = csegp->sides[cside].tmap_num2 = WallAnims[anim_num].frames[0]; } } break; } case ND_EVENT_LASER_LEVEL: { sbyte old_level, new_level; nd_read_byte(&old_level); nd_read_byte(&new_level); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { Players[Player_num].laser_level = old_level; update_laser_weapon_info(); } else if ((Newdemo_vcr_state == ND_STATE_PLAYBACK) || (Newdemo_vcr_state == ND_STATE_FASTFORWARD) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD)) { Players[Player_num].laser_level = new_level; update_laser_weapon_info(); } break; } case ND_EVENT_CLOAKING_WALL: { ubyte back_wall_num,front_wall_num,type,state,cloak_value; short l0,l1,l2,l3; segment *segp; int sidenum; nd_read_byte(&front_wall_num); nd_read_byte(&back_wall_num); nd_read_byte(&type); nd_read_byte(&state); nd_read_byte(&cloak_value); nd_read_short(&l0); nd_read_short(&l1); nd_read_short(&l2); nd_read_short(&l3); Walls[front_wall_num].type = type; Walls[front_wall_num].state = state; Walls[front_wall_num].cloak_value = cloak_value; segp = &Segments[Walls[front_wall_num].segnum]; sidenum = Walls[front_wall_num].sidenum; segp->sides[sidenum].uvls[0].l = ((int) l0) << 8; segp->sides[sidenum].uvls[1].l = ((int) l1) << 8; segp->sides[sidenum].uvls[2].l = ((int) l2) << 8; segp->sides[sidenum].uvls[3].l = ((int) l3) << 8; Walls[back_wall_num].type = type; Walls[back_wall_num].state = state; Walls[back_wall_num].cloak_value = cloak_value; segp = &Segments[Walls[back_wall_num].segnum]; sidenum = Walls[back_wall_num].sidenum; segp->sides[sidenum].uvls[0].l = ((int) l0) << 8; segp->sides[sidenum].uvls[1].l = ((int) l1) << 8; segp->sides[sidenum].uvls[2].l = ((int) l2) << 8; segp->sides[sidenum].uvls[3].l = ((int) l3) << 8; break; } case ND_EVENT_NEW_LEVEL: { sbyte new_level, old_level, loaded_level; nd_read_byte (&new_level); nd_read_byte (&old_level); if (Newdemo_vcr_state == ND_STATE_PAUSED) break; stop_time(); if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) loaded_level = old_level; else { loaded_level = new_level; for (i = 0; i < MAX_PLAYERS; i++) { Players[i].cloak_time = 0; Players[i].flags &= ~PLAYER_FLAGS_CLOAKED; } } if ((loaded_level < Last_secret_level) || (loaded_level > Last_level)) { newmenu_item m[3]; m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK; m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD; m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT; newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL ); return -1; } LoadLevel((int)loaded_level,1); Newdemo_cntrlcen_destroyed = 0; if (JustStartedPlayback) { nd_read_int (&Num_walls); for (i=0;isides[side].tmap_num); nd_read_short (&seg->sides[side].tmap_num2); } #ifdef NETWORK if (Newdemo_game_mode & GM_CAPTURE) multi_apply_goal_textures (); #endif JustStartedPlayback=0; } // so says Rob H.!!! if (Newdemo_game_mode & GM_MULTI) { // so says Rob H.!!! for (i = 0; i < Num_walls; i++) { // so says Rob H.!!! if (Walls[i].type == WALL_BLASTABLE) // so says Rob H.!!! { // so says Rob H.!!! int a, n; // so says Rob H.!!! int side; // so says Rob H.!!! segment *seg; // so says Rob H.!!! // so says Rob H.!!! seg = &Segments[Walls[i].segnum]; // so says Rob H.!!! side = Walls[i].sidenum; // so says Rob H.!!! a = Walls[i].clip_num; // so says Rob H.!!! n = WallAnims[a].num_frames; // so says Rob H.!!! seg->sides[side].tmap_num = WallAnims[a].frames[n-1]; // so says Rob H.!!! Walls[i].flags |= WALL_BLASTED; // so says Rob H.!!! } // so says Rob H.!!! } // so says Rob H.!!! } reset_palette_add(); // get palette back to normal start_time(); break; } case ND_EVENT_EOF: { done=-1; cfseek(infile, -1, SEEK_CUR); // get back to the EOF marker Newdemo_at_eof = 1; NewdemoFrameCount++; break; } default: Int3(); } } LastReadValue=c; if (nd_bad_read) { newmenu_item m[2]; m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_ERR_READING; m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_DEMO_OLD_CORRUPT; newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL ); } return done; } void newdemo_goto_beginning() { //if (NewdemoFrameCount == 0) // return; cfseek(infile, 0, SEEK_SET); Newdemo_vcr_state = ND_STATE_PLAYBACK; if (newdemo_read_demo_start(0)) newdemo_stop_playback(); if (newdemo_read_frame_information() == -1) newdemo_stop_playback(); if (newdemo_read_frame_information() == -1) newdemo_stop_playback(); Newdemo_vcr_state = ND_STATE_PAUSED; Newdemo_at_eof = 0; } void newdemo_goto_end() { short frame_length, byte_count, bshort; sbyte level, bbyte, laser_level; ubyte energy, shield, c; int i, loc, bint; cfseek(infile, -2, SEEK_END); nd_read_byte(&level); if ((level < Last_secret_level) || (level > Last_level)) { newmenu_item m[3]; m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_CANT_PLAYBACK; m[ 1].type = NM_TYPE_TEXT; m[ 1].text = TXT_LEVEL_CANT_LOAD; m[ 2].type = NM_TYPE_TEXT; m[ 2].text = TXT_DEMO_OLD_CORRUPT; newmenu_do( NULL, NULL, sizeof(m)/sizeof(*m), m, NULL ); newdemo_stop_playback(); return; } if (level != Current_level_num) LoadLevel(level,1); cfseek(infile, -4, SEEK_END); nd_read_short(&byte_count); cfseek(infile, -2 - byte_count, SEEK_CUR); nd_read_short(&frame_length); loc = cftell(infile); if (Newdemo_game_mode & GM_MULTI) nd_read_byte(&Newdemo_players_cloaked); else nd_read_byte(&bbyte); nd_read_byte(&bbyte); nd_read_short(&bshort); nd_read_int(&bint); nd_read_byte(&energy); nd_read_byte(&shield); Players[Player_num].energy = i2f(energy); Players[Player_num].shields = i2f(shield); nd_read_int((int *)&(Players[Player_num].flags)); if (Players[Player_num].flags & PLAYER_FLAGS_CLOAKED) { Players[Player_num].cloak_time = GameTime - (CLOAK_TIME_MAX / 2); Newdemo_players_cloaked |= (1 << Player_num); } if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE) Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2); nd_read_byte((sbyte *)&Primary_weapon); nd_read_byte((sbyte *)&Secondary_weapon); for (i = 0; i < MAX_PRIMARY_WEAPONS; i++) nd_read_short((short *)&(Players[Player_num].primary_ammo[i])); for (i = 0; i < MAX_SECONDARY_WEAPONS; i++) nd_read_short((short *)&(Players[Player_num].secondary_ammo[i])); nd_read_byte(&laser_level); if (laser_level != Players[Player_num].laser_level) { Players[Player_num].laser_level = laser_level; update_laser_weapon_info(); } if (Newdemo_game_mode & GM_MULTI) { nd_read_byte(&c); N_players = (int)c; // see newdemo_read_start_demo for explanation of // why this is commented out // nd_read_byte((sbyte *)&N_players); for (i = 0; i < N_players; i++) { nd_read_string(Players[i].callsign); nd_read_byte(&(Players[i].connected)); if (Newdemo_game_mode & GM_MULTI_COOP) { nd_read_int(&(Players[i].score)); } else { nd_read_short((short *)&(Players[i].net_killed_total)); nd_read_short((short *)&(Players[i].net_kills_total)); } } } else { nd_read_int(&(Players[Player_num].score)); } cfseek(infile, loc, SEEK_SET); cfseek(infile, -frame_length, SEEK_CUR); nd_read_int(&NewdemoFrameCount); // get the frame count NewdemoFrameCount--; cfseek(infile, 4, SEEK_CUR); Newdemo_vcr_state = ND_STATE_PLAYBACK; newdemo_read_frame_information(); // then the frame information Newdemo_vcr_state = ND_STATE_PAUSED; return; } void newdemo_back_frames(int frames) { short last_frame_length; int i; for (i = 0; i < frames; i++) { cfseek(infile, -10, SEEK_CUR); nd_read_short(&last_frame_length); cfseek(infile, 8 - last_frame_length, SEEK_CUR); if (!Newdemo_at_eof && newdemo_read_frame_information() == -1) { newdemo_stop_playback(); return; } if (Newdemo_at_eof) Newdemo_at_eof = 0; cfseek(infile, -10, SEEK_CUR); nd_read_short(&last_frame_length); cfseek(infile, 8 - last_frame_length, SEEK_CUR); } } /* * routine to interpolate the viewer position. the current position is * stored in the Viewer object. Save this position, and read the next * frame to get all objects read in. Calculate the delta playback and * the delta recording frame times between the two frames, then intepolate * the viewers position accordingly. nd_recorded_time is the time that it * took the recording to render the frame that we are currently looking * at. */ void interpolate_frame(fix d_play, fix d_recorded) { int i, j, num_cur_objs; fix factor; object *cur_objs; factor = fixdiv(d_play, d_recorded); if (factor > F1_0) factor = F1_0; num_cur_objs = Highest_object_index; cur_objs = (object *)d_malloc(sizeof(object) * (num_cur_objs + 1)); if (cur_objs == NULL) { mprintf((0,"Couldn't get %d bytes for cur_objs in interpolate_frame\n", sizeof(object) * num_cur_objs)); Int3(); return; } for (i = 0; i <= num_cur_objs; i++) memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object)); Newdemo_vcr_state = ND_STATE_PAUSED; if (newdemo_read_frame_information() == -1) { d_free(cur_objs); newdemo_stop_playback(); return; } for (i = 0; i <= num_cur_objs; i++) { for (j = 0; j <= Highest_object_index; j++) { if (cur_objs[i].signature == Objects[j].signature) { ubyte render_type = cur_objs[i].render_type; //fix delta_p, delta_h, delta_b; fix delta_x, delta_y, delta_z; //vms_angvec cur_angles, dest_angles; // Extract the angles from the object orientation matrix. // Some of this code taken from ai_turn_towards_vector // Don't do the interpolation on certain render types which don't use an orientation matrix if (!((render_type == RT_LASER) || (render_type == RT_FIREBALL) || (render_type == RT_POWERUP))) { vms_vector fvec1, fvec2, rvec1, rvec2; fix mag1; fvec1 = cur_objs[i].orient.fvec; vm_vec_scale(&fvec1, F1_0-factor); fvec2 = Objects[j].orient.fvec; vm_vec_scale(&fvec2, factor); vm_vec_add2(&fvec1, &fvec2); mag1 = vm_vec_normalize_quick(&fvec1); if (mag1 > F1_0/256) { rvec1 = cur_objs[i].orient.rvec; vm_vec_scale(&rvec1, F1_0-factor); rvec2 = Objects[j].orient.rvec; vm_vec_scale(&rvec2, factor); vm_vec_add2(&rvec1, &rvec2); vm_vec_normalize_quick(&rvec1); // Note: Doesn't matter if this is null, if null, vm_vector_2_matrix will just use fvec1 vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1); } //--old new way -- vms_vector fvec1, fvec2, rvec1, rvec2; //--old new way -- //--old new way -- fvec1 = cur_objs[i].orient.fvec; //--old new way -- vm_vec_scale(&fvec1, F1_0-factor); //--old new way -- fvec2 = Objects[j].orient.fvec; //--old new way -- vm_vec_scale(&fvec2, factor); //--old new way -- vm_vec_add2(&fvec1, &fvec2); //--old new way -- vm_vec_normalize_quick(&fvec1); //--old new way -- //--old new way -- rvec1 = cur_objs[i].orient.rvec; //--old new way -- vm_vec_scale(&rvec1, F1_0-factor); //--old new way -- rvec2 = Objects[j].orient.rvec; //--old new way -- vm_vec_scale(&rvec2, factor); //--old new way -- vm_vec_add2(&rvec1, &rvec2); //--old new way -- vm_vec_normalize_quick(&rvec1); //--old new way -- //--old new way -- vm_vector_2_matrix(&cur_objs[i].orient, &fvec1, NULL, &rvec1); // -- old fashioned way -- vm_extract_angles_matrix(&cur_angles, &(cur_objs[i].orient)); // -- old fashioned way -- vm_extract_angles_matrix(&dest_angles, &(Objects[j].orient)); // -- old fashioned way -- // -- old fashioned way -- delta_p = (dest_angles.p - cur_angles.p); // -- old fashioned way -- delta_h = (dest_angles.h - cur_angles.h); // -- old fashioned way -- delta_b = (dest_angles.b - cur_angles.b); // -- old fashioned way -- // -- old fashioned way -- if (delta_p != 0) { // -- old fashioned way -- if (delta_p > F1_0/2) delta_p = dest_angles.p - cur_angles.p - F1_0; // -- old fashioned way -- if (delta_p < -F1_0/2) delta_p = dest_angles.p - cur_angles.p + F1_0; // -- old fashioned way -- delta_p = fixmul(delta_p, factor); // -- old fashioned way -- cur_angles.p += delta_p; // -- old fashioned way -- } // -- old fashioned way -- if (delta_h != 0) { // -- old fashioned way -- if (delta_h > F1_0/2) delta_h = dest_angles.h - cur_angles.h - F1_0; // -- old fashioned way -- if (delta_h < -F1_0/2) delta_h = dest_angles.h - cur_angles.h + F1_0; // -- old fashioned way -- delta_h = fixmul(delta_h, factor); // -- old fashioned way -- cur_angles.h += delta_h; // -- old fashioned way -- } // -- old fashioned way -- if (delta_b != 0) { // -- old fashioned way -- if (delta_b > F1_0/2) delta_b = dest_angles.b - cur_angles.b - F1_0; // -- old fashioned way -- if (delta_b < -F1_0/2) delta_b = dest_angles.b - cur_angles.b + F1_0; // -- old fashioned way -- delta_b = fixmul(delta_b, factor); // -- old fashioned way -- cur_angles.b += delta_b; // -- old fashioned way -- } } // Interpolate the object position. This is just straight linear // interpolation. delta_x = Objects[j].pos.x - cur_objs[i].pos.x; delta_y = Objects[j].pos.y - cur_objs[i].pos.y; delta_z = Objects[j].pos.z - cur_objs[i].pos.z; delta_x = fixmul(delta_x, factor); delta_y = fixmul(delta_y, factor); delta_z = fixmul(delta_z, factor); cur_objs[i].pos.x += delta_x; cur_objs[i].pos.y += delta_y; cur_objs[i].pos.z += delta_z; // -- old fashioned way --// stuff the new angles back into the object structure // -- old fashioned way -- vm_angles_2_matrix(&(cur_objs[i].orient), &cur_angles); } } } // get back to original position in the demo file. Reread the current // frame information again to reset all of the object stuff not covered // with Highest_object_index and the object array (previously rendered // objects, etc....) newdemo_back_frames(1); newdemo_back_frames(1); if (newdemo_read_frame_information() == -1) newdemo_stop_playback(); Newdemo_vcr_state = ND_STATE_PLAYBACK; for (i = 0; i <= num_cur_objs; i++) memcpy(&(Objects[i]), &(cur_objs[i]), sizeof(object)); Highest_object_index = num_cur_objs; d_free(cur_objs); } void newdemo_playback_one_frame() { int frames_back, i, level; static fix base_interpol_time = 0; static fix d_recorded = 0; for (i = 0; i < MAX_PLAYERS; i++) if (Newdemo_players_cloaked & (1 << i)) Players[i].cloak_time = GameTime - (CLOAK_TIME_MAX / 2); if (Players[Player_num].flags & PLAYER_FLAGS_INVULNERABLE) Players[Player_num].invulnerable_time = GameTime - (INVULNERABLE_TIME_MAX / 2); if (Newdemo_vcr_state == ND_STATE_PAUSED) // render a frame or not return; if (Newdemo_vcr_state == ND_STATE_PLAYBACK) DoJasonInterpolate(nd_recorded_time); Control_center_destroyed = 0; Countdown_seconds_left = -1; PALETTE_FLASH_SET(0,0,0); //clear flash if ((Newdemo_vcr_state == ND_STATE_REWINDING) || (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD)) { level = Current_level_num; if (NewdemoFrameCount == 0) return; else if ((Newdemo_vcr_state == ND_STATE_REWINDING) && (NewdemoFrameCount < 10)) { newdemo_goto_beginning(); return; } if (Newdemo_vcr_state == ND_STATE_REWINDING) frames_back = 10; else frames_back = 1; if (Newdemo_at_eof) { cfseek(infile, 11, SEEK_CUR); } newdemo_back_frames(frames_back); if (level != Current_level_num) newdemo_pop_ctrlcen_triggers(); if (Newdemo_vcr_state == ND_STATE_ONEFRAMEBACKWARD) { if (level != Current_level_num) newdemo_back_frames(1); Newdemo_vcr_state = ND_STATE_PAUSED; } } else if (Newdemo_vcr_state == ND_STATE_FASTFORWARD) { if (!Newdemo_at_eof) { for (i = 0; i < 10; i++) { if (newdemo_read_frame_information() == -1) { if (Newdemo_at_eof) Newdemo_vcr_state = ND_STATE_PAUSED; else newdemo_stop_playback(); break; } } } else Newdemo_vcr_state = ND_STATE_PAUSED; } else if (Newdemo_vcr_state == ND_STATE_ONEFRAMEFORWARD) { if (!Newdemo_at_eof) { level = Current_level_num; if (newdemo_read_frame_information() == -1) { if (!Newdemo_at_eof) newdemo_stop_playback(); } if (level != Current_level_num) { if (newdemo_read_frame_information() == -1) { if (!Newdemo_at_eof) newdemo_stop_playback(); } } Newdemo_vcr_state = ND_STATE_PAUSED; } else Newdemo_vcr_state = ND_STATE_PAUSED; } else { // First, uptate the total playback time to date. Then we check to see // if we need to change the playback style to interpolate frames or // skip frames based on where the playback time is relative to the // recorded time. if (NewdemoFrameCount <= 0) nd_playback_total = nd_recorded_total; // baseline total playback time else nd_playback_total += FrameTime; if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10)) if ((nd_playback_total * INTERPOL_FACTOR) < nd_recorded_total) { playback_style = INTERPOLATE_PLAYBACK; nd_playback_total = nd_recorded_total + FrameTime; // baseline playback time base_interpol_time = nd_recorded_total; d_recorded = nd_recorded_time; // baseline delta recorded } if ((playback_style == NORMAL_PLAYBACK) && (NewdemoFrameCount > 10)) if (nd_playback_total > nd_recorded_total) playback_style = SKIP_PLAYBACK; if ((playback_style == INTERPOLATE_PLAYBACK) && Newdemo_do_interpolate) { fix d_play = 0; if (nd_recorded_total - nd_playback_total < FrameTime) { d_recorded = nd_recorded_total - nd_playback_total; while (nd_recorded_total - nd_playback_total < FrameTime) { object *cur_objs; int i, j, num_objs, level; num_objs = Highest_object_index; cur_objs = (object *)d_malloc(sizeof(object) * (num_objs + 1)); if (cur_objs == NULL) { Warning ("Couldn't get %d bytes for objects in interpolate playback\n", sizeof(object) * num_objs); break; } for (i = 0; i <= num_objs; i++) memcpy(&(cur_objs[i]), &(Objects[i]), sizeof(object)); level = Current_level_num; if (newdemo_read_frame_information() == -1) { d_free(cur_objs); newdemo_stop_playback(); return; } if (level != Current_level_num) { d_free(cur_objs); if (newdemo_read_frame_information() == -1) newdemo_stop_playback(); break; } // for each new object in the frame just read in, determine if there is // a corresponding object that we have been interpolating. If so, then // copy that interpolated object to the new Objects array so that the // interpolated position and orientation can be preserved. for (i = 0; i <= num_objs; i++) { for (j = 0; j <= Highest_object_index; j++) { if (cur_objs[i].signature == Objects[j].signature) { memcpy(&(Objects[j].orient), &(cur_objs[i].orient), sizeof(vms_matrix)); memcpy(&(Objects[j].pos), &(cur_objs[i].pos), sizeof(vms_vector)); break; } } } d_free(cur_objs); d_recorded += nd_recorded_time; base_interpol_time = nd_playback_total - FrameTime; } } d_play = nd_playback_total - base_interpol_time; interpolate_frame(d_play, d_recorded); return; } else { //mprintf ((0, "*")); if (newdemo_read_frame_information() == -1) { newdemo_stop_playback(); return; } if (playback_style == SKIP_PLAYBACK) { //mprintf ((0, ".")); while (nd_playback_total > nd_recorded_total) { if (newdemo_read_frame_information() == -1) { newdemo_stop_playback(); return; } } } } } } void newdemo_start_recording() { #ifdef WINDOWS Newdemo_size=GetFreeDiskSpace(); mprintf((0, "Free space = %d\n", Newdemo_size)); #else Newdemo_size = GetDiskFree(); #endif Newdemo_size -= 100000; if ((Newdemo_size+100000) < 2000000000) { if (((int)(Newdemo_size)) < 500000) { #ifndef MACINTOSH nm_messagebox(NULL, 1, TXT_OK, TXT_DEMO_NO_SPACE); #else nm_messagebox(NULL, 1, TXT_OK, "Not enough space on current\ndrive to start demo recording."); #endif return; } } Newdemo_num_written = 0; Newdemo_no_space=0; Newdemo_state = ND_STATE_RECORDING; outfile = cfopen(DEMO_FILENAME, "wb"); #if !defined(MACINTOSH) && !defined(_WIN32_WCE) if (outfile == NULL && errno == ENOENT) { //dir doesn't exist? #else if (outfile == NULL) { //dir doesn't exist and no errno on mac! #endif cfile_mkdir(DEMO_DIR); //try making directory outfile = cfopen(DEMO_FILENAME, "wb"); } if (outfile == NULL) { nm_messagebox(NULL, 1, TXT_OK, "Cannot open demo temp file"); Newdemo_state = ND_STATE_NORMAL; } else newdemo_record_start_demo(); } char demoname_allowed_chars[] = "azAZ09__--"; void newdemo_stop_recording() { newmenu_item m[6]; int l, exit; static char filename[15] = "", *s; static ubyte tmpcnt = 0; ubyte cloaked = 0; char fullname[15+FILENAME_LEN] = DEMO_DIR; unsigned short byte_count = 0; exit = 0; nd_write_byte(ND_EVENT_EOF); nd_write_short(frame_bytes_written - 1); if (Game_mode & GM_MULTI) { for (l = 0; l < N_players; l++) { if (Players[l].flags & PLAYER_FLAGS_CLOAKED) cloaked |= (1 << l); } nd_write_byte(cloaked); nd_write_byte(ND_EVENT_EOF); } else { nd_write_short(ND_EVENT_EOF); } nd_write_short(ND_EVENT_EOF); nd_write_int(ND_EVENT_EOF); byte_count += 10; // from frame_bytes_written nd_write_byte((sbyte)(f2ir(Players[Player_num].energy))); nd_write_byte((sbyte)(f2ir(Players[Player_num].shields))); nd_write_int(Players[Player_num].flags); // be sure players flags are set nd_write_byte((sbyte)Primary_weapon); nd_write_byte((sbyte)Secondary_weapon); byte_count += 8; for (l = 0; l < MAX_PRIMARY_WEAPONS; l++) nd_write_short((short)Players[Player_num].primary_ammo[l]); for (l = 0; l < MAX_SECONDARY_WEAPONS; l++) nd_write_short((short)Players[Player_num].secondary_ammo[l]); byte_count += (sizeof(short) * (MAX_PRIMARY_WEAPONS + MAX_SECONDARY_WEAPONS)); nd_write_byte(Players[Player_num].laser_level); byte_count++; if (Game_mode & GM_MULTI) { nd_write_byte((sbyte)N_players); byte_count++; for (l = 0; l < N_players; l++) { nd_write_string(Players[l].callsign); byte_count += (strlen(Players[l].callsign) + 2); nd_write_byte(Players[l].connected); if (Game_mode & GM_MULTI_COOP) { nd_write_int(Players[l].score); byte_count += 5; } else { nd_write_short((short)Players[l].net_killed_total); nd_write_short((short)Players[l].net_kills_total); byte_count += 5; } } } else { nd_write_int(Players[Player_num].score); byte_count += 4; } nd_write_short(byte_count); nd_write_byte(Current_level_num); nd_write_byte(ND_EVENT_EOF); l = cftell(outfile); cfclose(outfile); outfile = NULL; Newdemo_state = ND_STATE_NORMAL; gr_palette_load( gr_palette ); if (filename[0] != '\0') { int num, i = strlen(filename) - 1; char newfile[15]; while (isdigit(filename[i])) { i--; if (i == -1) break; } i++; num = atoi(&(filename[i])); num++; filename[i] = '\0'; sprintf (newfile, "%s%d", filename, num); strncpy(filename, newfile, 8); filename[8] = '\0'; } try_again: ; Newmenu_allowed_chars = demoname_allowed_chars; if (!Newdemo_no_space) { m[0].type=NM_TYPE_INPUT; m[0].text_len = 8; m[0].text = filename; exit = newmenu_do( NULL, TXT_SAVE_DEMO_AS, 1, &(m[0]), NULL ); } else if (Newdemo_no_space == 1) { m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_BAD; m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename; exit = newmenu_do( NULL, NULL, 2, m, NULL ); } else if (Newdemo_no_space == 2) { m[ 0].type = NM_TYPE_TEXT; m[ 0].text = TXT_DEMO_SAVE_NOSPACE; m[ 1].type = NM_TYPE_INPUT;m[ 1].text_len = 8; m[1].text = filename; exit = newmenu_do( NULL, NULL, 2, m, NULL ); } Newmenu_allowed_chars = NULL; if (exit == -2) { // got bumped out from network menu char save_file[7+FILENAME_LEN]; if (filename[0] != '\0') { strcpy(save_file, DEMO_DIR); strcat(save_file, filename); strcat(save_file, ".dem"); } else sprintf (save_file, "%stmp%d.dem", DEMO_DIR, tmpcnt++); cfile_delete(save_file); cfile_rename(DEMO_FILENAME, save_file); return; } if (exit == -1) { // pressed ESC cfile_delete(DEMO_FILENAME); // might as well remove the file return; // return without doing anything } if (filename[0]==0) //null string goto try_again; //check to make sure name is ok for (s=filename;*s;s++) if (!isalnum(*s) && *s!='_') { nm_messagebox1(NULL, NULL,1,TXT_CONTINUE, TXT_DEMO_USE_LETTERS); goto try_again; } if (Newdemo_no_space) strcat(fullname, m[1].text); else strcat(fullname, m[0].text); strcat(fullname, ".dem"); cfile_delete(fullname); cfile_rename(DEMO_FILENAME, fullname); } extern char AltHogDir[64]; extern char AltHogdir_initialized; //returns the number of demo files on the disk int newdemo_count_demos() { FILEFINDSTRUCT find; int NumFiles=0; if( !FileFindFirst( DEMO_DIR "*.dem", &find ) ) { do { NumFiles++; } while( !FileFindNext( &find ) ); FileFindClose(); } if ( AltHogdir_initialized ) { char search_name[PATH_MAX + 5]; strcpy(search_name, AltHogDir); strcat(search_name, "/" DEMO_DIR "*.dem"); if( !FileFindFirst( search_name, &find ) ) { do { NumFiles++; } while( !FileFindNext( &find ) ); FileFindClose(); } } return NumFiles; } void newdemo_start_playback(char * filename) { FILEFINDSTRUCT find; int rnd_demo = 0; char filename2[PATH_MAX+FILENAME_LEN] = DEMO_DIR; #ifdef NETWORK change_playernum_to(0); #endif First_time_playback=1; JasonPlaybackTotal=0; if (filename==NULL) { // Randomly pick a filename int NumFiles = 0, RandFileNum; rnd_demo = 1; NumFiles = newdemo_count_demos(); if ( NumFiles == 0 ) { return; // No files found! } RandFileNum = d_rand() % NumFiles; NumFiles = 0; if( !FileFindFirst( DEMO_DIR "*.dem", &find ) ) { do { if ( NumFiles==RandFileNum ) { filename = (char *)&find.name; break; } NumFiles++; } while( !FileFindNext( &find ) ); FileFindClose(); } if ( filename == NULL && AltHogdir_initialized ) { char search_name[PATH_MAX + 5]; strcpy(search_name, AltHogDir); strcat(search_name, "/" DEMO_DIR "*.dem"); if( !FileFindFirst( search_name, &find ) ) { do { if ( NumFiles==RandFileNum ) { filename = (char *)&find.name; break; } NumFiles++; } while( !FileFindNext( &find ) ); FileFindClose(); } } if ( filename==NULL) return; } if (!filename) return; strcat(filename2,filename); infile = cfopen(filename2, "rb"); if (infile==NULL) { mprintf( (0, "Error reading '%s'\n", filename )); return; } nd_bad_read = 0; #ifdef NETWORK change_playernum_to(0); // force playernum to 0 #endif strncpy(nd_save_callsign, Players[Player_num].callsign, CALLSIGN_LEN); Viewer = ConsoleObject = &Objects[0]; // play properly as if console player if (newdemo_read_demo_start(rnd_demo)) { cfclose(infile); return; } Game_mode = GM_NORMAL; Newdemo_state = ND_STATE_PLAYBACK; Newdemo_vcr_state = ND_STATE_PLAYBACK; Newdemo_old_cockpit = Cockpit_mode; Newdemo_size = cfilelength(infile); nd_bad_read = 0; Newdemo_at_eof = 0; NewdemoFrameCount = 0; Newdemo_players_cloaked = 0; playback_style = NORMAL_PLAYBACK; Function_mode = FMODE_GAME; Cockpit_3d_view[0] = CV_NONE; //turn off 3d views on cockpit Cockpit_3d_view[1] = CV_NONE; //turn off 3d views on cockpit newdemo_playback_one_frame(); // this one loads new level newdemo_playback_one_frame(); // get all of the objects to renderb game } void newdemo_stop_playback() { cfclose(infile); Newdemo_state = ND_STATE_NORMAL; #ifdef NETWORK change_playernum_to(0); //this is reality #endif strncpy(Players[Player_num].callsign, nd_save_callsign, CALLSIGN_LEN); Cockpit_mode = Newdemo_old_cockpit; Game_mode = GM_GAME_OVER; Function_mode = FMODE_MENU; longjmp(LeaveGame,0); // Exit game loop } #ifndef NDEBUG #define BUF_SIZE 16384 void newdemo_strip_frames(char *outname, int bytes_to_strip) { CFILE *outfile; char *buf; int total_size, bytes_done, read_elems, bytes_back; int trailer_start, loc1, loc2, stop_loc, bytes_to_read; short last_frame_length; bytes_done = 0; total_size = cfilelength(infile); outfile = cfopen(outname, "wb"); if (outfile == NULL) { newmenu_item m[1]; m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't open output file"; newmenu_do( NULL, NULL, 1, m, NULL ); newdemo_stop_playback(); return; } buf = d_malloc(BUF_SIZE); if (buf == NULL) { newmenu_item m[1]; m[ 0].type = NM_TYPE_TEXT; m[ 0].text = "Can't malloc output buffer"; newmenu_do( NULL, NULL, 1, m, NULL ); cfclose(outfile); newdemo_stop_playback(); return; } newdemo_goto_end(); trailer_start = cftell(infile); cfseek(infile, 11, SEEK_CUR); bytes_back = 0; while (bytes_back < bytes_to_strip) { loc1 = cftell(infile); //cfseek(infile, -10, SEEK_CUR); //nd_read_short(&last_frame_length); //cfseek(infile, 8 - last_frame_length, SEEK_CUR); newdemo_back_frames(1); loc2 = cftell(infile); bytes_back += (loc1 - loc2); } cfseek(infile, -10, SEEK_CUR); nd_read_short(&last_frame_length); cfseek(infile, -3, SEEK_CUR); stop_loc = cftell(infile); cfseek(infile, 0, SEEK_SET); while (stop_loc > 0) { if (stop_loc < BUF_SIZE) bytes_to_read = stop_loc; else bytes_to_read = BUF_SIZE; read_elems = cfread(buf, 1, bytes_to_read, infile); cfwrite(buf, 1, read_elems, outfile); stop_loc -= read_elems; } stop_loc = cftell(outfile); cfseek(infile, trailer_start, SEEK_SET); while ((read_elems = cfread(buf, 1, BUF_SIZE, infile)) != 0) cfwrite(buf, 1, read_elems, outfile); cfseek(outfile, stop_loc, SEEK_SET); cfseek(outfile, 1, SEEK_CUR); cfwrite(&last_frame_length, 2, 1, outfile); cfclose(outfile); newdemo_stop_playback(); } #endif object DemoRightExtra,DemoLeftExtra; ubyte DemoDoRight=0,DemoDoLeft=0; void nd_render_extras (ubyte which,object *obj) { ubyte w=which>>4; ubyte type=which&15; if (which==255) { Int3(); // how'd we get here? do_cockpit_window_view(w,NULL,0,WBU_WEAPON,NULL); return; } if (w) { memcpy (&DemoRightExtra,obj,sizeof(object)); DemoDoRight=type; } else { memcpy (&DemoLeftExtra,obj,sizeof(object)); DemoDoLeft=type; } } void DoJasonInterpolate (fix recorded_time) { fix the_delay; JasonPlaybackTotal+=FrameTime; if (!First_time_playback) { // get the difference between the recorded time and the playback time the_delay=(recorded_time - FrameTime); //mprintf ((0,"The delay=%d\n", f2i(the_delay))); if (the_delay >= f0_0) { stop_time(); timer_delay(the_delay); start_time(); } else { while (JasonPlaybackTotal > nd_recorded_total) if (newdemo_read_frame_information() == -1) { newdemo_stop_playback(); return; } //the_delay = nd_recorded_total - JasonPlaybackTotal; //if (the_delay > f0_0) // timer_delay(the_delay); } } First_time_playback=0; } #ifdef MACINTOSH #pragma global_optimizer reset #endif