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