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