]> icculus.org git repositories - btb/d2x.git/blob - main/endlevel.c
landscape fixes, implemented gr_check_mode
[btb/d2x.git] / main / endlevel.c
1 /* $Id: endlevel.c,v 1.20 2003-10-12 09:38:48 btb Exp $ */
2 /*
3 THE COMPUTER CODE CONTAINED HEREIN IS THE SOLE PROPERTY OF PARALLAX
4 SOFTWARE CORPORATION ("PARALLAX").  PARALLAX, IN DISTRIBUTING THE CODE TO
5 END-USERS, AND SUBJECT TO ALL OF THE TERMS AND CONDITIONS HEREIN, GRANTS A
6 ROYALTY-FREE, PERPETUAL LICENSE TO SUCH END-USERS FOR USE BY SUCH END-USERS
7 IN USING, DISPLAYING,  AND CREATING DERIVATIVE WORKS THEREOF, SO LONG AS
8 SUCH USE, DISPLAY OR CREATION IS FOR NON-COMMERCIAL, ROYALTY OR REVENUE
9 FREE PURPOSES.  IN NO EVENT SHALL THE END-USER USE THE COMPUTER CODE
10 CONTAINED HEREIN FOR REVENUE-BEARING PURPOSES.  THE END-USER UNDERSTANDS
11 AND AGREES TO THE TERMS HEREIN AND ACCEPTS THE SAME BY USE OF THIS FILE.
12 COPYRIGHT 1993-1999 PARALLAX SOFTWARE CORPORATION.  ALL RIGHTS RESERVED.
13 */
14
15 /*
16  *
17  * Code for rendering external scenes
18  *
19  * Old Log:
20  * Revision 1.5  1995/10/31  10:24:09  allender
21  * shareware stuff
22  *
23  * Revision 1.4  1995/09/14  16:33:54  allender
24  * fixed function return values for those that didn't
25  * have them...thanks matt!
26  *
27  * Revision 1.3  1995/07/28  15:36:26  allender
28  * reverse inverse sqrt change
29  *
30  * Revision 1.2  1995/07/28  15:17:40  allender
31  * inverse magnitude fixup
32  *
33  * Revision 1.1  1995/05/16  15:24:32  allender
34  * Initial revision
35  *
36  * Revision 2.2  1995/03/21  14:40:14  john
37  * Ifdef'd out the NETWORK code.
38  *
39  * Revision 2.1  1995/03/20  18:15:50  john
40  * Added code to not store the normals in the segment structure.
41  *
42  * Revision 2.0  1995/02/27  11:30:42  john
43  * New version 2.0, which has no anonymous unions, builds with
44  * Watcom 10.0, and doesn't require parsing BITMAPS.TBL.
45  *
46  * Revision 1.80  1995/02/22  13:24:45  john
47  * Removed the vecmat anonymous unions.
48  *
49  * Revision 1.79  1995/02/11  12:41:54  john
50  * Added new song method, with FM bank switching..
51  *
52  * Revision 1.78  1995/02/08  11:37:41  mike
53  * Check for failures in call to obj_create.
54  *
55  * Revision 1.77  1995/02/05  22:09:49  matt
56  * Switch out of rear view when starting endlevel sequence
57  *
58  * Revision 1.76  1995/01/30  18:08:28  rob
59  * Add palette fade out before ending level on special missions.
60  *
61  * Revision 1.75  1995/01/29  16:19:19  rob
62  * Fixed endlevel for custom missions.
63  *
64  * Revision 1.74  1995/01/26  12:18:10  rob
65  * Changed calling convention of network_do_frame.
66  *
67  * Revision 1.73  1995/01/21  16:50:03  matt
68  * Made endlevel work with new mission stuff
69  *
70  * Revision 1.72  1994/12/20  18:22:51  john
71  * Added code to support non-looping songs, and put
72  * it in for endlevel and credits.
73  *
74  * Revision 1.71  1994/12/15  12:23:58  matt
75  * Added check for failure to create camera object
76  *
77  * Revision 1.70  1994/12/15  03:05:28  matt
78  * Added error checking for NULL return from object_create_explosion()
79  *
80  * Revision 1.69  1994/12/12  21:41:38  matt
81  * Don't start endlevel if OF_SHOULD_BE_DEAD is set for player
82  *
83  * Revision 1.68  1994/12/12  15:44:54  rob
84  * Rolled back a change to endlevel_start that caused more bugs than
85  * it fixed.
86  *
87  * Revision 1.67  1994/12/12  12:08:33  rob
88  * IF a player is dead upon entering the tunnel, make them not dead.  Not perfect solution
89  * but avoids some last-minute weirdness we want to fix.  This should be revisited in new
90  * versions if possible!
91  *
92  * Revision 1.66  1994/12/11  22:02:13  allender
93  * made endlevel data loading work with .txb encoded format (made with
94  * compbit -i level0?.end -o level0?.txb)
95  *
96  * Revision 1.65  1994/12/11  20:32:47  matt
97  * Made camera transition happen 1/3 of the way through exit tunnel
98  *
99  * Revision 1.64  1994/12/08  20:56:27  john
100  * More cfile stuff.
101  *
102  * Revision 1.63  1994/12/07  17:00:52  rob
103  * Trying to fix homing tone warning when in exit tunnel.
104  *
105  * Revision 1.62  1994/12/06  13:24:47  matt
106  * Made exit model come out of bitmaps.tbl
107  *
108  * Revision 1.61  1994/12/06  12:06:22  matt
109  * Fixed/cleaned up satellite (planet/sun) code
110  *
111  * Revision 1.60  1994/12/05  13:37:12  adam
112  * removed slew-mode
113  *
114  * Revision 1.59  1994/12/05  12:49:37  matt
115  * Made satellite a rod (instead of a plane old non-rotating bitmap), and
116  * made the size settable in the .end file
117  *
118  * Revision 1.58  1994/12/04  21:40:00  matt
119  * Added explosion sounds
120  *
121  * Revision 1.57  1994/12/04  18:31:41  matt
122  * Wasn't coding planet position, causing it to disappear sometimes
123  *
124  * Revision 1.56  1994/12/04  14:30:26  john
125  * Added hooks for music..
126  *
127  * Revision 1.55  1994/12/04  13:53:52  matt
128  * Added code to make camera off-centered during lookback
129  *
130  * Revision 1.54  1994/12/04  12:30:18  matt
131  * Fixed slew for short sequence
132  *
133  * Revision 1.53  1994/12/03  19:28:10  matt
134  * Added alternate model for exit model after mine destruction
135  *
136  * Revision 1.52  1994/12/03  00:17:23  matt
137  * Made endlevel sequence cut off early
138  * Made exit model and bit explosion always plot last (after all terrain)
139  *
140  * Revision 1.51  1994/12/01  20:15:43  yuan
141  * Localization.
142  *
143  * Revision 1.50  1994/11/30  23:27:35  adam
144  * mucked around carelessly
145  *
146  * Revision 1.49  1994/11/28  21:50:37  mike
147  * optimizations.
148  *
149  * Revision 1.48  1994/11/28  00:12:05  allender
150  * took out demo code that was in at one time to record endlevel sequence.
151  * We are _not_ recording endlevel sequence
152  *
153  * Revision 1.47  1994/11/27  23:35:54  allender
154  * pause demo recording when starting endlevel sequence.  on demo playback,
155  * don't do endlevel at all.
156  *
157  * Revision 1.46  1994/11/27  23:13:59  matt
158  * Made changes for new mprintf calling convention
159  *
160  * Revision 1.45  1994/11/26  23:17:29  matt
161  * When camera leaves mine, bank it so it's level with the ground
162  *
163  * Revision 1.44  1994/11/23  16:52:13  rob
164  * Ended netgame endlevel sequence a bit earlier.
165  *
166  * Revision 1.43  1994/11/22  19:20:46  rob
167  * Modem support for secret levels.
168  *
169  * Revision 1.42  1994/11/22  12:11:03  rob
170  * Fixed bug - file handle left open in load_endlevel_data.
171  *
172  * Revision 1.41  1994/11/21  17:29:22  matt
173  * Cleaned up sequencing & game saving for secret levels
174  *
175  * Revision 1.40  1994/11/19  15:14:54  mike
176  * remove unused code and data
177  *
178  * Revision 1.39  1994/11/19  12:41:32  matt
179  * Added system to read endlevel data from file, and to make it work
180  * with any exit tunnel.
181  *
182  * Revision 1.38  1994/11/17  15:02:24  mike
183  * support new segment validation functions.
184  *
185  * Revision 1.37  1994/11/17  13:04:45  allender
186  * backout out newdemo changes
187  *
188  * Revision 1.35  1994/11/16  14:52:33  rob
189  * Commented out SLEW_ON on Matt's direction.
190  * Changed something to fix demo recording.
191  *
192  * Revision 1.34  1994/11/16  11:49:29  matt
193  * Added code to rotate terrain to match mine
194  *
195  * Revision 1.33  1994/11/14  17:54:54  allender
196  * on exit sequence during demo recording, force player exited from mine
197  * packet to all other network players
198  *
199  * Revision 1.32  1994/11/10  21:27:42  matt
200  * Took out printf's
201  *
202  * Revision 1.31  1994/11/10  14:02:24  matt
203  * Hacked in support for player ships with different textures
204  *
205  * Revision 1.30  1994/11/09  10:31:33  matt
206  * Don't create explosions if can't find seg to create them in
207  *
208  * Revision 1.29  1994/11/05  17:22:37  john
209  * Fixed lots of sequencing problems with newdemo stuff.
210  *
211  * Revision 1.28  1994/11/03  11:10:39  matt
212  * Fixed chase angles code
213  * Maybe other things, too.
214  *
215  * Revision 1.27  1994/10/30  20:09:21  matt
216  * For endlevel: added big explosion at tunnel exit; made lights in tunnel
217  * go out; made more explosions on walls.
218  *
219  * Revision 1.26  1994/10/28  16:37:50  allender
220  * stop demo recording when endlevel sequence activated
221  *
222  * Revision 1.25  1994/10/27  21:15:21  matt
223  * Added explosions in mine chasing player
224  *
225  * Revision 1.24  1994/10/27  01:03:57  matt
226  * Fixed several small bugs in flythrough
227  *
228  * Revision 1.23  1994/10/22  01:32:30  matt
229  * Don't start endlevel sequence if player dead
230  *
231  * Revision 1.22  1994/10/22  00:08:06  matt
232  * Fixed up problems with bonus & game sequencing
233  * Player doesn't get credit for hostages unless he gets them out alive
234  *
235  *
236  *
237  */
238
239 #ifdef HAVE_CONFIG_H
240 #include <conf.h>
241 #endif
242
243 #ifdef RCS
244 static char rcsid[] = "$Id: endlevel.c,v 1.20 2003-10-12 09:38:48 btb Exp $";
245 #endif
246
247 //#define SLEW_ON 1
248
249 //#define _MARK_ON
250
251 #include <stdlib.h>
252
253
254 #include <stdio.h>
255 #include <string.h>
256 #include <ctype.h> // for isspace
257
258 #include "fix.h"
259 #include "vecmat.h"
260 #include "gr.h"
261 #include "3d.h"
262 #include "error.h"
263 #include "palette.h"
264 #include "iff.h"
265 #include "mono.h"
266 #include "texmap.h"
267 #include "fvi.h"
268 #include "u_mem.h"
269 #include "sounds.h"
270
271 #include "inferno.h"
272 #include "endlevel.h"
273 #include "object.h"
274 #include "game.h"
275 #include "screens.h"
276 #include "gauges.h"
277 #include "wall.h"
278 #include "terrain.h"
279 #include "polyobj.h"
280 #include "bm.h"
281 #include "gameseq.h"
282 #include "newdemo.h"
283 #include "multi.h"
284 #include "vclip.h"
285 #include "fireball.h"
286 #ifdef NETWORK
287 #include "network.h"
288 #endif
289 #include "text.h"
290 #include "digi.h"
291 #include "cfile.h"
292 #include "compbit.h"
293 #include "songs.h"
294 #include "movie.h"
295 #include "render.h"
296
297 typedef struct flythrough_data {
298         object          *obj;
299         vms_angvec      angles;                 //orientation in angles
300         vms_vector      step;                           //how far in a second
301         vms_vector      angstep;                        //rotation per second
302         fix                     speed;                  //how fast object is moving
303         vms_vector      headvec;                        //where we want to be pointing
304         int                     first_time;             //flag for if first time through
305         fix                     offset_frac;    //how far off-center as portion of way
306         fix                     offset_dist;    //how far currently off-center
307 } flythrough_data;
308
309 //endlevel sequence states
310
311 #define EL_OFF                          0               //not in endlevel
312 #define EL_FLYTHROUGH   1               //auto-flythrough in tunnel
313 #define EL_LOOKBACK             2               //looking back at player
314 #define EL_OUTSIDE              3               //flying outside for a while
315 #define EL_STOPPED              4               //stopped, watching explosion
316 #define EL_PANNING              5               //panning around, watching player
317 #define EL_CHASING              6               //chasing player to station
318
319 #define SHORT_SEQUENCE  1               //if defined, end sequnce when panning starts
320 //#define STATION_ENABLED       1               //if defined, load & use space station model
321
322 int Endlevel_sequence = 0;
323
324 extern fix player_speed;
325
326 int transition_segnum,exit_segnum;
327
328 object *endlevel_camera;
329
330 #define FLY_SPEED i2f(50)
331
332 void do_endlevel_flythrough(int n);
333 void draw_stars();
334 int find_exit_side(object *obj);
335 void generate_starfield();
336 void start_endlevel_flythrough(int n,object *obj,fix speed);
337 void start_rendered_endlevel_sequence();
338
339 #ifdef D2_OEM
340 char movie_table[] =    {       'a','a','a','a','d','d','d','d' };
341 #else
342 char movie_table[] =    {       'a','b','c',
343                                                 #ifndef SHAREWARE
344                                                         'a',
345                                                         'd','f','d','f',
346                                                         'g','h','i','g',
347                                                         'j','k','l','j',
348                                                         'm','o','m','o',
349                                                         'p','q','p','q'
350                                                 #endif
351                                         };
352 #endif
353
354 #define N_MOVIES (sizeof(movie_table) / sizeof(*movie_table))
355
356 #ifndef SHAREWARE
357 #ifdef D2_OEM
358 char movie_table_secret[] = {'a','d'};
359 #else
360 char movie_table_secret[] = {'a','d','g','j','m','p'};
361 #endif
362 #define N_MOVIES_SECRET (sizeof(movie_table_secret) / sizeof(*movie_table_secret))
363 #else
364 #define N_MOVIES_SECRET 0
365 #endif
366
367
368 #define FLY_ACCEL i2f(5)
369
370 fix cur_fly_speed,desired_fly_speed;
371
372 extern int matt_find_connect_side(int seg0,int seg1);
373
374 grs_bitmap *satellite_bitmap,*station_bitmap,*terrain_bitmap;   //!!*exit_bitmap,
375 vms_vector satellite_pos,satellite_upvec;
376 //!!grs_bitmap **exit_bitmap_list[1];
377 int station_modelnum,exit_modelnum,destroyed_exit_modelnum;
378
379 vms_vector station_pos = {0xf8c4<<10,0x3c1c<<12,0x372<<10};
380
381 #ifdef STATION_ENABLED
382 grs_bitmap *station_bitmap;
383 grs_bitmap **station_bitmap_list[1];
384 int station_modelnum;
385 #endif
386
387 vms_vector mine_exit_point;
388 vms_vector mine_ground_exit_point;
389 vms_vector mine_side_exit_point;
390 vms_matrix mine_exit_orient;
391
392 int outside_mine;
393
394 grs_bitmap terrain_bm_instance;
395 grs_bitmap satellite_bm_instance;
396
397 //find delta between two angles
398 fixang delta_ang(fixang a,fixang b)
399 {
400         fixang delta0,delta1;
401
402         return (abs(delta0 = a - b) < abs(delta1 = b - a)) ? delta0 : delta1;
403
404 }
405
406 //return though which side of seg0 is seg1
407 int matt_find_connect_side(int seg0,int seg1)
408 {
409         segment *Seg=&Segments[seg0];
410         int i;
411
412         for (i=MAX_SIDES_PER_SEGMENT;i--;) if (Seg->children[i]==seg1) return i;
413
414         return -1;
415 }
416
417 extern int Kmatrix_nomovie_message;
418
419 #if defined(D2_OEM) || defined(COMPILATION)
420 #define MOVIE_REQUIRED 0
421 #else
422 #define MOVIE_REQUIRED 1
423 #endif
424
425 //returns movie played status.  see movie.h
426 int start_endlevel_movie()
427 {
428         char movie_name[] = "esa.mve";
429         int r;
430         ubyte save_pal[768];
431
432         //Assert(Current_mission_num == Builtin_mission_num);   //only play movie for built-in mission
433
434         //Assert(N_MOVIES >= Last_level);
435         //Assert(N_MOVIES_SECRET >= -Last_secret_level);
436
437         if (!is_D2_OEM)
438                 if (Current_level_num == Last_level)
439                         return 1;   //don't play movie
440
441         if (Current_level_num > 0)
442                 movie_name[2] = movie_table[Current_level_num-1];
443         else {
444                 #ifndef SHAREWARE
445                         return 0;       //no escapes for secret level
446                 #else
447                         Error("Invalid level number <%d>",Current_level_num);
448                 #endif
449         }
450
451         memcpy(save_pal,gr_palette,768);
452
453         #ifndef SHAREWARE
454                 r=PlayMovie(movie_name,(Game_mode & GM_MULTI)?0:MOVIE_REQUIRED);
455         #else
456                 return  0;      // movie not played for shareware
457         #endif
458
459         if (Newdemo_state == ND_STATE_PLAYBACK) {
460                 set_screen_mode(SCREEN_GAME);
461                 memcpy(gr_palette,save_pal,768);
462         }
463
464 #ifdef NETWORK
465    if (r==MOVIE_NOT_PLAYED && (Game_mode & GM_MULTI))
466           Kmatrix_nomovie_message=1;
467         else
468           Kmatrix_nomovie_message=0;
469 #endif
470
471         return (r);
472
473 }
474
475 void
476 free_endlevel_data()
477 {
478         if (terrain_bm_instance.bm_data)
479                 d_free(terrain_bm_instance.bm_data);
480
481         if (satellite_bm_instance.bm_data)
482                 d_free(satellite_bm_instance.bm_data);
483 }
484
485 void init_endlevel()
486 {
487         //##satellite_bitmap = bm_load("earth.bbm");
488         //##terrain_bitmap = bm_load("moon.bbm");
489         //##
490         //##load_terrain("matt5b.bbm");         //load bitmap as height array
491         //##//load_terrain("ttest2.bbm");               //load bitmap as height array
492
493         #ifdef STATION_ENABLED
494         station_bitmap = bm_load("steel3.bbm");
495         station_bitmap_list[0] = &station_bitmap;
496
497         station_modelnum = load_polygon_model("station.pof",1,station_bitmap_list,NULL);
498         #endif
499
500 //!!    exit_bitmap = bm_load("steel1.bbm");
501 //!!    exit_bitmap_list[0] = &exit_bitmap;
502
503 //!!    exit_modelnum = load_polygon_model("exit01.pof",1,exit_bitmap_list,NULL);
504 //!!    destroyed_exit_modelnum = load_polygon_model("exit01d.pof",1,exit_bitmap_list,NULL);
505
506         generate_starfield();
507
508         atexit(free_endlevel_data);
509
510         terrain_bm_instance.bm_data = satellite_bm_instance.bm_data = NULL;
511 }
512
513 object external_explosion;
514 int ext_expl_playing,mine_destroyed;
515
516 extern fix flash_scale;
517
518 vms_angvec exit_angles={-0xa00,0,0};
519
520 vms_matrix surface_orient;
521
522 int endlevel_data_loaded=0;
523 extern char last_palette_loaded[];
524
525 void start_endlevel_sequence()
526 {
527         int     i;
528         int movie_played = MOVIE_NOT_PLAYED;
529
530         if (Newdemo_state == ND_STATE_RECORDING)                // stop demo recording
531                 Newdemo_state = ND_STATE_PAUSED;
532
533         if (Newdemo_state == ND_STATE_PLAYBACK) {               // don't do this if in playback mode
534                 if (Current_mission_num == Builtin_mission_num) //only play movie for built-in mission
535                         start_endlevel_movie();
536                 strcpy(last_palette_loaded,"");         //force palette load next time
537                 return;
538         }
539
540         if (Player_is_dead || ConsoleObject->flags&OF_SHOULD_BE_DEAD)
541                 return;                         //don't start if dead!
542
543         //      Dematerialize Buddy!
544         for (i=0; i<=Highest_object_index; i++)
545                 if (Objects[i].type == OBJ_ROBOT)
546                         if (Robot_info[Objects[i].id].companion) {
547                                 object_create_explosion(Objects[i].segnum, &Objects[i].pos, F1_0*7/2, VCLIP_POWERUP_DISAPPEARANCE );
548                                 Objects[i].flags |= OF_SHOULD_BE_DEAD;
549                         }
550
551         Players[Player_num].homing_object_dist = -F1_0; // Turn off homing sound.
552
553         reset_rear_view();              //turn off rear view if set
554
555 #ifdef NETWORK
556         if (Game_mode & GM_MULTI) {
557                 multi_send_endlevel_start(0);
558                 network_do_frame(1, 1);
559         }
560 #endif
561
562         if (Current_mission_num == Builtin_mission_num) {
563                 // only play movie for built-in mission
564                 if (!(Game_mode & GM_MULTI))
565                         movie_played = start_endlevel_movie();
566         }
567
568         if (!(Game_mode & GM_MULTI) && (movie_played == MOVIE_NOT_PLAYED) && endlevel_data_loaded)
569         {   //don't have movie.  Do rendered sequence, if available
570                 int exit_models_loaded = 0;
571
572                 if (Piggy_hamfile_version < 3)
573                         exit_models_loaded = 1; // built-in for PC shareware
574
575                 else
576                         exit_models_loaded = load_exit_models();
577
578 #ifndef WINDOWS
579                 if (exit_models_loaded)
580                 {
581                         start_rendered_endlevel_sequence();
582                         return;
583                 }
584 #endif
585         }
586
587         //don't have movie or rendered sequence, fade out
588         gr_palette_fade_out(gr_palette, 32, 0);
589
590         PlayerFinishedLevel(0);         //done with level
591 }
592
593 static int cockpit_mode_save;
594
595 void start_rendered_endlevel_sequence()
596 {
597         int last_segnum,exit_side,tunnel_length;
598
599         {
600                 int segnum,old_segnum,entry_side,i;
601
602                 //count segments in exit tunnel
603
604                 old_segnum = ConsoleObject->segnum;
605                 exit_side = find_exit_side(ConsoleObject);
606                 segnum = Segments[old_segnum].children[exit_side];
607                 tunnel_length = 0;
608                 do {
609                         entry_side = matt_find_connect_side(segnum,old_segnum);
610                         exit_side = Side_opposite[entry_side];
611                         old_segnum = segnum;
612                         segnum = Segments[segnum].children[exit_side];
613                         tunnel_length++;
614                 } while (segnum >= 0);
615
616                 if (segnum != -2) {
617                         PlayerFinishedLevel(0);         //don't do special sequence
618                         return;
619                 }
620
621                 last_segnum = old_segnum;
622
623                 //now pick transition segnum 1/3 of the way in
624
625                 old_segnum = ConsoleObject->segnum;
626                 exit_side = find_exit_side(ConsoleObject);
627                 segnum = Segments[old_segnum].children[exit_side];
628                 i=tunnel_length/3;
629                 while (i--) {
630
631                         entry_side = matt_find_connect_side(segnum,old_segnum);
632                         exit_side = Side_opposite[entry_side];
633                         old_segnum = segnum;
634                         segnum = Segments[segnum].children[exit_side];
635                 }
636                 transition_segnum = segnum;
637
638         }
639
640         cockpit_mode_save = Cockpit_mode;
641
642         #ifdef NETWORK
643         if (Game_mode & GM_MULTI) {
644                 multi_send_endlevel_start(0);
645                 network_do_frame(1, 1);
646         }
647         #endif
648
649         #ifdef SHAREWARE
650         Assert(last_segnum == exit_segnum);
651         //      songs_play_song( SONG_ENDLEVEL, 0 );    // JTS: Until we get an exit song, just don't worry
652         #endif
653
654         Endlevel_sequence = EL_FLYTHROUGH;
655
656         ConsoleObject->movement_type = MT_NONE;                 //movement handled by flythrough
657         ConsoleObject->control_type = CT_NONE;
658
659         Game_suspended |= SUSP_ROBOTS;          //robots don't move
660
661         cur_fly_speed = desired_fly_speed = FLY_SPEED;
662
663         start_endlevel_flythrough(0,ConsoleObject,cur_fly_speed);               //initialize
664
665         HUD_init_message( TXT_EXIT_SEQUENCE );
666
667         outside_mine = ext_expl_playing = 0;
668
669         flash_scale = f1_0;
670
671         //init_endlevel();
672
673         mine_destroyed=0;
674
675 }
676
677 extern flythrough_data fly_objects[];
678
679 extern object *slew_obj;
680
681 vms_angvec player_angles,player_dest_angles;
682 vms_angvec camera_desired_angles,camera_cur_angles;
683
684 #define CHASE_TURN_RATE (0x4000/4)              //max turn per second
685
686 //returns bitmask of which angles are at dest. bits 0,1,2 = p,b,h
687 int chase_angles(vms_angvec *cur_angles,vms_angvec *desired_angles)
688 {
689         vms_angvec delta_angs,alt_angles,alt_delta_angs;
690         fix total_delta,alt_total_delta;
691         fix frame_turn;
692         int mask=0;
693
694         delta_angs.p = desired_angles->p - cur_angles->p;
695         delta_angs.h = desired_angles->h - cur_angles->h;
696         delta_angs.b = desired_angles->b - cur_angles->b;
697 //delta_angs.b = 0;
698
699 //printf("chasing angles...desired = %x %x %x, cur = %x %x %x   ",desired_angles->p,desired_angles->b,desired_angles->h,cur_angles->p,cur_angles->b,cur_angles->h);
700
701         total_delta = abs(delta_angs.p) + abs(delta_angs.b) + abs(delta_angs.h);
702
703         alt_angles.p = f1_0/2 - cur_angles->p;
704         alt_angles.b = cur_angles->b + f1_0/2;
705         alt_angles.h = cur_angles->h + f1_0/2;
706
707         alt_delta_angs.p = desired_angles->p - alt_angles.p;
708         alt_delta_angs.h = desired_angles->h - alt_angles.h;
709         alt_delta_angs.b = desired_angles->b - alt_angles.b;
710 //alt_delta_angs.b = 0;
711
712         alt_total_delta = abs(alt_delta_angs.p) + abs(alt_delta_angs.b) + abs(alt_delta_angs.h);
713
714 //printf("Total delta = %x, alt total_delta = %x\n",total_delta,alt_total_delta);
715
716         if (alt_total_delta < total_delta) {
717                 //mprintf((0,"FLIPPING ANGLES!\n"));
718                 //printf("FLIPPING ANGLES!\n");
719                 *cur_angles = alt_angles;
720                 delta_angs = alt_delta_angs;
721         }
722
723         frame_turn = fixmul(FrameTime,CHASE_TURN_RATE);
724
725         if (abs(delta_angs.p) < frame_turn) {
726                 cur_angles->p = desired_angles->p;
727                 mask |= 1;
728         }
729         else
730                 if (delta_angs.p > 0)
731                         cur_angles->p += frame_turn;
732                 else
733                         cur_angles->p -= frame_turn;
734
735         if (abs(delta_angs.b) < frame_turn) {
736                 cur_angles->b = desired_angles->b;
737                 mask |= 2;
738         }
739         else
740                 if (delta_angs.b > 0)
741                         cur_angles->b += frame_turn;
742                 else
743                         cur_angles->b -= frame_turn;
744 //cur_angles->b = 0;
745
746         if (abs(delta_angs.h) < frame_turn) {
747                 cur_angles->h = desired_angles->h;
748                 mask |= 4;
749         }
750         else
751                 if (delta_angs.h > 0)
752                         cur_angles->h += frame_turn;
753                 else
754                         cur_angles->h -= frame_turn;
755
756         return mask;
757 }
758
759 void stop_endlevel_sequence()
760 {
761         Interpolation_method = 0;
762
763         gr_palette_fade_out(gr_palette, 32, 0);
764
765         select_cockpit(cockpit_mode_save);
766
767         Endlevel_sequence = EL_OFF;
768
769         PlayerFinishedLevel(0);
770
771 }
772
773 #define VCLIP_BIG_PLAYER_EXPLOSION      58
774
775 //--unused-- vms_vector upvec = {0,f1_0,0};
776
777 //find the angle between the player's heading & the station
778 void get_angs_to_object(vms_angvec *av,vms_vector *targ_pos,vms_vector *cur_pos)
779 {
780         vms_vector tv;
781
782         vm_vec_sub(&tv,targ_pos,cur_pos);
783
784         vm_extract_angles_vector(av,&tv);
785 }
786
787 void do_endlevel_frame()
788 {
789         static fix timer;
790         static fix bank_rate;
791         vms_vector save_last_pos;
792         static fix explosion_wait1=0;
793         static fix explosion_wait2=0;
794         static fix ext_expl_halflife;
795
796         save_last_pos = ConsoleObject->last_pos;        //don't let move code change this
797         object_move_all();
798         ConsoleObject->last_pos = save_last_pos;
799
800         if (ext_expl_playing) {
801
802                 external_explosion.lifeleft -= FrameTime;
803                 do_explosion_sequence(&external_explosion);
804
805                 if (external_explosion.lifeleft < ext_expl_halflife)
806                         mine_destroyed = 1;
807
808                 if (external_explosion.flags & OF_SHOULD_BE_DEAD)
809                         ext_expl_playing = 0;
810         }
811
812         if (cur_fly_speed != desired_fly_speed) {
813                 fix delta = desired_fly_speed - cur_fly_speed;
814                 fix frame_accel = fixmul(FrameTime,FLY_ACCEL);
815
816                 if (abs(delta) < frame_accel)
817                         cur_fly_speed = desired_fly_speed;
818                 else
819                         if (delta > 0)
820                                 cur_fly_speed += frame_accel;
821                         else
822                                 cur_fly_speed -= frame_accel;
823         }
824
825         //do big explosions
826         if (!outside_mine) {
827
828                 if (Endlevel_sequence==EL_OUTSIDE) {
829                         vms_vector tvec;
830
831                         vm_vec_sub(&tvec,&ConsoleObject->pos,&mine_side_exit_point);
832
833                         if (vm_vec_dot(&tvec,&mine_exit_orient.fvec) > 0) {
834                                 object *tobj;
835
836                                 outside_mine = 1;
837
838                                 tobj = object_create_explosion(exit_segnum,&mine_side_exit_point,i2f(50),VCLIP_BIG_PLAYER_EXPLOSION);
839
840                                 if (tobj) {
841                                         external_explosion = *tobj;
842
843                                         tobj->flags |= OF_SHOULD_BE_DEAD;
844
845                                         flash_scale = 0;        //kill lights in mine
846
847                                         ext_expl_halflife = tobj->lifeleft;
848
849                                         ext_expl_playing = 1;
850                                 }
851
852                                 digi_link_sound_to_pos( SOUND_BIG_ENDLEVEL_EXPLOSION, exit_segnum, 0, &mine_side_exit_point, 0, i2f(3)/4 );
853                         }
854                 }
855
856                 //do explosions chasing player
857                 if ((explosion_wait1-=FrameTime) < 0) {
858                         vms_vector tpnt;
859                         int segnum;
860                         object *expl;
861                         static int sound_count;
862
863                         vm_vec_scale_add(&tpnt,&ConsoleObject->pos,&ConsoleObject->orient.fvec,-ConsoleObject->size*5);
864                         vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.rvec,(d_rand()-RAND_MAX/2)*15);
865                         vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.uvec,(d_rand()-RAND_MAX/2)*15);
866
867                         segnum = find_point_seg(&tpnt,ConsoleObject->segnum);
868
869                         if (segnum != -1) {
870                                 expl = object_create_explosion(segnum,&tpnt,i2f(20),VCLIP_BIG_PLAYER_EXPLOSION);
871                                 if (d_rand()<10000 || ++sound_count==7) {               //pseudo-random
872                                         digi_link_sound_to_pos( SOUND_TUNNEL_EXPLOSION, segnum, 0, &tpnt, 0, F1_0 );
873                                         sound_count=0;
874                                 }
875                         }
876
877                         explosion_wait1 = 0x2000 + d_rand()/4;
878
879                 }
880         }
881
882         //do little explosions on walls
883         if (Endlevel_sequence >= EL_FLYTHROUGH && Endlevel_sequence < EL_OUTSIDE)
884                 if ((explosion_wait2-=FrameTime) < 0) {
885                         vms_vector tpnt;
886                         fvi_query fq;
887                         fvi_info hit_data;
888
889                         //create little explosion on wall
890
891                         vm_vec_copy_scale(&tpnt,&ConsoleObject->orient.rvec,(d_rand()-RAND_MAX/2)*100);
892                         vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.uvec,(d_rand()-RAND_MAX/2)*100);
893                         vm_vec_add2(&tpnt,&ConsoleObject->pos);
894
895                         if (Endlevel_sequence == EL_FLYTHROUGH)
896                                 vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.fvec,d_rand()*200);
897                         else
898                                 vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.fvec,d_rand()*60);
899
900                         //find hit point on wall
901
902                         fq.p0 = &ConsoleObject->pos;
903                         fq.p1 = &tpnt;
904                         fq.startseg = ConsoleObject->segnum;
905                         fq.rad = 0;
906                         fq.thisobjnum = 0;
907                         fq.ignore_obj_list = NULL;
908                         fq.flags = 0;
909
910                         find_vector_intersection(&fq,&hit_data);
911
912                         if (hit_data.hit_type==HIT_WALL && hit_data.hit_seg!=-1)
913                                 object_create_explosion(hit_data.hit_seg,&hit_data.hit_pnt,i2f(3)+d_rand()*6,VCLIP_SMALL_EXPLOSION);
914
915                         explosion_wait2 = (0xa00 + d_rand()/8)/2;
916                 }
917
918         switch (Endlevel_sequence) {
919
920                 case EL_OFF: return;
921
922                 case EL_FLYTHROUGH: {
923
924                         do_endlevel_flythrough(0);
925
926                         if (ConsoleObject->segnum == transition_segnum) {
927
928                                 if ((Current_mission_num == Builtin_mission_num) &&
929                                         (start_endlevel_movie() != MOVIE_NOT_PLAYED))
930                                         stop_endlevel_sequence();
931                                 else {
932                                         int objnum;
933
934                                         //songs_play_song( SONG_ENDLEVEL, 0 );
935
936                                         Endlevel_sequence = EL_LOOKBACK;
937
938                                         objnum = obj_create(OBJ_CAMERA, 0,
939                                                             ConsoleObject->segnum,&ConsoleObject->pos,&ConsoleObject->orient,0,
940                                                             CT_NONE,MT_NONE,RT_NONE);
941
942                                         if (objnum == -1) { //can't get object, so abort
943                                                 mprintf((1, "Can't get object for endlevel sequence.  Aborting endlevel sequence.\n"));
944                                                 stop_endlevel_sequence();
945                                                 return;
946                                         }
947
948                                         Viewer = endlevel_camera = &Objects[objnum];
949
950                                         select_cockpit(CM_LETTERBOX);
951
952                                         fly_objects[1] = fly_objects[0];
953                                         fly_objects[1].obj = endlevel_camera;
954                                         fly_objects[1].speed = (5*cur_fly_speed)/4;
955                                         fly_objects[1].offset_frac = 0x4000;
956
957                                         vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,i2f(7));
958
959                                         timer=0x20000;
960
961                                 }
962                         }
963
964                         break;
965                 }
966
967
968                 case EL_LOOKBACK: {
969
970                         do_endlevel_flythrough(0);
971                         do_endlevel_flythrough(1);
972
973                         if (timer>0) {
974
975                                 timer -= FrameTime;
976
977                                 if (timer < 0)          //reduce speed
978                                         fly_objects[1].speed = fly_objects[0].speed;
979                         }
980
981                         if (endlevel_camera->segnum == exit_segnum) {
982                                 vms_angvec cam_angles,exit_seg_angles;
983
984                                 Endlevel_sequence = EL_OUTSIDE;
985
986                                 timer = i2f(2);
987
988                                 vm_vec_negate(&endlevel_camera->orient.fvec);
989                                 vm_vec_negate(&endlevel_camera->orient.rvec);
990
991                                 vm_extract_angles_matrix(&cam_angles,&endlevel_camera->orient);
992                                 vm_extract_angles_matrix(&exit_seg_angles,&mine_exit_orient);
993                                 bank_rate = (-exit_seg_angles.b - cam_angles.b)/2;
994
995                                 ConsoleObject->control_type = endlevel_camera->control_type = CT_NONE;
996
997 #ifdef SLEW_ON
998  slew_obj = endlevel_camera;
999 #endif
1000                         }
1001
1002                         break;
1003                 }
1004
1005                 case EL_OUTSIDE: {
1006                         #ifndef SLEW_ON
1007                         vms_angvec cam_angles;
1008                         #endif
1009
1010                         vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));
1011 #ifndef SLEW_ON
1012                         vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,fixmul(FrameTime,-2*cur_fly_speed));
1013                         vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.uvec,fixmul(FrameTime,-cur_fly_speed/10));
1014
1015                         vm_extract_angles_matrix(&cam_angles,&endlevel_camera->orient);
1016                         cam_angles.b += fixmul(bank_rate,FrameTime);
1017                         vm_angles_2_matrix(&endlevel_camera->orient,&cam_angles);
1018 #endif
1019
1020                         timer -= FrameTime;
1021
1022                         if (timer < 0) {
1023
1024                                 Endlevel_sequence = EL_STOPPED;
1025
1026                                 vm_extract_angles_matrix(&player_angles,&ConsoleObject->orient);
1027
1028                                 timer = i2f(3);
1029
1030                         }
1031
1032                         break;
1033                 }
1034
1035                 case EL_STOPPED: {
1036
1037                         get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos);
1038                         chase_angles(&player_angles,&player_dest_angles);
1039                         vm_angles_2_matrix(&ConsoleObject->orient,&player_angles);
1040
1041                         vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));
1042
1043                         timer -= FrameTime;
1044
1045                         if (timer < 0) {
1046
1047                                 #ifdef SLEW_ON
1048                                 slew_obj = endlevel_camera;
1049                                 _do_slew_movement(endlevel_camera,1,1);
1050                                 timer += FrameTime;             //make time stop
1051                                 break;
1052                                 #else
1053
1054                                 #ifdef SHORT_SEQUENCE
1055
1056                                 stop_endlevel_sequence();
1057
1058                                 #else
1059                                 Endlevel_sequence = EL_PANNING;
1060
1061                                 vm_extract_angles_matrix(&camera_cur_angles,&endlevel_camera->orient);
1062
1063
1064                                 timer = i2f(3);
1065
1066                                 if (Game_mode & GM_MULTI) { // try to skip part of the seq if multiplayer
1067                                         stop_endlevel_sequence();
1068                                         return;
1069                                 }
1070
1071                                 //mprintf((0,"Switching to pan...\n"));
1072                                 #endif          //SHORT_SEQUENCE
1073                                 #endif          //SLEW_ON
1074
1075                         }
1076                         break;
1077                 }
1078
1079                 #ifndef SHORT_SEQUENCE
1080                 case EL_PANNING: {
1081                         #ifndef SLEW_ON
1082                         int mask;
1083                         #endif
1084
1085                         get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos);
1086                         chase_angles(&player_angles,&player_dest_angles);
1087                         vm_angles_2_matrix(&ConsoleObject->orient,&player_angles);
1088                         vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));
1089
1090                         #ifdef SLEW_ON
1091                         _do_slew_movement(endlevel_camera,1,1);
1092                         #else
1093
1094                         get_angs_to_object(&camera_desired_angles,&ConsoleObject->pos,&endlevel_camera->pos);
1095                         mask = chase_angles(&camera_cur_angles,&camera_desired_angles);
1096                         vm_angles_2_matrix(&endlevel_camera->orient,&camera_cur_angles);
1097
1098                         if ((mask&5) == 5) {
1099
1100                                 vms_vector tvec;
1101
1102                                 Endlevel_sequence = EL_CHASING;
1103
1104                                 vm_vec_normalized_dir_quick(&tvec,&station_pos,&ConsoleObject->pos);
1105                                 vm_vector_2_matrix(&ConsoleObject->orient,&tvec,&surface_orient.uvec,NULL);
1106
1107                                 desired_fly_speed *= 2;
1108
1109                                 //mprintf((0,"Switching to chase...\n"));
1110
1111                         }
1112                         #endif
1113
1114                         break;
1115                 }
1116
1117                 case EL_CHASING: {
1118                         fix d,speed_scale;
1119
1120                         #ifdef SLEW_ON
1121                         _do_slew_movement(endlevel_camera,1,1);
1122                         #endif
1123
1124                         get_angs_to_object(&camera_desired_angles,&ConsoleObject->pos,&endlevel_camera->pos);
1125                         chase_angles(&camera_cur_angles,&camera_desired_angles);
1126
1127                         #ifndef SLEW_ON
1128                         vm_angles_2_matrix(&endlevel_camera->orient,&camera_cur_angles);
1129                         #endif
1130
1131                         d = vm_vec_dist_quick(&ConsoleObject->pos,&endlevel_camera->pos);
1132
1133                         speed_scale = fixdiv(d,i2f(0x20));
1134                         if (d<f1_0) d=f1_0;
1135
1136                         get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos);
1137                         chase_angles(&player_angles,&player_dest_angles);
1138                         vm_angles_2_matrix(&ConsoleObject->orient,&player_angles);
1139
1140                         vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));
1141                         #ifndef SLEW_ON
1142                         vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,fixmul(FrameTime,fixmul(speed_scale,cur_fly_speed)));
1143
1144                         if (vm_vec_dist(&ConsoleObject->pos,&station_pos) < i2f(10))
1145                                 stop_endlevel_sequence();
1146                         #endif
1147
1148                         break;
1149
1150                 }
1151                 #endif          //ifdef SHORT_SEQUENCE
1152
1153         }
1154 }
1155
1156
1157 #define MIN_D 0x100
1158
1159 //find which side to fly out of
1160 int find_exit_side(object *obj)
1161 {
1162         int i;
1163         vms_vector prefvec,segcenter,sidevec;
1164         fix best_val=-f2_0;
1165         int best_side;
1166         segment *pseg = &Segments[obj->segnum];
1167
1168         //find exit side
1169
1170         vm_vec_normalized_dir_quick(&prefvec,&obj->pos,&obj->last_pos);
1171
1172         compute_segment_center(&segcenter,pseg);
1173
1174         best_side=-1;
1175         for (i=MAX_SIDES_PER_SEGMENT;--i >= 0;) {
1176                 fix d;
1177
1178                 if (pseg->children[i]!=-1) {
1179
1180                         compute_center_point_on_side(&sidevec,pseg,i);
1181                         vm_vec_normalized_dir_quick(&sidevec,&sidevec,&segcenter);
1182                         d = vm_vec_dotprod(&sidevec,&prefvec);
1183
1184                         if (labs(d) < MIN_D) d=0;
1185
1186                         if (d > best_val) {best_val=d; best_side=i;}
1187
1188                 }
1189         }
1190
1191         Assert(best_side!=-1);
1192
1193         return best_side;
1194 }
1195
1196 extern fix Render_zoom;                                                 //the player's zoom factor
1197
1198 extern vms_vector Viewer_eye;   //valid during render
1199
1200 void draw_exit_model()
1201 {
1202         vms_vector model_pos;
1203         int f=15,u=0;   //21;
1204
1205         vm_vec_scale_add(&model_pos,&mine_exit_point,&mine_exit_orient.fvec,i2f(f));
1206         vm_vec_scale_add2(&model_pos,&mine_exit_orient.uvec,i2f(u));
1207
1208         draw_polygon_model(&model_pos,&mine_exit_orient,NULL,(mine_destroyed)?destroyed_exit_modelnum:exit_modelnum,0,f1_0,NULL,NULL);
1209
1210 }
1211
1212 int exit_point_bmx,exit_point_bmy;
1213
1214 fix satellite_size = i2f(400);
1215
1216 #define SATELLITE_DIST          i2f(1024)
1217 #define SATELLITE_WIDTH         satellite_size
1218 #define SATELLITE_HEIGHT        ((satellite_size*9)/4)          //((satellite_size*5)/2)
1219
1220 void render_external_scene(fix eye_offset)
1221 {
1222
1223         Viewer_eye = Viewer->pos;
1224
1225         if (eye_offset)
1226                 vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.rvec,eye_offset);
1227
1228         g3_set_view_matrix(&Viewer->pos,&Viewer->orient,Render_zoom);
1229
1230         //g3_draw_horizon(BM_XRGB(0,0,0),BM_XRGB(16,16,16));            //,-1);
1231         gr_clear_canvas(BM_XRGB(0,0,0));
1232
1233         g3_start_instance_matrix(&vmd_zero_vector,&surface_orient);
1234         draw_stars();
1235         g3_done_instance();
1236
1237         {       //draw satellite
1238
1239                 vms_vector delta;
1240                 g3s_point p,top_pnt;
1241
1242                 g3_rotate_point(&p,&satellite_pos);
1243                 g3_rotate_delta_vec(&delta,&satellite_upvec);
1244
1245                 g3_add_delta_vec(&top_pnt,&p,&delta);
1246
1247                 if (! (p.p3_codes & CC_BEHIND)) {
1248                         int save_im = Interpolation_method;
1249                         //p.p3_flags &= ~PF_PROJECTED;
1250                         //g3_project_point(&p);
1251                         if (! (p.p3_flags & PF_OVERFLOW)) {
1252                                 Interpolation_method = 0;
1253                                 //gr_bitmapm(f2i(p.p3_sx)-32,f2i(p.p3_sy)-32,satellite_bitmap);
1254                                 g3_draw_rod_tmap(satellite_bitmap,&p,SATELLITE_WIDTH,&top_pnt,SATELLITE_WIDTH,f1_0);
1255                                 Interpolation_method = save_im;
1256                         }
1257                 }
1258         }
1259
1260         #ifdef STATION_ENABLED
1261         draw_polygon_model(&station_pos,&vmd_identity_matrix,NULL,station_modelnum,0,f1_0,NULL,NULL);
1262         #endif
1263
1264         render_terrain(&mine_ground_exit_point,exit_point_bmx,exit_point_bmy);
1265
1266         draw_exit_model();
1267         if (ext_expl_playing)
1268                 draw_fireball(&external_explosion);
1269
1270         Lighting_on=0;
1271         render_object(ConsoleObject);
1272         Lighting_on=1;
1273 }
1274
1275 #define MAX_STARS 500
1276
1277 vms_vector stars[MAX_STARS];
1278
1279 void generate_starfield()
1280 {
1281         int i;
1282
1283         for (i=0;i<MAX_STARS;i++) {
1284
1285                 stars[i].x = (d_rand() - RAND_MAX/2) << 14;
1286                 stars[i].z = (d_rand() - RAND_MAX/2) << 14;
1287                 stars[i].y = (d_rand()/2) << 14;
1288
1289         }
1290 }
1291
1292 void draw_stars()
1293 {
1294         int i;
1295         int intensity=31;
1296         g3s_point p;
1297
1298         for (i=0;i<MAX_STARS;i++) {
1299
1300                 if ((i&63) == 0) {
1301                         gr_setcolor(BM_XRGB(intensity,intensity,intensity));
1302                         intensity-=3;
1303                 }
1304
1305                 //g3_rotate_point(&p,&stars[i]);
1306                 g3_rotate_delta_vec(&p.p3_vec,&stars[i]);
1307                 g3_code_point(&p);
1308
1309                 if (p.p3_codes == 0) {
1310
1311                         p.p3_flags &= ~PF_PROJECTED;
1312
1313                         g3_project_point(&p);
1314
1315                         gr_pixel(f2i(p.p3_sx),f2i(p.p3_sy));
1316                 }
1317         }
1318
1319 //@@    {
1320 //@@            vms_vector delta;
1321 //@@            g3s_point top_pnt;
1322 //@@
1323 //@@            g3_rotate_point(&p,&satellite_pos);
1324 //@@            g3_rotate_delta_vec(&delta,&satellite_upvec);
1325 //@@
1326 //@@            g3_add_delta_vec(&top_pnt,&p,&delta);
1327 //@@
1328 //@@            if (! (p.p3_codes & CC_BEHIND)) {
1329 //@@                    int save_im = Interpolation_method;
1330 //@@                    Interpolation_method = 0;
1331 //@@                    //p.p3_flags &= ~PF_PROJECTED;
1332 //@@                    g3_project_point(&p);
1333 //@@                    if (! (p.p3_flags & PF_OVERFLOW))
1334 //@@                            //gr_bitmapm(f2i(p.p3_sx)-32,f2i(p.p3_sy)-32,satellite_bitmap);
1335 //@@                            g3_draw_rod_tmap(satellite_bitmap,&p,SATELLITE_WIDTH,&top_pnt,SATELLITE_WIDTH,f1_0);
1336 //@@                    Interpolation_method = save_im;
1337 //@@            }
1338 //@@    }
1339
1340 }
1341
1342 void endlevel_render_mine(fix eye_offset)
1343 {
1344         int start_seg_num;
1345
1346         Viewer_eye = Viewer->pos;
1347
1348         if (Viewer->type == OBJ_PLAYER )
1349                 vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.fvec,(Viewer->size*3)/4);
1350
1351         if (eye_offset)
1352                 vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.rvec,eye_offset);
1353
1354         #ifdef EDITOR
1355         if (Function_mode==FMODE_EDITOR)
1356                 Viewer_eye = Viewer->pos;
1357         #endif
1358
1359         if (Endlevel_sequence >= EL_OUTSIDE) {
1360
1361                 start_seg_num = exit_segnum;
1362         }
1363         else {
1364                 start_seg_num = find_point_seg(&Viewer_eye,Viewer->segnum);
1365
1366                 if (start_seg_num==-1)
1367                         start_seg_num = Viewer->segnum;
1368         }
1369
1370         if (Endlevel_sequence == EL_LOOKBACK) {
1371                 vms_matrix headm,viewm;
1372                 vms_angvec angles = {0,0,0x7fff};
1373
1374                 vm_angles_2_matrix(&headm,&angles);
1375                 vm_matrix_x_matrix(&viewm,&Viewer->orient,&headm);
1376                 g3_set_view_matrix(&Viewer_eye,&viewm,Render_zoom);
1377         }
1378         else
1379                 g3_set_view_matrix(&Viewer_eye,&Viewer->orient,Render_zoom);
1380
1381         render_mine(start_seg_num,eye_offset, 0);
1382 }
1383
1384 void render_endlevel_frame(fix eye_offset)
1385 {
1386
1387         g3_start_frame();
1388
1389         if (Endlevel_sequence < EL_OUTSIDE)
1390                 endlevel_render_mine(eye_offset);
1391         else
1392                 render_external_scene(eye_offset);
1393
1394         g3_end_frame();
1395
1396 }
1397
1398
1399 ///////////////////////// copy of flythrough code for endlevel
1400
1401
1402 #define MAX_FLY_OBJECTS 2
1403
1404 flythrough_data fly_objects[MAX_FLY_OBJECTS];
1405
1406 flythrough_data *flydata;
1407
1408 int matt_find_connect_side(int seg0,int seg1);
1409
1410 void compute_segment_center(vms_vector *vp,segment *sp);
1411
1412 fixang delta_ang(fixang a,fixang b);
1413 fixang interp_angle(fixang dest,fixang src,fixang step);
1414
1415 #define DEFAULT_SPEED i2f(16)
1416
1417 #define MIN_D 0x100
1418
1419 //if speed is zero, use default speed
1420 void start_endlevel_flythrough(int n,object *obj,fix speed)
1421 {
1422         flydata = &fly_objects[n];
1423
1424         flydata->obj = obj;
1425
1426         flydata->first_time = 1;
1427
1428         flydata->speed = speed?speed:DEFAULT_SPEED;
1429
1430         flydata->offset_frac = 0;
1431 }
1432
1433 static vms_angvec *angvec_add2_scale(vms_angvec *dest,vms_vector *src,fix s)
1434 {
1435         dest->p += fixmul(src->x,s);
1436         dest->b  += fixmul(src->z,s);
1437         dest->h  += fixmul(src->y,s);
1438
1439         return dest;
1440 }
1441
1442 #define MAX_ANGSTEP     0x4000          //max turn per second
1443
1444 #define MAX_SLIDE_PER_SEGMENT 0x10000
1445
1446 void do_endlevel_flythrough(int n)
1447 {
1448         object *obj;
1449         segment *pseg;
1450         int old_player_seg;
1451
1452         flydata = &fly_objects[n];
1453         obj = flydata->obj;
1454         
1455         old_player_seg = obj->segnum;
1456
1457         //move the player for this frame
1458
1459         if (!flydata->first_time) {
1460
1461                 vm_vec_scale_add2(&obj->pos,&flydata->step,FrameTime);
1462                 angvec_add2_scale(&flydata->angles,&flydata->angstep,FrameTime);
1463
1464                 vm_angles_2_matrix(&obj->orient,&flydata->angles);
1465         }
1466
1467         //check new player seg
1468
1469         update_object_seg(obj);
1470         pseg = &Segments[obj->segnum];
1471
1472         if (flydata->first_time || obj->segnum != old_player_seg) {             //moved into new seg
1473                 vms_vector curcenter,nextcenter;
1474                 fix step_size,seg_time;
1475                 short entry_side,exit_side = -1;//what sides we entry and leave through
1476                 vms_vector dest_point;          //where we are heading (center of exit_side)
1477                 vms_angvec dest_angles;         //where we want to be pointing
1478                 vms_matrix dest_orient;
1479                 int up_side=0;
1480
1481                 entry_side=0;
1482
1483                 //find new exit side
1484
1485                 if (!flydata->first_time) {
1486
1487                         entry_side = matt_find_connect_side(obj->segnum,old_player_seg);
1488                         exit_side = Side_opposite[entry_side];
1489                 }
1490
1491                 if (flydata->first_time || entry_side==-1 || pseg->children[exit_side]==-1)
1492                         exit_side = find_exit_side(obj);
1493
1494                 {                                                                               //find closest side to align to
1495                         fix d,largest_d=-f1_0;
1496                         int i;
1497
1498                         for (i=0;i<6;i++) {
1499                                 #ifdef COMPACT_SEGS
1500                                 vms_vector v1;
1501                                 get_side_normal(pseg, i, 0, &v1 );
1502                                 d = vm_vec_dot(&v1,&flydata->obj->orient.uvec);
1503                                 #else
1504                                 d = vm_vec_dot(&pseg->sides[i].normals[0],&flydata->obj->orient.uvec);
1505                                 #endif
1506                                 if (d > largest_d) {largest_d = d; up_side=i;}
1507                         }
1508
1509                 }
1510
1511                 //update target point & angles
1512
1513                 compute_center_point_on_side(&dest_point,pseg,exit_side);
1514
1515                 //update target point and movement points
1516
1517                 //offset object sideways
1518                 if (flydata->offset_frac) {
1519                         int s0=-1,s1=0,i;
1520                         vms_vector s0p,s1p;
1521                         fix dist;
1522
1523                         for (i=0;i<6;i++)
1524                                 if (i!=entry_side && i!=exit_side && i!=up_side && i!=Side_opposite[up_side])
1525                                  {
1526                                         if (s0==-1)
1527                                                 s0 = i;
1528                                         else
1529                                                 s1 = i;
1530                                  }
1531
1532                         compute_center_point_on_side(&s0p,pseg,s0);
1533                         compute_center_point_on_side(&s1p,pseg,s1);
1534                         dist = fixmul(vm_vec_dist(&s0p,&s1p),flydata->offset_frac);
1535
1536                         if (dist-flydata->offset_dist > MAX_SLIDE_PER_SEGMENT)
1537                                 dist = flydata->offset_dist + MAX_SLIDE_PER_SEGMENT;
1538
1539                         flydata->offset_dist = dist;
1540
1541                         vm_vec_scale_add2(&dest_point,&obj->orient.rvec,dist);
1542
1543                 }
1544
1545                 vm_vec_sub(&flydata->step,&dest_point,&obj->pos);
1546                 step_size = vm_vec_normalize_quick(&flydata->step);
1547                 vm_vec_scale(&flydata->step,flydata->speed);
1548
1549                 compute_segment_center(&curcenter,pseg);
1550                 compute_segment_center(&nextcenter,&Segments[pseg->children[exit_side]]);
1551                 vm_vec_sub(&flydata->headvec,&nextcenter,&curcenter);
1552
1553                 #ifdef COMPACT_SEGS
1554                 {
1555                         vms_vector _v1;
1556                         get_side_normal(pseg, up_side, 0, &_v1 );
1557                         vm_vector_2_matrix(&dest_orient,&flydata->headvec,&_v1,NULL);
1558                 }
1559                 #else
1560                 vm_vector_2_matrix(&dest_orient,&flydata->headvec,&pseg->sides[up_side].normals[0],NULL);
1561                 #endif
1562                 vm_extract_angles_matrix(&dest_angles,&dest_orient);
1563
1564                 if (flydata->first_time)
1565                         vm_extract_angles_matrix(&flydata->angles,&obj->orient);
1566
1567                 seg_time = fixdiv(step_size,flydata->speed);    //how long through seg
1568
1569                 if (seg_time) {
1570                         flydata->angstep.x = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.p,dest_angles.p),seg_time)));
1571                         flydata->angstep.z = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.b,dest_angles.b),seg_time)));
1572                         flydata->angstep.y = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.h,dest_angles.h),seg_time)));
1573
1574                 }
1575                 else {
1576                         flydata->angles = dest_angles;
1577                         flydata->angstep.x = flydata->angstep.y = flydata->angstep.z = 0;
1578                 }
1579         }
1580
1581         flydata->first_time=0;
1582 }
1583
1584 #define JOY_NULL 15
1585 #define ROT_SPEED 8             //rate of rotation while key held down
1586 #define VEL_SPEED (15)  //rate of acceleration while key held down
1587
1588 extern short old_joy_x,old_joy_y;       //position last time around
1589
1590 #include "key.h"
1591 #include "joy.h"
1592
1593 #ifdef SLEW_ON          //this is a special routine for slewing around external scene
1594 int _do_slew_movement(object *obj, int check_keys, int check_joy )
1595 {
1596         int moved = 0;
1597         vms_vector svel, movement;                              //scaled velocity (per this frame)
1598         vms_matrix rotmat,new_pm;
1599         int joy_x,joy_y,btns;
1600         int joyx_moved,joyy_moved;
1601         vms_angvec rotang;
1602
1603         if (keyd_pressed[KEY_PAD5])
1604                 vm_vec_zero(&obj->phys_info.velocity);
1605
1606         if (check_keys) {
1607                 obj->phys_info.velocity.x += VEL_SPEED * (key_down_time(KEY_PAD9) - key_down_time(KEY_PAD7));
1608                 obj->phys_info.velocity.y += VEL_SPEED * (key_down_time(KEY_PADMINUS) - key_down_time(KEY_PADPLUS));
1609                 obj->phys_info.velocity.z += VEL_SPEED * (key_down_time(KEY_PAD8) - key_down_time(KEY_PAD2));
1610
1611                 rotang.pitch =  (key_down_time(KEY_LBRACKET) - key_down_time(KEY_RBRACKET))/ROT_SPEED;
1612                 rotang.bank  = (key_down_time(KEY_PAD1) - key_down_time(KEY_PAD3))/ROT_SPEED;
1613                 rotang.head  = (key_down_time(KEY_PAD6) - key_down_time(KEY_PAD4))/ROT_SPEED;
1614         }
1615         else
1616                 rotang.pitch = rotang.bank  = rotang.head  = 0;
1617
1618         //check for joystick movement
1619
1620         if (check_joy && joy_present)   {
1621                 joy_get_pos(&joy_x,&joy_y);
1622                 btns=joy_get_btns();
1623
1624                 joyx_moved = (abs(joy_x - old_joy_x)>JOY_NULL);
1625                 joyy_moved = (abs(joy_y - old_joy_y)>JOY_NULL);
1626
1627                 if (abs(joy_x) < JOY_NULL) joy_x = 0;
1628                 if (abs(joy_y) < JOY_NULL) joy_y = 0;
1629
1630                 if (btns)
1631                         if (!rotang.pitch) rotang.pitch = fixmul(-joy_y * 512,FrameTime); else;
1632                 else
1633                         if (joyy_moved) obj->phys_info.velocity.z = -joy_y * 8192;
1634
1635                 if (!rotang.head) rotang.head = fixmul(joy_x * 512,FrameTime);
1636
1637                 if (joyx_moved) old_joy_x = joy_x;
1638                 if (joyy_moved) old_joy_y = joy_y;
1639         }
1640
1641         moved = rotang.pitch | rotang.bank | rotang.head;
1642
1643         vm_angles_2_matrix(&rotmat,&rotang);
1644         vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
1645         obj->orient = new_pm;
1646         vm_transpose_matrix(&new_pm);           //make those columns rows
1647
1648         moved |= obj->phys_info.velocity.x | obj->phys_info.velocity.y | obj->phys_info.velocity.z;
1649
1650         svel = obj->phys_info.velocity;
1651         vm_vec_scale(&svel,FrameTime);          //movement in this frame
1652         vm_vec_rotate(&movement,&svel,&new_pm);
1653
1654         vm_vec_add2(&obj->pos,&movement);
1655
1656         moved |= (movement.x || movement.y || movement.z);
1657
1658         return moved;
1659 }
1660 #endif
1661
1662 #define LINE_LEN        80
1663 #define NUM_VARS        8
1664
1665 #define STATION_DIST    i2f(1024)
1666
1667 int convert_ext( char *dest, char *ext )
1668 {
1669         char *t;
1670
1671         t = strchr(dest,'.');
1672
1673         if (t && (t-dest <= 8)) {
1674                 t[1] = ext[0];                  
1675                 t[2] = ext[1];                  
1676                 t[3] = ext[2];  
1677                 return 1;
1678         }
1679         else
1680                 return 0;
1681 }
1682
1683 //called for each level to load & setup the exit sequence
1684 void load_endlevel_data(int level_num)
1685 {
1686         char filename[13];
1687         char line[LINE_LEN],*p;
1688         CFILE *ifile;
1689         int var,segnum,sidenum;
1690         int exit_side=0, i;
1691         int have_binary = 0;
1692
1693         endlevel_data_loaded = 0;               //not loaded yet
1694
1695 try_again:
1696         ;
1697
1698         if (level_num<0)                //secret level
1699                 strcpy(filename,Secret_level_names[-level_num-1]);
1700         else                                    //normal level
1701                 strcpy(filename,Level_names[level_num-1]);
1702
1703         if (!convert_ext(filename,"END"))
1704                 Error("Error converting filename <%s> for endlevel data\n",filename);
1705
1706         ifile = cfopen(filename,"rb");
1707
1708         if (!ifile) {
1709
1710                 convert_ext(filename,"txb");
1711
1712                 ifile = cfopen(filename,"rb");
1713
1714                 if (!ifile) {
1715                         if (level_num==1) {
1716                                 con_printf(CON_DEBUG, "Cannot load file text of binary version of <%s>\n",filename);
1717                                 endlevel_data_loaded = 0; // won't be able to play endlevel sequence
1718                                 return;
1719                         }
1720                         else {
1721                                 level_num = 1;
1722                                 goto try_again;
1723                         }
1724                 }
1725
1726                 have_binary = 1;
1727         }
1728
1729         //ok...this parser is pretty simple.  It ignores comments, but
1730         //everything else must be in the right place
1731
1732         var = 0;
1733
1734         while (cfgets(line,LINE_LEN,ifile)) {
1735
1736                 if (have_binary) {
1737                         for (i = 0; i < strlen(line) - 1; i++) {
1738                                 encode_rotate_left(&(line[i]));
1739                                 line[i] = line[i] ^ BITMAP_TBL_XOR;
1740                                 encode_rotate_left(&(line[i]));
1741                         }
1742                         p = line;
1743                 }
1744
1745                 if ((p=strchr(line,';'))!=NULL)
1746                         *p = 0;         //cut off comment
1747
1748                 for (p=line+strlen(line)-1;p>line && isspace(*p);*p--=0);
1749                 for (p=line;isspace(*p);p++);
1750
1751                 if (!*p)                //empty line
1752                         continue;
1753
1754                 switch (var) {
1755
1756                         case 0: {                                               //ground terrain
1757                                 int iff_error;
1758                                 ubyte pal[768];
1759
1760                                 if (terrain_bm_instance.bm_data)
1761                                         d_free(terrain_bm_instance.bm_data);
1762
1763                                 Assert(terrain_bm_instance.bm_data == NULL);
1764
1765                                 iff_error = iff_read_bitmap(p,&terrain_bm_instance,BM_LINEAR,pal);
1766                                 if (iff_error != IFF_NO_ERROR) {
1767                                         Warning("Can't load exit terrain from file %s: IFF error: %s",
1768                                                 p, iff_errormsg(iff_error));
1769                                         endlevel_data_loaded = 0; // won't be able to play endlevel sequence
1770                                         return;
1771                                 }
1772
1773                                 terrain_bitmap = &terrain_bm_instance;
1774
1775                                 gr_remap_bitmap_good( terrain_bitmap, pal, iff_transparent_color, -1);
1776
1777                                 break;
1778                         }
1779
1780                         case 1:                                                 //height map
1781
1782                                 load_terrain(p);
1783                                 break;
1784
1785
1786                         case 2:
1787
1788                                 sscanf(p,"%d,%d",&exit_point_bmx,&exit_point_bmy);
1789                                 break;
1790
1791                         case 3:                                                 //exit heading
1792
1793                                 exit_angles.h = i2f(atoi(p))/360;
1794                                 break;
1795
1796                         case 4: {                                               //planet bitmap
1797                                 int iff_error;
1798                                 ubyte pal[768];
1799
1800                                 if (satellite_bm_instance.bm_data)
1801                                         d_free(satellite_bm_instance.bm_data);
1802
1803                                 iff_error = iff_read_bitmap(p,&satellite_bm_instance,BM_LINEAR,pal);
1804                                 if (iff_error != IFF_NO_ERROR) {
1805                                         Warning("Can't load exit satellite from file %s: IFF error: %s",
1806                                                 p, iff_errormsg(iff_error));
1807                                         endlevel_data_loaded = 0; // won't be able to play endlevel sequence
1808                                         return;
1809                                 }
1810
1811                                 satellite_bitmap = &satellite_bm_instance;
1812                                 gr_remap_bitmap_good( satellite_bitmap, pal, iff_transparent_color, -1);
1813
1814                                 break;
1815                         }
1816
1817                         case 5:                                                 //earth pos
1818                         case 7: {                                               //station pos
1819                                 vms_matrix tm;
1820                                 vms_angvec ta;
1821                                 int pitch,head;
1822
1823                                 sscanf(p,"%d,%d",&head,&pitch);
1824
1825                                 ta.h = i2f(head)/360;
1826                                 ta.p = -i2f(pitch)/360;
1827                                 ta.b = 0;
1828
1829                                 vm_angles_2_matrix(&tm,&ta);
1830
1831                                 if (var==5)
1832                                         satellite_pos = tm.fvec;
1833                                         //vm_vec_copy_scale(&satellite_pos,&tm.fvec,SATELLITE_DIST);
1834                                 else
1835                                         station_pos = tm.fvec;
1836
1837                                 break;
1838                         }
1839
1840                         case 6:                                         //planet size
1841                                 satellite_size = i2f(atoi(p));
1842                                 break;
1843                 }
1844
1845                 var++;
1846
1847         }
1848
1849         Assert(var == NUM_VARS);
1850
1851
1852         // OK, now the data is loaded.  Initialize everything
1853
1854         //find the exit sequence by searching all segments for a side with
1855         //children == -2
1856
1857         for (segnum=0,exit_segnum=-1;exit_segnum==-1 && segnum<=Highest_segment_index;segnum++)
1858                 for (sidenum=0;sidenum<6;sidenum++)
1859                         if (Segments[segnum].children[sidenum] == -2) {
1860                                 exit_segnum = segnum;
1861                                 exit_side = sidenum;
1862                                 break;
1863                         }
1864
1865         Assert(exit_segnum!=-1);
1866
1867         compute_segment_center(&mine_exit_point,&Segments[exit_segnum]);
1868         extract_orient_from_segment(&mine_exit_orient,&Segments[exit_segnum]);
1869         compute_center_point_on_side(&mine_side_exit_point,&Segments[exit_segnum],exit_side);
1870
1871         vm_vec_scale_add(&mine_ground_exit_point,&mine_exit_point,&mine_exit_orient.uvec,-i2f(20));
1872
1873         //compute orientation of surface
1874         {
1875                 vms_vector tv;
1876                 vms_matrix exit_orient,tm;
1877
1878                 vm_angles_2_matrix(&exit_orient,&exit_angles);
1879                 vm_transpose_matrix(&exit_orient);
1880                 vm_matrix_x_matrix(&surface_orient,&mine_exit_orient,&exit_orient);
1881
1882                 vm_copy_transpose_matrix(&tm,&surface_orient);
1883                 vm_vec_rotate(&tv,&station_pos,&tm);
1884                 vm_vec_scale_add(&station_pos,&mine_exit_point,&tv,STATION_DIST);
1885
1886                 vm_vec_rotate(&tv,&satellite_pos,&tm);
1887                 vm_vec_scale_add(&satellite_pos,&mine_exit_point,&tv,SATELLITE_DIST);
1888
1889                 vm_vector_2_matrix(&tm,&tv,&surface_orient.uvec,NULL);
1890                 vm_vec_copy_scale(&satellite_upvec,&tm.uvec,SATELLITE_HEIGHT);
1891
1892
1893         }
1894
1895         cfclose(ifile);
1896
1897         endlevel_data_loaded = 1;
1898
1899 }