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.
19 static char rcsid[] = "$Id: endlevel.c,v 1.5 2001-11-09 06:54:23 bradleyb Exp $";
31 #include <ctype.h> // for isspace
72 typedef struct flythrough_data {
74 vms_angvec angles; //orientation in angles
75 vms_vector step; //how far in a second
76 vms_vector angstep; //rotation per second
77 fix speed; //how fast object is moving
78 vms_vector headvec; //where we want to be pointing
79 int first_time; //flag for if first time through
80 fix offset_frac; //how far off-center as portion of way
81 fix offset_dist; //how far currently off-center
84 //endlevel sequence states
86 #define EL_OFF 0 //not in endlevel
87 #define EL_FLYTHROUGH 1 //auto-flythrough in tunnel
88 #define EL_LOOKBACK 2 //looking back at player
89 #define EL_OUTSIDE 3 //flying outside for a while
90 #define EL_STOPPED 4 //stopped, watching explosion
91 #define EL_PANNING 5 //panning around, watching player
92 #define EL_CHASING 6 //chasing player to station
94 #define SHORT_SEQUENCE 1 //if defined, end sequnce when panning starts
95 //#define STATION_ENABLED 1 //if defined, load & use space station model
97 int Endlevel_sequence = 0;
99 extern fix player_speed;
101 int transition_segnum,exit_segnum;
103 object *endlevel_camera;
105 #define FLY_SPEED i2f(50)
107 void do_endlevel_flythrough(int n);
109 int find_exit_side(object *obj);
110 void generate_starfield();
111 void start_endlevel_flythrough(int n,object *obj,fix speed);
112 void start_rendered_endlevel_sequence();
115 char movie_table[] = { 'a','a','a','a','d','d','d','d' };
117 char movie_table[] = { 'a','b','c',
129 #define N_MOVIES (sizeof(movie_table) / sizeof(*movie_table))
133 char movie_table_secret[] = {'a','d'};
135 char movie_table_secret[] = {'a','d','g','j','m','p'};
137 #define N_MOVIES_SECRET (sizeof(movie_table_secret) / sizeof(*movie_table_secret))
139 #define N_MOVIES_SECRET 0
143 #define FLY_ACCEL i2f(5)
145 fix cur_fly_speed,desired_fly_speed;
147 extern int matt_find_connect_side(int seg0,int seg1);
149 grs_bitmap *satellite_bitmap,*station_bitmap,*terrain_bitmap; //!!*exit_bitmap,
150 vms_vector satellite_pos,satellite_upvec;
151 //!!grs_bitmap **exit_bitmap_list[1];
152 int station_modelnum,exit_modelnum,destroyed_exit_modelnum;
154 vms_vector station_pos = {0xf8c4<<10,0x3c1c<<12,0x372<<10};
156 #ifdef STATION_ENABLED
157 grs_bitmap *station_bitmap;
158 grs_bitmap **station_bitmap_list[1];
159 int station_modelnum;
162 vms_vector mine_exit_point;
163 vms_vector mine_ground_exit_point;
164 vms_vector mine_side_exit_point;
165 vms_matrix mine_exit_orient;
169 grs_bitmap terrain_bm_instance;
170 grs_bitmap satellite_bm_instance;
172 //find delta between two angles
173 fixang delta_ang(fixang a,fixang b)
175 fixang delta0,delta1;
177 return (abs(delta0 = a - b) < abs(delta1 = b - a)) ? delta0 : delta1;
181 //return though which side of seg0 is seg1
182 int matt_find_connect_side(int seg0,int seg1)
184 segment *Seg=&Segments[seg0];
187 for (i=MAX_SIDES_PER_SEGMENT;i--;) if (Seg->children[i]==seg1) return i;
192 extern int Kmatrix_nomovie_message;
194 #if defined(D2_OEM) || defined(COMPILATION)
195 #define MOVIE_REQUIRED 0
197 #define MOVIE_REQUIRED 1
200 //returns movie played status. see movie.h
201 int start_endlevel_movie()
203 char movie_name[] = "esa.mve";
207 Assert(Current_mission_num == 0); //only play movie for built-in mission
209 Assert(N_MOVIES >= Last_level);
210 Assert(N_MOVIES_SECRET >= -Last_secret_level);
213 if (Current_level_num == Last_level)
214 return 1; //don't play movie
217 if (Current_level_num > 0)
218 movie_name[2] = movie_table[Current_level_num-1];
221 return 0; //no escapes for secret level
223 Error("Invalid level number <%d>",Current_level_num);
227 memcpy(save_pal,gr_palette,768);
229 #if !defined(SHAREWARE) && !defined (NMOVIES)
230 r=PlayMovie(movie_name,(Game_mode & GM_MULTI)?0:MOVIE_REQUIRED);
232 return 0; // movie not played for shareware
235 if (Newdemo_state == ND_STATE_PLAYBACK) {
236 set_screen_mode(SCREEN_GAME);
237 memcpy(gr_palette,save_pal,768);
241 if (r==MOVIE_NOT_PLAYED && (Game_mode & GM_MULTI))
242 Kmatrix_nomovie_message=1;
244 Kmatrix_nomovie_message=0;
255 if (terrain_bm_instance.bm_data)
256 d_free(terrain_bm_instance.bm_data);
258 if (satellite_bm_instance.bm_data)
259 d_free(satellite_bm_instance.bm_data);
264 //##satellite_bitmap = bm_load("earth.bbm");
265 //##terrain_bitmap = bm_load("moon.bbm");
267 //##load_terrain("matt5b.bbm"); //load bitmap as height array
268 //##//load_terrain("ttest2.bbm"); //load bitmap as height array
270 #ifdef STATION_ENABLED
271 station_bitmap = bm_load("steel3.bbm");
272 station_bitmap_list[0] = &station_bitmap;
274 station_modelnum = load_polygon_model("station.pof",1,station_bitmap_list,NULL);
277 //!! exit_bitmap = bm_load("steel1.bbm");
278 //!! exit_bitmap_list[0] = &exit_bitmap;
280 //!! exit_modelnum = load_polygon_model("exit01.pof",1,exit_bitmap_list,NULL);
281 //!! destroyed_exit_modelnum = load_polygon_model("exit01d.pof",1,exit_bitmap_list,NULL);
283 generate_starfield();
285 atexit(free_endlevel_data);
287 terrain_bm_instance.bm_data = satellite_bm_instance.bm_data = NULL;
291 object external_explosion;
292 int ext_expl_playing,mine_destroyed;
294 extern fix flash_scale;
296 vms_angvec exit_angles={-0xa00,0,0};
298 vms_matrix surface_orient;
300 int endlevel_data_loaded=0;
301 extern char last_palette_loaded[];
303 void start_endlevel_sequence()
306 int movie_played = MOVIE_NOT_PLAYED;
307 #if defined(MACINTOSH) && defined(SHAREWARE)
308 static int inited = 0;
316 if (Newdemo_state == ND_STATE_RECORDING) // stop demo recording
317 Newdemo_state = ND_STATE_PAUSED;
319 if (Newdemo_state == ND_STATE_PLAYBACK) { // don't do this if in playback mode
320 if (Current_mission_num == 0) //only play movie for built-in mission
321 start_endlevel_movie();
322 strcpy(last_palette_loaded,""); //force palette load next time
326 if (Player_is_dead || ConsoleObject->flags&OF_SHOULD_BE_DEAD)
327 return; //don't start if dead!
329 // Dematerialize Buddy!
330 for (i=0; i<=Highest_object_index; i++)
331 if (Objects[i].type == OBJ_ROBOT)
332 if (Robot_info[Objects[i].id].companion) {
333 object_create_explosion(Objects[i].segnum, &Objects[i].pos, F1_0*7/2, VCLIP_POWERUP_DISAPPEARANCE );
334 Objects[i].flags |= OF_SHOULD_BE_DEAD;
337 Players[Player_num].homing_object_dist = -F1_0; // Turn off homing sound.
339 reset_rear_view(); //turn off rear view if set
342 if (Game_mode & GM_MULTI) {
343 multi_send_endlevel_start(0);
344 network_do_frame(1, 1);
348 if (Current_mission_num == 0) { //only play movie for built-in mission
350 //try playing movie. If it plays, great. if not, do rendered ending
352 if (!(Game_mode & GM_MULTI))
353 movie_played = start_endlevel_movie();
355 #if defined(SHAREWARE) || defined(NMOVIES)
356 if (movie_played == MOVIE_NOT_PLAYED) { //don't have movie. Do rendered sequence
358 start_rendered_endlevel_sequence();
366 gr_palette_fade_out(gr_palette, 32, 0);
368 PlayerFinishedLevel(0); //done with level
371 #if !defined(SHAREWARE) && !defined(NMOVIES)
373 void do_endlevel_frame() {Int3();}
374 void stop_endlevel_sequence() {Int3();}
375 void render_endlevel_frame(fix eye_offset) {Int3();}
379 static int cockpit_mode_save;
381 void start_rendered_endlevel_sequence()
383 int last_segnum,exit_side,tunnel_length;
386 int segnum,old_segnum,entry_side,i;
388 //count segments in exit tunnel
390 old_segnum = ConsoleObject->segnum;
391 exit_side = find_exit_side(ConsoleObject);
392 segnum = Segments[old_segnum].children[exit_side];
395 entry_side = matt_find_connect_side(segnum,old_segnum);
396 exit_side = Side_opposite[entry_side];
398 segnum = Segments[segnum].children[exit_side];
400 } while (segnum >= 0);
403 PlayerFinishedLevel(0); //don't do special sequence
407 last_segnum = old_segnum;
409 //now pick transition segnum 1/3 of the way in
411 old_segnum = ConsoleObject->segnum;
412 exit_side = find_exit_side(ConsoleObject);
413 segnum = Segments[old_segnum].children[exit_side];
417 entry_side = matt_find_connect_side(segnum,old_segnum);
418 exit_side = Side_opposite[entry_side];
420 segnum = Segments[segnum].children[exit_side];
422 transition_segnum = segnum;
426 cockpit_mode_save = Cockpit_mode;
429 if (Game_mode & GM_MULTI) {
430 multi_send_endlevel_start(0);
431 network_do_frame(1, 1);
436 Assert(last_segnum == exit_segnum);
437 // songs_play_song( SONG_ENDLEVEL, 0 ); // JTS: Until we get an exit song, just don't worry
440 Endlevel_sequence = EL_FLYTHROUGH;
442 ConsoleObject->movement_type = MT_NONE; //movement handled by flythrough
443 ConsoleObject->control_type = CT_NONE;
445 Game_suspended |= SUSP_ROBOTS; //robots don't move
447 cur_fly_speed = desired_fly_speed = FLY_SPEED;
449 start_endlevel_flythrough(0,ConsoleObject,cur_fly_speed); //initialize
451 HUD_init_message( TXT_EXIT_SEQUENCE );
453 outside_mine = ext_expl_playing = 0;
463 extern flythrough_data fly_objects[];
465 extern object *slew_obj;
467 vms_angvec player_angles,player_dest_angles;
468 vms_angvec camera_desired_angles,camera_cur_angles;
470 #define CHASE_TURN_RATE (0x4000/4) //max turn per second
472 //returns bitmask of which angles are at dest. bits 0,1,2 = p,b,h
473 int chase_angles(vms_angvec *cur_angles,vms_angvec *desired_angles)
475 vms_angvec delta_angs,alt_angles,alt_delta_angs;
476 fix total_delta,alt_total_delta;
480 delta_angs.p = desired_angles->p - cur_angles->p;
481 delta_angs.h = desired_angles->h - cur_angles->h;
482 delta_angs.b = desired_angles->b - cur_angles->b;
485 //printf("chasing angles...desired = %x %x %x, cur = %x %x %x ",desired_angles->p,desired_angles->b,desired_angles->h,cur_angles->p,cur_angles->b,cur_angles->h);
487 total_delta = abs(delta_angs.p) + abs(delta_angs.b) + abs(delta_angs.h);
489 alt_angles.p = f1_0/2 - cur_angles->p;
490 alt_angles.b = cur_angles->b + f1_0/2;
491 alt_angles.h = cur_angles->h + f1_0/2;
493 alt_delta_angs.p = desired_angles->p - alt_angles.p;
494 alt_delta_angs.h = desired_angles->h - alt_angles.h;
495 alt_delta_angs.b = desired_angles->b - alt_angles.b;
496 //alt_delta_angs.b = 0;
498 alt_total_delta = abs(alt_delta_angs.p) + abs(alt_delta_angs.b) + abs(alt_delta_angs.h);
500 //printf("Total delta = %x, alt total_delta = %x\n",total_delta,alt_total_delta);
502 if (alt_total_delta < total_delta) {
503 //mprintf((0,"FLIPPING ANGLES!\n"));
504 //printf("FLIPPING ANGLES!\n");
505 *cur_angles = alt_angles;
506 delta_angs = alt_delta_angs;
509 frame_turn = fixmul(FrameTime,CHASE_TURN_RATE);
511 if (abs(delta_angs.p) < frame_turn) {
512 cur_angles->p = desired_angles->p;
516 if (delta_angs.p > 0)
517 cur_angles->p += frame_turn;
519 cur_angles->p -= frame_turn;
521 if (abs(delta_angs.b) < frame_turn) {
522 cur_angles->b = desired_angles->b;
526 if (delta_angs.b > 0)
527 cur_angles->b += frame_turn;
529 cur_angles->b -= frame_turn;
532 if (abs(delta_angs.h) < frame_turn) {
533 cur_angles->h = desired_angles->h;
537 if (delta_angs.h > 0)
538 cur_angles->h += frame_turn;
540 cur_angles->h -= frame_turn;
545 void stop_endlevel_sequence()
547 Interpolation_method = 0;
549 gr_palette_fade_out(gr_palette, 32, 0);
551 select_cockpit(cockpit_mode_save);
553 Endlevel_sequence = EL_OFF;
555 PlayerFinishedLevel(0);
559 #define VCLIP_BIG_PLAYER_EXPLOSION 58
561 //--unused-- vms_vector upvec = {0,f1_0,0};
563 //find the angle between the player's heading & the station
564 void get_angs_to_object(vms_angvec *av,vms_vector *targ_pos,vms_vector *cur_pos)
568 vm_vec_sub(&tv,targ_pos,cur_pos);
570 vm_extract_angles_vector(av,&tv);
573 void do_endlevel_frame()
575 #if defined(SHAREWARE) || defined(NMOVIES)
577 static fix bank_rate;
579 vms_vector save_last_pos;
580 static fix explosion_wait1=0;
581 static fix explosion_wait2=0;
582 static fix ext_expl_halflife;
584 save_last_pos = ConsoleObject->last_pos; //don't let move code change this
586 ConsoleObject->last_pos = save_last_pos;
588 if (ext_expl_playing) {
590 external_explosion.lifeleft -= FrameTime;
591 do_explosion_sequence(&external_explosion);
593 if (external_explosion.lifeleft < ext_expl_halflife)
596 if (external_explosion.flags & OF_SHOULD_BE_DEAD)
597 ext_expl_playing = 0;
600 if (cur_fly_speed != desired_fly_speed) {
601 fix delta = desired_fly_speed - cur_fly_speed;
602 fix frame_accel = fixmul(FrameTime,FLY_ACCEL);
604 if (abs(delta) < frame_accel)
605 cur_fly_speed = desired_fly_speed;
608 cur_fly_speed += frame_accel;
610 cur_fly_speed -= frame_accel;
616 if (Endlevel_sequence==EL_OUTSIDE) {
619 vm_vec_sub(&tvec,&ConsoleObject->pos,&mine_side_exit_point);
621 if (vm_vec_dot(&tvec,&mine_exit_orient.fvec) > 0) {
626 tobj = object_create_explosion(exit_segnum,&mine_side_exit_point,i2f(50),VCLIP_BIG_PLAYER_EXPLOSION);
629 external_explosion = *tobj;
631 tobj->flags |= OF_SHOULD_BE_DEAD;
633 flash_scale = 0; //kill lights in mine
635 ext_expl_halflife = tobj->lifeleft;
637 ext_expl_playing = 1;
640 digi_link_sound_to_pos( SOUND_BIG_ENDLEVEL_EXPLOSION, exit_segnum, 0, &mine_side_exit_point, 0, i2f(3)/4 );
644 //do explosions chasing player
645 if ((explosion_wait1-=FrameTime) < 0) {
649 static int sound_count;
651 vm_vec_scale_add(&tpnt,&ConsoleObject->pos,&ConsoleObject->orient.fvec,-ConsoleObject->size*5);
652 vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.rvec,(d_rand()-RAND_MAX/2)*15);
653 vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.uvec,(d_rand()-RAND_MAX/2)*15);
655 segnum = find_point_seg(&tpnt,ConsoleObject->segnum);
658 expl = object_create_explosion(segnum,&tpnt,i2f(20),VCLIP_BIG_PLAYER_EXPLOSION);
659 if (d_rand()<10000 || ++sound_count==7) { //pseudo-random
660 digi_link_sound_to_pos( SOUND_TUNNEL_EXPLOSION, segnum, 0, &tpnt, 0, F1_0 );
665 explosion_wait1 = 0x2000 + d_rand()/4;
670 //do little explosions on walls
671 if (Endlevel_sequence >= EL_FLYTHROUGH && Endlevel_sequence < EL_OUTSIDE)
672 if ((explosion_wait2-=FrameTime) < 0) {
677 //create little explosion on wall
679 vm_vec_copy_scale(&tpnt,&ConsoleObject->orient.rvec,(d_rand()-RAND_MAX/2)*100);
680 vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.uvec,(d_rand()-RAND_MAX/2)*100);
681 vm_vec_add2(&tpnt,&ConsoleObject->pos);
683 if (Endlevel_sequence == EL_FLYTHROUGH)
684 vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.fvec,d_rand()*200);
686 vm_vec_scale_add2(&tpnt,&ConsoleObject->orient.fvec,d_rand()*60);
688 //find hit point on wall
690 fq.p0 = &ConsoleObject->pos;
692 fq.startseg = ConsoleObject->segnum;
695 fq.ignore_obj_list = NULL;
698 find_vector_intersection(&fq,&hit_data);
700 if (hit_data.hit_type==HIT_WALL && hit_data.hit_seg!=-1)
701 object_create_explosion(hit_data.hit_seg,&hit_data.hit_pnt,i2f(3)+d_rand()*6,VCLIP_SMALL_EXPLOSION);
703 explosion_wait2 = (0xa00 + d_rand()/8)/2;
706 switch (Endlevel_sequence) {
710 case EL_FLYTHROUGH: {
712 do_endlevel_flythrough(0);
714 if (ConsoleObject->segnum == transition_segnum) {
716 #if !defined(SHAREWARE) && !defined(NMOVIES)
717 start_endlevel_movie();
718 stop_endlevel_sequence();
723 //songs_play_song( SONG_ENDLEVEL, 0 );
725 Endlevel_sequence = EL_LOOKBACK;
727 objnum = obj_create(OBJ_CAMERA, 0,
728 ConsoleObject->segnum,&ConsoleObject->pos,&ConsoleObject->orient,0,
729 CT_NONE,MT_NONE,RT_NONE);
731 if (objnum == -1) { //can't get object, so abort
732 mprintf((1, "Can't get object for endlevel sequence. Aborting endlevel sequence.\n"));
733 stop_endlevel_sequence();
737 Viewer = endlevel_camera = &Objects[objnum];
739 select_cockpit(CM_LETTERBOX);
741 fly_objects[1] = fly_objects[0];
742 fly_objects[1].obj = endlevel_camera;
743 fly_objects[1].speed = (5*cur_fly_speed)/4;
744 fly_objects[1].offset_frac = 0x4000;
746 vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,i2f(7));
757 #if defined(SHAREWARE) || defined(NMOVIES)
760 do_endlevel_flythrough(0);
761 do_endlevel_flythrough(1);
767 if (timer < 0) //reduce speed
768 fly_objects[1].speed = fly_objects[0].speed;
771 if (endlevel_camera->segnum == exit_segnum) {
772 vms_angvec cam_angles,exit_seg_angles;
774 Endlevel_sequence = EL_OUTSIDE;
778 vm_vec_negate(&endlevel_camera->orient.fvec);
779 vm_vec_negate(&endlevel_camera->orient.rvec);
781 vm_extract_angles_matrix(&cam_angles,&endlevel_camera->orient);
782 vm_extract_angles_matrix(&exit_seg_angles,&mine_exit_orient);
783 bank_rate = (-exit_seg_angles.b - cam_angles.b)/2;
785 ConsoleObject->control_type = endlevel_camera->control_type = CT_NONE;
788 slew_obj = endlevel_camera;
797 vms_angvec cam_angles;
800 vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));
802 vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,fixmul(FrameTime,-2*cur_fly_speed));
803 vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.uvec,fixmul(FrameTime,-cur_fly_speed/10));
805 vm_extract_angles_matrix(&cam_angles,&endlevel_camera->orient);
806 cam_angles.b += fixmul(bank_rate,FrameTime);
807 vm_angles_2_matrix(&endlevel_camera->orient,&cam_angles);
814 Endlevel_sequence = EL_STOPPED;
816 vm_extract_angles_matrix(&player_angles,&ConsoleObject->orient);
827 get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos);
828 chase_angles(&player_angles,&player_dest_angles);
829 vm_angles_2_matrix(&ConsoleObject->orient,&player_angles);
831 vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));
838 slew_obj = endlevel_camera;
839 _do_slew_movement(endlevel_camera,1,1);
840 timer += FrameTime; //make time stop
844 #ifdef SHORT_SEQUENCE
846 stop_endlevel_sequence();
849 Endlevel_sequence = EL_PANNING;
851 vm_extract_angles_matrix(&camera_cur_angles,&endlevel_camera->orient);
856 if (Game_mode & GM_MULTI) { // try to skip part of the seq if multiplayer
857 stop_endlevel_sequence();
861 //mprintf((0,"Switching to pan...\n"));
862 #endif //SHORT_SEQUENCE
869 #ifndef SHORT_SEQUENCE
875 get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos);
876 chase_angles(&player_angles,&player_dest_angles);
877 vm_angles_2_matrix(&ConsoleObject->orient,&player_angles);
878 vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));
881 _do_slew_movement(endlevel_camera,1,1);
884 get_angs_to_object(&camera_desired_angles,&ConsoleObject->pos,&endlevel_camera->pos);
885 mask = chase_angles(&camera_cur_angles,&camera_desired_angles);
886 vm_angles_2_matrix(&endlevel_camera->orient,&camera_cur_angles);
892 Endlevel_sequence = EL_CHASING;
894 vm_vec_normalized_dir_quick(&tvec,&station_pos,&ConsoleObject->pos);
895 vm_vector_2_matrix(&ConsoleObject->orient,&tvec,&surface_orient.uvec,NULL);
897 desired_fly_speed *= 2;
899 //mprintf((0,"Switching to chase...\n"));
911 _do_slew_movement(endlevel_camera,1,1);
914 get_angs_to_object(&camera_desired_angles,&ConsoleObject->pos,&endlevel_camera->pos);
915 chase_angles(&camera_cur_angles,&camera_desired_angles);
918 vm_angles_2_matrix(&endlevel_camera->orient,&camera_cur_angles);
921 d = vm_vec_dist_quick(&ConsoleObject->pos,&endlevel_camera->pos);
923 speed_scale = fixdiv(d,i2f(0x20));
926 get_angs_to_object(&player_dest_angles,&station_pos,&ConsoleObject->pos);
927 chase_angles(&player_angles,&player_dest_angles);
928 vm_angles_2_matrix(&ConsoleObject->orient,&player_angles);
930 vm_vec_scale_add2(&ConsoleObject->pos,&ConsoleObject->orient.fvec,fixmul(FrameTime,cur_fly_speed));
932 vm_vec_scale_add2(&endlevel_camera->pos,&endlevel_camera->orient.fvec,fixmul(FrameTime,fixmul(speed_scale,cur_fly_speed)));
934 if (vm_vec_dist(&ConsoleObject->pos,&station_pos) < i2f(10))
935 stop_endlevel_sequence();
941 #endif //ifdef SHORT_SEQUENCE
950 //find which side to fly out of
951 int find_exit_side(object *obj)
954 vms_vector prefvec,segcenter,sidevec;
957 segment *pseg = &Segments[obj->segnum];
961 vm_vec_normalized_dir_quick(&prefvec,&obj->pos,&obj->last_pos);
963 compute_segment_center(&segcenter,pseg);
966 for (i=MAX_SIDES_PER_SEGMENT;--i >= 0;) {
969 if (pseg->children[i]!=-1) {
971 compute_center_point_on_side(&sidevec,pseg,i);
972 vm_vec_normalized_dir_quick(&sidevec,&sidevec,&segcenter);
973 d = vm_vec_dotprod(&sidevec,&prefvec);
975 if (labs(d) < MIN_D) d=0;
977 if (d > best_val) {best_val=d; best_side=i;}
982 Assert(best_side!=-1);
987 extern fix Render_zoom; //the player's zoom factor
989 extern vms_vector Viewer_eye; //valid during render
991 void draw_exit_model()
993 vms_vector model_pos;
996 vm_vec_scale_add(&model_pos,&mine_exit_point,&mine_exit_orient.fvec,i2f(f));
997 vm_vec_scale_add2(&model_pos,&mine_exit_orient.uvec,i2f(u));
999 draw_polygon_model(&model_pos,&mine_exit_orient,NULL,(mine_destroyed)?destroyed_exit_modelnum:exit_modelnum,0,f1_0,NULL,NULL);
1003 int exit_point_bmx,exit_point_bmy;
1005 fix satellite_size = i2f(400);
1007 #define SATELLITE_DIST i2f(1024)
1008 #define SATELLITE_WIDTH satellite_size
1009 #define SATELLITE_HEIGHT ((satellite_size*9)/4) //((satellite_size*5)/2)
1011 void render_external_scene(fix eye_offset)
1014 Viewer_eye = Viewer->pos;
1017 vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.rvec,eye_offset);
1019 g3_set_view_matrix(&Viewer->pos,&Viewer->orient,Render_zoom);
1021 //g3_draw_horizon(BM_XRGB(0,0,0),BM_XRGB(16,16,16)); //,-1);
1022 gr_clear_canvas(BM_XRGB(0,0,0));
1024 g3_start_instance_matrix(&vmd_zero_vector,&surface_orient);
1031 g3s_point p,top_pnt;
1033 g3_rotate_point(&p,&satellite_pos);
1034 g3_rotate_delta_vec(&delta,&satellite_upvec);
1036 g3_add_delta_vec(&top_pnt,&p,&delta);
1038 if (! (p.p3_codes & CC_BEHIND)) {
1039 int save_im = Interpolation_method;
1040 //p.p3_flags &= ~PF_PROJECTED;
1041 //g3_project_point(&p);
1042 if (! (p.p3_flags & PF_OVERFLOW)) {
1043 Interpolation_method = 0;
1044 //gr_bitmapm(f2i(p.p3_sx)-32,f2i(p.p3_sy)-32,satellite_bitmap);
1045 g3_draw_rod_tmap(satellite_bitmap,&p,SATELLITE_WIDTH,&top_pnt,SATELLITE_WIDTH,f1_0);
1046 Interpolation_method = save_im;
1051 #ifdef STATION_ENABLED
1052 draw_polygon_model(&station_pos,&vmd_identity_matrix,NULL,station_modelnum,0,f1_0,NULL,NULL);
1055 render_terrain(&mine_ground_exit_point,exit_point_bmx,exit_point_bmy);
1058 if (ext_expl_playing)
1059 draw_fireball(&external_explosion);
1062 render_object(ConsoleObject);
1066 #define MAX_STARS 500
1068 vms_vector stars[MAX_STARS];
1070 void generate_starfield()
1074 for (i=0;i<MAX_STARS;i++) {
1076 stars[i].x = (d_rand() - RAND_MAX/2) << 14;
1077 stars[i].z = (d_rand() - RAND_MAX/2) << 14;
1078 stars[i].y = (d_rand()/2) << 14;
1089 for (i=0;i<MAX_STARS;i++) {
1092 gr_setcolor(BM_XRGB(intensity,intensity,intensity));
1096 //g3_rotate_point(&p,&stars[i]);
1097 g3_rotate_delta_vec(&p.p3_vec,&stars[i]);
1100 if (p.p3_codes == 0) {
1102 p.p3_flags &= ~PF_PROJECTED;
1104 g3_project_point(&p);
1106 gr_pixel(f2i(p.p3_sx),f2i(p.p3_sy));
1111 //@@ vms_vector delta;
1112 //@@ g3s_point top_pnt;
1114 //@@ g3_rotate_point(&p,&satellite_pos);
1115 //@@ g3_rotate_delta_vec(&delta,&satellite_upvec);
1117 //@@ g3_add_delta_vec(&top_pnt,&p,&delta);
1119 //@@ if (! (p.p3_codes & CC_BEHIND)) {
1120 //@@ int save_im = Interpolation_method;
1121 //@@ Interpolation_method = 0;
1122 //@@ //p.p3_flags &= ~PF_PROJECTED;
1123 //@@ g3_project_point(&p);
1124 //@@ if (! (p.p3_flags & PF_OVERFLOW))
1125 //@@ //gr_bitmapm(f2i(p.p3_sx)-32,f2i(p.p3_sy)-32,satellite_bitmap);
1126 //@@ g3_draw_rod_tmap(satellite_bitmap,&p,SATELLITE_WIDTH,&top_pnt,SATELLITE_WIDTH,f1_0);
1127 //@@ Interpolation_method = save_im;
1133 void endlevel_render_mine(fix eye_offset)
1137 Viewer_eye = Viewer->pos;
1139 if (Viewer->type == OBJ_PLAYER )
1140 vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.fvec,(Viewer->size*3)/4);
1143 vm_vec_scale_add2(&Viewer_eye,&Viewer->orient.rvec,eye_offset);
1146 if (Function_mode==FMODE_EDITOR)
1147 Viewer_eye = Viewer->pos;
1150 if (Endlevel_sequence >= EL_OUTSIDE) {
1152 start_seg_num = exit_segnum;
1155 start_seg_num = find_point_seg(&Viewer_eye,Viewer->segnum);
1157 if (start_seg_num==-1)
1158 start_seg_num = Viewer->segnum;
1161 if (Endlevel_sequence == EL_LOOKBACK) {
1162 vms_matrix headm,viewm;
1163 vms_angvec angles = {0,0,0x7fff};
1165 vm_angles_2_matrix(&headm,&angles);
1166 vm_matrix_x_matrix(&viewm,&Viewer->orient,&headm);
1167 g3_set_view_matrix(&Viewer_eye,&viewm,Render_zoom);
1170 g3_set_view_matrix(&Viewer_eye,&Viewer->orient,Render_zoom);
1172 render_mine(start_seg_num,eye_offset, 0);
1175 void render_endlevel_frame(fix eye_offset)
1180 if (Endlevel_sequence < EL_OUTSIDE)
1181 endlevel_render_mine(eye_offset);
1184 render_external_scene(eye_offset);
1192 ///////////////////////// copy of flythrough code for endlevel
1195 #define MAX_FLY_OBJECTS 2
1197 flythrough_data fly_objects[MAX_FLY_OBJECTS];
1199 flythrough_data *flydata;
1201 int matt_find_connect_side(int seg0,int seg1);
1203 void compute_segment_center(vms_vector *vp,segment *sp);
1205 fixang delta_ang(fixang a,fixang b);
1206 fixang interp_angle(fixang dest,fixang src,fixang step);
1208 #define DEFAULT_SPEED i2f(16)
1212 //if speed is zero, use default speed
1213 void start_endlevel_flythrough(int n,object *obj,fix speed)
1215 flydata = &fly_objects[n];
1219 flydata->first_time = 1;
1221 flydata->speed = speed?speed:DEFAULT_SPEED;
1223 flydata->offset_frac = 0;
1226 static vms_angvec *angvec_add2_scale(vms_angvec *dest,vms_vector *src,fix s)
1228 dest->p += fixmul(src->x,s);
1229 dest->b += fixmul(src->z,s);
1230 dest->h += fixmul(src->y,s);
1235 #define MAX_ANGSTEP 0x4000 //max turn per second
1237 #define MAX_SLIDE_PER_SEGMENT 0x10000
1239 void do_endlevel_flythrough(int n)
1245 flydata = &fly_objects[n];
1248 old_player_seg = obj->segnum;
1250 //move the player for this frame
1252 if (!flydata->first_time) {
1254 vm_vec_scale_add2(&obj->pos,&flydata->step,FrameTime);
1255 angvec_add2_scale(&flydata->angles,&flydata->angstep,FrameTime);
1257 vm_angles_2_matrix(&obj->orient,&flydata->angles);
1260 //check new player seg
1262 update_object_seg(obj);
1263 pseg = &Segments[obj->segnum];
1265 if (flydata->first_time || obj->segnum != old_player_seg) { //moved into new seg
1266 vms_vector curcenter,nextcenter;
1267 fix step_size,seg_time;
1268 short entry_side,exit_side = -1;//what sides we entry and leave through
1269 vms_vector dest_point; //where we are heading (center of exit_side)
1270 vms_angvec dest_angles; //where we want to be pointing
1271 vms_matrix dest_orient;
1276 //find new exit side
1278 if (!flydata->first_time) {
1280 entry_side = matt_find_connect_side(obj->segnum,old_player_seg);
1281 exit_side = Side_opposite[entry_side];
1284 if (flydata->first_time || entry_side==-1 || pseg->children[exit_side]==-1)
1285 exit_side = find_exit_side(obj);
1287 { //find closest side to align to
1288 fix d,largest_d=-f1_0;
1294 get_side_normal(pseg, i, 0, &v1 );
1295 d = vm_vec_dot(&v1,&flydata->obj->orient.uvec);
1297 d = vm_vec_dot(&pseg->sides[i].normals[0],&flydata->obj->orient.uvec);
1299 if (d > largest_d) {largest_d = d; up_side=i;}
1304 //update target point & angles
1306 compute_center_point_on_side(&dest_point,pseg,exit_side);
1308 //update target point and movement points
1310 //offset object sideways
1311 if (flydata->offset_frac) {
1317 if (i!=entry_side && i!=exit_side && i!=up_side && i!=Side_opposite[up_side])
1325 compute_center_point_on_side(&s0p,pseg,s0);
1326 compute_center_point_on_side(&s1p,pseg,s1);
1327 dist = fixmul(vm_vec_dist(&s0p,&s1p),flydata->offset_frac);
1329 if (dist-flydata->offset_dist > MAX_SLIDE_PER_SEGMENT)
1330 dist = flydata->offset_dist + MAX_SLIDE_PER_SEGMENT;
1332 flydata->offset_dist = dist;
1334 vm_vec_scale_add2(&dest_point,&obj->orient.rvec,dist);
1338 vm_vec_sub(&flydata->step,&dest_point,&obj->pos);
1339 step_size = vm_vec_normalize_quick(&flydata->step);
1340 vm_vec_scale(&flydata->step,flydata->speed);
1342 compute_segment_center(&curcenter,pseg);
1343 compute_segment_center(&nextcenter,&Segments[pseg->children[exit_side]]);
1344 vm_vec_sub(&flydata->headvec,&nextcenter,&curcenter);
1349 get_side_normal(pseg, up_side, 0, &_v1 );
1350 vm_vector_2_matrix(&dest_orient,&flydata->headvec,&_v1,NULL);
1353 vm_vector_2_matrix(&dest_orient,&flydata->headvec,&pseg->sides[up_side].normals[0],NULL);
1355 vm_extract_angles_matrix(&dest_angles,&dest_orient);
1357 if (flydata->first_time)
1358 vm_extract_angles_matrix(&flydata->angles,&obj->orient);
1360 seg_time = fixdiv(step_size,flydata->speed); //how long through seg
1363 flydata->angstep.x = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.p,dest_angles.p),seg_time)));
1364 flydata->angstep.z = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.b,dest_angles.b),seg_time)));
1365 flydata->angstep.y = max(-MAX_ANGSTEP,min(MAX_ANGSTEP,fixdiv(delta_ang(flydata->angles.h,dest_angles.h),seg_time)));
1369 flydata->angles = dest_angles;
1370 flydata->angstep.x = flydata->angstep.y = flydata->angstep.z = 0;
1374 flydata->first_time=0;
1378 #define ROT_SPEED 8 //rate of rotation while key held down
1379 #define VEL_SPEED (15) //rate of acceleration while key held down
1381 extern short old_joy_x,old_joy_y; //position last time around
1386 #ifdef SLEW_ON //this is a special routine for slewing around external scene
1387 int _do_slew_movement(object *obj, int check_keys, int check_joy )
1390 vms_vector svel, movement; //scaled velocity (per this frame)
1391 vms_matrix rotmat,new_pm;
1392 int joy_x,joy_y,btns;
1393 int joyx_moved,joyy_moved;
1396 if (keyd_pressed[KEY_PAD5])
1397 vm_vec_zero(&obj->phys_info.velocity);
1400 obj->phys_info.velocity.x += VEL_SPEED * (key_down_time(KEY_PAD9) - key_down_time(KEY_PAD7));
1401 obj->phys_info.velocity.y += VEL_SPEED * (key_down_time(KEY_PADMINUS) - key_down_time(KEY_PADPLUS));
1402 obj->phys_info.velocity.z += VEL_SPEED * (key_down_time(KEY_PAD8) - key_down_time(KEY_PAD2));
1404 rotang.pitch = (key_down_time(KEY_LBRACKET) - key_down_time(KEY_RBRACKET))/ROT_SPEED;
1405 rotang.bank = (key_down_time(KEY_PAD1) - key_down_time(KEY_PAD3))/ROT_SPEED;
1406 rotang.head = (key_down_time(KEY_PAD6) - key_down_time(KEY_PAD4))/ROT_SPEED;
1409 rotang.pitch = rotang.bank = rotang.head = 0;
1411 //check for joystick movement
1413 if (check_joy && joy_present) {
1414 joy_get_pos(&joy_x,&joy_y);
1415 btns=joy_get_btns();
1417 joyx_moved = (abs(joy_x - old_joy_x)>JOY_NULL);
1418 joyy_moved = (abs(joy_y - old_joy_y)>JOY_NULL);
1420 if (abs(joy_x) < JOY_NULL) joy_x = 0;
1421 if (abs(joy_y) < JOY_NULL) joy_y = 0;
1424 if (!rotang.pitch) rotang.pitch = fixmul(-joy_y * 512,FrameTime); else;
1426 if (joyy_moved) obj->phys_info.velocity.z = -joy_y * 8192;
1428 if (!rotang.head) rotang.head = fixmul(joy_x * 512,FrameTime);
1430 if (joyx_moved) old_joy_x = joy_x;
1431 if (joyy_moved) old_joy_y = joy_y;
1434 moved = rotang.pitch | rotang.bank | rotang.head;
1436 vm_angles_2_matrix(&rotmat,&rotang);
1437 vm_matrix_x_matrix(&new_pm,&obj->orient,&rotmat);
1438 obj->orient = new_pm;
1439 vm_transpose_matrix(&new_pm); //make those columns rows
1441 moved |= obj->phys_info.velocity.x | obj->phys_info.velocity.y | obj->phys_info.velocity.z;
1443 svel = obj->phys_info.velocity;
1444 vm_vec_scale(&svel,FrameTime); //movement in this frame
1445 vm_vec_rotate(&movement,&svel,&new_pm);
1447 vm_vec_add2(&obj->pos,&movement);
1449 moved |= (movement.x || movement.y || movement.z);
1458 #define STATION_DIST i2f(1024)
1460 int convert_ext( char *dest, char *ext )
1464 t = strchr(dest,'.');
1466 if (t && (t-dest <= 8)) {
1476 //called for each level to load & setup the exit sequence
1477 void load_endlevel_data(int level_num)
1480 char line[LINE_LEN],*p;
1482 int var,segnum,sidenum;
1484 int have_binary = 0;
1486 endlevel_data_loaded = 0; //not loaded yet
1491 if (level_num<0) //secret level
1492 strcpy(filename,Secret_level_names[-level_num-1]);
1494 strcpy(filename,Level_names[level_num-1]);
1496 if (!convert_ext(filename,"END"))
1497 Error("Error converting filename <%s> for endlevel data\n",filename);
1499 ifile = cfopen(filename,"rb");
1503 convert_ext(filename,"txb");
1505 ifile = cfopen(filename,"rb");
1509 Error("Cannot load file text of binary version of <%s>",filename);
1520 //ok...this parser is pretty simple. It ignores comments, but
1521 //everything else must be in the right place
1525 while (cfgets(line,LINE_LEN,ifile)) {
1528 for (i = 0; i < strlen(line) - 1; i++) {
1529 encode_rotate_left(&(line[i]));
1530 line[i] = line[i] ^ BITMAP_TBL_XOR;
1531 encode_rotate_left(&(line[i]));
1536 if ((p=strchr(line,';'))!=NULL)
1537 *p = 0; //cut off comment
1539 for (p=line+strlen(line)-1;p>line && isspace(*p);*p--=0);
1540 for (p=line;isspace(*p);p++);
1542 if (!*p) //empty line
1547 case 0: { //ground terrain
1551 if (terrain_bm_instance.bm_data)
1552 d_free(terrain_bm_instance.bm_data);
1554 Assert(terrain_bm_instance.bm_data == NULL);
1556 iff_error = iff_read_bitmap(p,&terrain_bm_instance,BM_LINEAR,pal);
1557 if (iff_error != IFF_NO_ERROR) {
1558 mprintf((1, "File %s - IFF error: %s",p,iff_errormsg(iff_error)));
1559 Error("File %s - IFF error: %s",p,iff_errormsg(iff_error));
1562 terrain_bitmap = &terrain_bm_instance;
1564 gr_remap_bitmap_good( terrain_bitmap, pal, iff_transparent_color, -1);
1569 case 1: //height map
1577 sscanf(p,"%d,%d",&exit_point_bmx,&exit_point_bmy);
1580 case 3: //exit heading
1582 exit_angles.h = i2f(atoi(p))/360;
1585 case 4: { //planet bitmap
1589 if (satellite_bm_instance.bm_data)
1590 d_free(satellite_bm_instance.bm_data);
1592 iff_error = iff_read_bitmap(p,&satellite_bm_instance,BM_LINEAR,pal);
1593 if (iff_error != IFF_NO_ERROR) {
1594 mprintf((1, "File %s - IFF error: %s",p,iff_errormsg(iff_error)));
1595 Error("File %s - IFF error: %s",p,iff_errormsg(iff_error));
1598 satellite_bitmap = &satellite_bm_instance;
1599 gr_remap_bitmap_good( satellite_bitmap, pal, iff_transparent_color, -1);
1605 case 7: { //station pos
1610 sscanf(p,"%d,%d",&head,&pitch);
1612 ta.h = i2f(head)/360;
1613 ta.p = -i2f(pitch)/360;
1616 vm_angles_2_matrix(&tm,&ta);
1619 satellite_pos = tm.fvec;
1620 //vm_vec_copy_scale(&satellite_pos,&tm.fvec,SATELLITE_DIST);
1622 station_pos = tm.fvec;
1627 case 6: //planet size
1628 satellite_size = i2f(atoi(p));
1636 Assert(var == NUM_VARS);
1639 // OK, now the data is loaded. Initialize everything
1641 //find the exit sequence by searching all segments for a side with
1644 for (segnum=0,exit_segnum=-1;exit_segnum==-1 && segnum<=Highest_segment_index;segnum++)
1645 for (sidenum=0;sidenum<6;sidenum++)
1646 if (Segments[segnum].children[sidenum] == -2) {
1647 exit_segnum = segnum;
1648 exit_side = sidenum;
1652 Assert(exit_segnum!=-1);
1654 compute_segment_center(&mine_exit_point,&Segments[exit_segnum]);
1655 extract_orient_from_segment(&mine_exit_orient,&Segments[exit_segnum]);
1656 compute_center_point_on_side(&mine_side_exit_point,&Segments[exit_segnum],exit_side);
1658 vm_vec_scale_add(&mine_ground_exit_point,&mine_exit_point,&mine_exit_orient.uvec,-i2f(20));
1660 //compute orientation of surface
1663 vms_matrix exit_orient,tm;
1665 vm_angles_2_matrix(&exit_orient,&exit_angles);
1666 vm_transpose_matrix(&exit_orient);
1667 vm_matrix_x_matrix(&surface_orient,&mine_exit_orient,&exit_orient);
1669 vm_copy_transpose_matrix(&tm,&surface_orient);
1670 vm_vec_rotate(&tv,&station_pos,&tm);
1671 vm_vec_scale_add(&station_pos,&mine_exit_point,&tv,STATION_DIST);
1673 vm_vec_rotate(&tv,&satellite_pos,&tm);
1674 vm_vec_scale_add(&satellite_pos,&mine_exit_point,&tv,SATELLITE_DIST);
1676 vm_vector_2_matrix(&tm,&tv,&surface_orient.uvec,NULL);
1677 vm_vec_copy_scale(&satellite_upvec,&tm.uvec,SATELLITE_HEIGHT);
1684 endlevel_data_loaded = 1;