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