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