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