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