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