2 * Elite - The New Kind.
4 * Reverse engineered from the BBC disk version of Elite.
5 * Additional material by C.J.Pinder.
7 * The original Elite code is (C) I.Bell & D.Braben 1984.
8 * This version re-engineered in C by C.J.Pinder 1999-2001.
10 * email: <christian@newkind.co.uk>
18 * This module handles all the flight system and management of the space universe.
47 extern int flight_climb;
48 extern int flight_roll;
49 extern int flight_speed;
51 struct galaxy_seed destination_planet;
63 void rotate_x_first (double *a, double *b, int direction)
72 *a = fx - (fx / 512) + (ux / 19);
73 *b = ux - (ux / 512) - (fx / 19);
77 *a = fx - (fx / 512) - (ux / 19);
78 *b = ux - (ux / 512) + (fx / 19);
83 void rotate_vec (struct vector *vec, double alpha, double beta)
103 * Update an objects location in the universe.
106 void move_univ_object (struct univ_object *obj)
115 alpha = flight_roll / 256.0;
116 beta = flight_climb / 256.0;
122 if (!(obj->flags & FLG_DEAD))
124 if (obj->velocity != 0)
126 speed = obj->velocity;
128 x += obj->rotmat[2].x * speed;
129 y += obj->rotmat[2].y * speed;
130 z += obj->rotmat[2].z * speed;
133 if (obj->acceleration != 0)
135 obj->velocity += obj->acceleration;
136 obj->acceleration = 0;
137 if (obj->velocity > ship_list[obj->type]->velocity)
138 obj->velocity = ship_list[obj->type]->velocity;
140 if (obj->velocity <= 0)
150 z = z - flight_speed;
156 obj->distance = sqrt (x*x + y*y + z*z);
158 if (obj->type == SHIP_PLANET)
161 rotate_vec (&obj->rotmat[2], alpha, beta);
162 rotate_vec (&obj->rotmat[1], alpha, beta);
163 rotate_vec (&obj->rotmat[0], alpha, beta);
165 if (obj->flags & FLG_DEAD)
172 /* If necessary rotate the object around the X axis... */
176 rotate_x_first (&obj->rotmat[2].x, &obj->rotmat[1].x, rotx);
177 rotate_x_first (&obj->rotmat[2].y, &obj->rotmat[1].y, rotx);
178 rotate_x_first (&obj->rotmat[2].z, &obj->rotmat[1].z, rotx);
180 if ((rotx != 127) && (rotx != -127))
181 obj->rotx -= (rotx < 0) ? -1 : 1;
185 /* If necessary rotate the object around the Z axis... */
189 rotate_x_first (&obj->rotmat[0].x, &obj->rotmat[1].x, rotz);
190 rotate_x_first (&obj->rotmat[0].y, &obj->rotmat[1].y, rotz);
191 rotate_x_first (&obj->rotmat[0].z, &obj->rotmat[1].z, rotz);
193 if ((rotz != 127) && (rotz != -127))
194 obj->rotz -= (rotz < 0) ? -1 : 1;
198 /* Orthonormalize the rotation matrix... */
200 tidy_matrix (obj->rotmat);
205 * Dock the player into the space station.
208 void dock_player (void)
210 disengage_auto_pilot();
218 myship.altitude = 255;
225 * Check if we are correctly aligned to dock.
228 int is_docking (int sn)
234 if (auto_pilot) // Don't want it to kill anyone!
237 fz = universe[sn].rotmat[2].z;
242 vec = unit_vector (&universe[sn].location);
247 ux = universe[sn].rotmat[1].x;
262 void do_game_over (void)
264 snd_play_sample (SND_GAMEOVER);
269 void update_altitude (void)
274 myship.altitude = 255;
279 x = fabs(universe[0].location.x);
280 y = fabs(universe[0].location.y);
281 z = fabs(universe[0].location.z);
283 if ((x > 65535) || (y > 65535) || (z > 65535))
290 dist = (x * x) + (y * y) + (z * z);
311 myship.altitude = dist;
315 void update_cabin_temp (void)
325 if (ship_count[SHIP_CORIOLIS] || ship_count[SHIP_DODEC])
328 x = abs((int)universe[1].location.x);
329 y = abs((int)universe[1].location.y);
330 z = abs((int)universe[1].location.z);
332 if ((x > 65535) || (y > 65535) || (z > 65535))
339 dist = ((x * x) + (y * y) + (z * z)) / 256;
346 myship.cabtemp = dist + 30;
348 if (myship.cabtemp > 255)
350 myship.cabtemp = 255;
355 if ((myship.cabtemp < 224) || (cmdr.fuel_scoop == 0))
358 cmdr.fuel += flight_speed / 2;
359 if (cmdr.fuel > myship.max_fuel)
360 cmdr.fuel = myship.max_fuel;
362 info_message ("Fuel Scoop On");
368 * Regenerate the shields and the energy banks.
371 void regenerate_shields (void)
375 if (front_shield < 255)
381 if (aft_shield < 255)
389 energy += cmdr.energy_unit;
395 void decrease_energy (int amount)
405 * Deplete the shields. Drain the energy banks if the shields fail.
408 void damage_ship (int damage, int front)
412 if (damage <= 0) /* sanity check */
415 shield = front ? front_shield : aft_shield;
420 decrease_energy (shield);
425 front_shield = shield;
433 void make_station_appear (void)
440 px = universe[0].location.x;
441 py = universe[0].location.y;
442 pz = universe[0].location.z;
444 vec.x = (rand() & 32767) - 16384;
445 vec.y = (rand() & 32767) - 16384;
446 vec.z = rand() & 32767;
448 vec = unit_vector (&vec);
450 sx = px - vec.x * 65792;
451 sy = py - vec.y * 65792;
452 sz = pz - vec.z * 65792;
454 // set_init_matrix (rotmat);
462 rotmat[1].z = -vec.y;
468 tidy_matrix (rotmat);
470 add_new_station (sx, sy, sz, rotmat);
475 void check_docking (int i)
479 snd_play_sample (SND_DOCK);
481 current_screen = SCR_BREAK_PATTERN;
485 if (flight_speed >= 5)
492 damage_ship (5, universe[i].location.z > 0);
493 snd_play_sample (SND_CRASH);
497 void switch_to_view (struct univ_object *flip)
501 if ((current_screen == SCR_REAR_VIEW) ||
502 (current_screen == SCR_GAME_OVER))
504 flip->location.x = -flip->location.x;
505 flip->location.z = -flip->location.z;
507 flip->rotmat[0].x = -flip->rotmat[0].x;
508 flip->rotmat[0].z = -flip->rotmat[0].z;
510 flip->rotmat[1].x = -flip->rotmat[1].x;
511 flip->rotmat[1].z = -flip->rotmat[1].z;
513 flip->rotmat[2].x = -flip->rotmat[2].x;
514 flip->rotmat[2].z = -flip->rotmat[2].z;
519 if (current_screen == SCR_LEFT_VIEW)
521 tmp = flip->location.x;
522 flip->location.x = flip->location.z;
523 flip->location.z = -tmp;
528 tmp = flip->rotmat[0].x;
529 flip->rotmat[0].x = flip->rotmat[0].z;
530 flip->rotmat[0].z = -tmp;
532 tmp = flip->rotmat[1].x;
533 flip->rotmat[1].x = flip->rotmat[1].z;
534 flip->rotmat[1].z = -tmp;
536 tmp = flip->rotmat[2].x;
537 flip->rotmat[2].x = flip->rotmat[2].z;
538 flip->rotmat[2].z = -tmp;
542 if (current_screen == SCR_RIGHT_VIEW)
544 tmp = flip->location.x;
545 flip->location.x = -flip->location.z;
546 flip->location.z = tmp;
551 tmp = flip->rotmat[0].x;
552 flip->rotmat[0].x = -flip->rotmat[0].z;
553 flip->rotmat[0].z = tmp;
555 tmp = flip->rotmat[1].x;
556 flip->rotmat[1].x = -flip->rotmat[1].z;
557 flip->rotmat[1].z = tmp;
559 tmp = flip->rotmat[2].x;
560 flip->rotmat[2].x = -flip->rotmat[2].z;
561 flip->rotmat[2].z = tmp;
568 * Update all the objects in the universe and render them.
571 void update_universe (void)
577 struct univ_object flip;
582 for (i = 0; i < MAX_UNIV_OBJECTS; i++)
584 type = universe[i].type;
588 if (universe[i].flags & FLG_REMOVE)
590 if (type == SHIP_VIPER ||
591 !(universe[i].flags & FLG_TARGET))
592 cmdr.legal_status |= 64;
594 bounty = ship_list[type]->bounty;
596 if ((bounty != 0) && (!witchspace))
598 cmdr.credits += bounty;
599 sprintf (str, "Bounty: %d.%d CR", bounty / 10, bounty % 10);
607 if ((detonate_bomb) && ((universe[i].flags & FLG_DEAD) == 0) &&
608 (type != SHIP_PLANET) && (type != SHIP_SUN) &&
609 (type != SHIP_CONSTRICTOR) && (type != SHIP_COUGAR) &&
610 (type != SHIP_CORIOLIS) && (type != SHIP_DODEC))
612 snd_play_sample (SND_EXPLODE);
613 universe[i].flags |= FLG_DEAD;
616 if ((current_screen != SCR_INTRO_ONE) &&
617 (current_screen != SCR_INTRO_TWO) &&
618 (current_screen != SCR_GAME_OVER) &&
619 (current_screen != SCR_ESCAPE_POD))
624 move_univ_object (&universe[i]);
627 switch_to_view (&flip);
629 if (type == SHIP_PLANET)
631 if ((ship_count[SHIP_CORIOLIS] == 0) &&
632 (ship_count[SHIP_DODEC] == 0) &&
633 (universe[i].distance < 65792)) // was 49152
635 make_station_appear();
642 if (type == SHIP_SUN)
649 if (universe[i].distance < 170)
651 if ((type == SHIP_CORIOLIS) || (type == SHIP_DODEC))
659 if (universe[i].distance > 57344)
667 universe[i].flags = flip.flags;
668 universe[i].exp_seed = flip.exp_seed;
669 universe[i].exp_delta = flip.exp_delta;
671 universe[i].flags &= ~FLG_FIRING;
673 if (universe[i].flags & FLG_DEAD)
676 check_target (i, &flip);
688 * Update the scanner and draw all the lollipops.
691 void update_scanner (void)
698 for (i = 0; i < MAX_UNIV_OBJECTS; i++)
700 if ((universe[i].type <= 0) ||
701 (universe[i].flags & FLG_DEAD) ||
702 (universe[i].flags & FLG_CLOAKED))
705 x = universe[i].location.x * scanner_zoom / 256;
706 y = universe[i].location.y * scanner_zoom / 256;
707 z = universe[i].location.z * scanner_zoom / 256;
713 if ((y2 < -28) || (y2 > 28) ||
714 (x1 < -50) || (x1 > 50))
721 colour = (universe[i].flags & FLG_HOSTILE) ? GFX_COL_YELLOW_5 : GFX_COL_WHITE;
723 switch (universe[i].type)
731 colour = GFX_COL_GREEN_1;
739 gfx_draw_colour_line (x1+2, y2, x1-3, y2, colour);
740 gfx_draw_colour_line (x1+2, y2+1, x1-3, y2+1, colour);
741 gfx_draw_colour_line (x1+2, y2+2, x1-3, y2+2, colour);
742 gfx_draw_colour_line (x1+2, y2+3, x1-3, y2+3, colour);
745 gfx_draw_colour_line (x1, y1, x1, y2, colour);
746 gfx_draw_colour_line (x1+1, y1, x1+1, y2, colour);
747 gfx_draw_colour_line (x1+2, y1, x1+2, y2, colour);
753 * Update the compass which tracks the space station / planet.
756 void update_compass (void)
766 if (ship_count[SHIP_CORIOLIS] || ship_count[SHIP_DODEC])
769 dest = unit_vector (&universe[un].location);
771 compass_x = compass_centre_x + (dest.x * 16);
772 compass_y = compass_centre_y + (dest.y * -16);
776 gfx_draw_sprite (IMG_RED_DOT, compass_x, compass_y);
780 gfx_draw_sprite (IMG_GREEN_DOT, compass_x, compass_y);
787 * Display the speed bar.
790 void display_speed (void)
800 len = ((flight_speed * 64) / myship.max_speed) - 1;
802 colour = (flight_speed > (myship.max_speed * 2 / 3)) ? GFX_COL_DARK_RED : GFX_COL_GOLD;
804 for (i = 0; i < 6; i++)
806 gfx_draw_colour_line (sx, sy + i, sx + len, sy + i, colour);
812 * Draw an indicator bar.
813 * Used for shields and energy banks.
816 void display_dial_bar (int len, int x, int y)
820 gfx_draw_colour_line (x, y + 384, x + len, y + 384, GFX_COL_GOLD);
822 gfx_draw_colour_line (x, y + i + 384, x + len, y + i + 384, GFX_COL_GOLD);
824 for (i = 2; i < 7; i++)
825 gfx_draw_colour_line (x, y + i + 384, x + len, y + i + 384, GFX_COL_YELLOW_1);
827 gfx_draw_colour_line (x, y + i + 384, x + len, y + i + 384, GFX_COL_DARK_RED);
832 * Display the current shield strengths.
835 void display_shields (void)
837 if (front_shield > 3)
838 display_dial_bar (front_shield / 4, 31, 7);
841 display_dial_bar (aft_shield / 4, 31, 23);
845 void display_altitude (void)
847 if (myship.altitude > 3)
848 display_dial_bar (myship.altitude / 4, 31, 92);
851 void display_cabin_temp (void)
853 if (myship.cabtemp > 3)
854 display_dial_bar (myship.cabtemp / 4, 31, 60);
858 void display_laser_temp (void)
861 display_dial_bar (laser_temp / 4, 31, 76);
866 * Display the energy banks.
869 void display_energy (void)
873 e1 = energy > 64 ? 64 : energy;
874 e2 = energy > 128 ? 64 : energy - 64;
875 e3 = energy > 192 ? 64 : energy - 128;
879 display_dial_bar (e4, 416, 61);
882 display_dial_bar (e3, 416, 79);
885 display_dial_bar (e2, 416, 97);
888 display_dial_bar (e1, 416, 115);
893 void display_flight_roll (void)
902 pos = sx - ((flight_roll * 28) / myship.max_roll);
905 for (i = 0; i < 4; i++)
907 gfx_draw_colour_line (pos + i, sy, pos + i, sy + 7, GFX_COL_GOLD);
911 void display_flight_climb (void)
918 sy = 384 + 9 + 14 + 16;
920 pos = sx + ((flight_climb * 28) / myship.max_climb);
923 for (i = 0; i < 4; i++)
925 gfx_draw_colour_line (pos + i, sy, pos + i, sy + 7, GFX_COL_GOLD);
930 void display_fuel (void)
933 display_dial_bar ((cmdr.fuel * 64) / myship.max_fuel, 31, 44);
937 void display_missiles (void)
942 if (cmdr.missiles == 0)
945 nomiss = cmdr.missiles > 4 ? 4 : cmdr.missiles;
947 x = (4 - nomiss) * 16 + 35;
950 if (missile_target != MISSILE_UNARMED)
952 gfx_draw_sprite ((missile_target < 0) ? IMG_MISSILE_YELLOW :
953 IMG_MISSILE_RED, x, y);
958 for (; nomiss > 0; nomiss--)
960 gfx_draw_sprite (IMG_MISSILE_GREEN, x, y);
967 void display_condition(void)
969 static const int cc[] = { GFX_COL_BLACK, GFX_COL_GREEN_1, GFX_COL_YELLOW_1,
970 GFX_COL_RED, GFX_COL_RED };
971 int c = cc[condition];
972 if (condition == COND_ALERT && (mcount & 4))
974 gfx_draw_filled_circle(condition_x, condition_y, condition_r, c);
977 void update_console (void)
982 display_flight_climb();
983 display_flight_roll();
987 display_cabin_temp();
988 display_laser_temp();
994 gfx_set_clip_region (0, 0, 512, 512);
1000 display_condition();
1004 sprintf(buf, "x%d", scanner_zoom);
1005 gfx_display_text(zoom_x, zoom_y, buf);
1008 if (ship_count[SHIP_CORIOLIS] || ship_count[SHIP_DODEC])
1009 gfx_draw_sprite (IMG_BIG_S, 387, 490);
1012 gfx_draw_sprite (IMG_BIG_E, 115, 490);
1014 gfx_set_clip_region (0, 0, 512, 512);
1017 void increase_flight_roll (void)
1019 if (flight_roll < myship.max_roll)
1024 void decrease_flight_roll (void)
1026 if (flight_roll > -myship.max_roll)
1031 void increase_flight_climb (void)
1033 if (flight_climb < myship.max_climb)
1037 void decrease_flight_climb (void)
1039 if (flight_climb > -myship.max_climb)
1044 void start_hyperspace (void)
1049 hyper_distance = calc_distance_to_planet (docked_planet, hyperspace_planet);
1051 if ((docked_planet.a == hyperspace_planet.a &&
1052 docked_planet.b == hyperspace_planet.b &&
1053 docked_planet.c == hyperspace_planet.c &&
1054 docked_planet.d == hyperspace_planet.d &&
1055 docked_planet.e == hyperspace_planet.e &&
1056 docked_planet.f == hyperspace_planet.f) ||
1057 (hyper_distance > cmdr.fuel))
1060 destination_planet = hyperspace_planet;
1061 name_planet (hyper_name, destination_planet);
1062 capitalise_name (hyper_name);
1065 hyper_countdown = 15;
1068 disengage_auto_pilot();
1071 void start_galactic_hyperspace (void)
1076 if (cmdr.galactic_hyperdrive == 0)
1080 hyper_countdown = 2;
1082 disengage_auto_pilot();
1087 void display_hyper_status (void)
1091 sprintf (str, "%d", hyper_countdown);
1093 if ((current_screen == SCR_FRONT_VIEW) || (current_screen == SCR_REAR_VIEW) ||
1094 (current_screen == SCR_LEFT_VIEW) || (current_screen == SCR_RIGHT_VIEW))
1096 gfx_display_text (5, 5, str);
1099 gfx_display_centre_text (358, "Galactic Hyperspace", 120, GFX_COL_WHITE);
1103 sprintf (str, "Hyperspace - %s", hyper_name);
1104 gfx_display_centre_text (358, str, 120, GFX_COL_WHITE);
1109 gfx_clear_area (5, 5, 25, 34);
1110 gfx_display_text (5, 5, str);
1115 int rotate_byte_left (int x)
1117 return ((x << 1) | (x >> 7)) & 255;
1120 void enter_next_galaxy (void)
1122 cmdr.galaxy_number++;
1123 cmdr.galaxy_number &= 7;
1125 cmdr.galaxy.a = rotate_byte_left (cmdr.galaxy.a);
1126 cmdr.galaxy.b = rotate_byte_left (cmdr.galaxy.b);
1127 cmdr.galaxy.c = rotate_byte_left (cmdr.galaxy.c);
1128 cmdr.galaxy.d = rotate_byte_left (cmdr.galaxy.d);
1129 cmdr.galaxy.e = rotate_byte_left (cmdr.galaxy.e);
1130 cmdr.galaxy.f = rotate_byte_left (cmdr.galaxy.f);
1132 docked_planet = find_planet (0x60, 0x60);
1133 hyperspace_planet = docked_planet;
1140 void enter_witchspace (void)
1146 docked_planet.b ^= 31;
1155 nthg = (randint() & 3) + 1;
1157 for (i = 0; i < nthg; i++)
1160 current_screen = SCR_BREAK_PATTERN;
1161 snd_play_sample (SND_HYPERSPACE);
1165 void complete_hyperspace (void)
1175 cmdr.galactic_hyperdrive = 0;
1176 enter_next_galaxy();
1177 cmdr.legal_status = 0;
1181 cmdr.fuel -= hyper_distance;
1182 cmdr.legal_status /= 2;
1184 if ((rand255() > 253) || (flight_climb == myship.max_climb))
1190 docked_planet = destination_planet;
1193 cmdr.market_rnd = rand255();
1194 generate_planet_data (¤t_planet_data, docked_planet);
1195 generate_stock_market ();
1203 generate_landscape(docked_planet.a * 251 + docked_planet.b);
1204 set_init_matrix (rotmat);
1206 pz = (((docked_planet.b) & 7) + 7) / 2;
1214 if ((docked_planet.b & 1) == 0)
1220 add_new_ship (SHIP_PLANET, px, py, pz, rotmat, 0, 0);
1223 pz = -(((docked_planet.d & 7) | 1) << 16);
1224 px = ((docked_planet.f & 3) << 16) | ((docked_planet.f & 3) << 8);
1226 add_new_ship (SHIP_SUN, px, py, pz, rotmat, 0, 0);
1228 current_screen = SCR_BREAK_PATTERN;
1229 snd_play_sample (SND_HYPERSPACE);
1233 void countdown_hyperspace (void)
1235 if (hyper_countdown == 0)
1237 complete_hyperspace();
1246 void jump_warp (void)
1252 for (i = 0; i < MAX_UNIV_OBJECTS; i++)
1254 type = universe[i].type;
1256 if ((type > 0) && (type != SHIP_ASTEROID) && (type != SHIP_CARGO) &&
1257 (type != SHIP_ALLOY) && (type != SHIP_ROCK) &&
1258 (type != SHIP_BOULDER) && (type != SHIP_ESCAPE_CAPSULE))
1260 info_message ("Mass Locked");
1265 if ((universe[0].distance < 75001) || (universe[1].distance < 75001))
1267 info_message ("Mass Locked");
1272 if (universe[0].distance < universe[1].distance)
1273 jump = universe[0].distance - 75000;
1275 jump = universe[1].distance - 75000;
1280 for (i = 0; i < MAX_UNIV_OBJECTS; i++)
1282 if (universe[i].type != 0)
1283 universe[i].location.z -= jump;
1292 void launch_player (void)
1300 cmdr.legal_status |= carrying_contraband();
1303 generate_landscape(docked_planet.a * 251 + docked_planet.b);
1304 set_init_matrix (rotmat);
1305 add_new_ship (SHIP_PLANET, 0, 0, 65536, rotmat, 0, 0);
1309 rotmat[2].x = -rotmat[2].x;
1310 rotmat[2].y = -rotmat[2].y;
1311 rotmat[2].z = -rotmat[2].z;
1312 add_new_station (0, 0, -256, rotmat);
1314 current_screen = SCR_BREAK_PATTERN;
1315 snd_play_sample (SND_LAUNCH);
1321 * Engage the docking computer.
1322 * For the moment we just do an instant dock if we are in the safe zone.
1325 void engage_docking_computer (void)
1327 if (ship_count[SHIP_CORIOLIS] || ship_count[SHIP_DODEC])
1329 snd_play_sample (SND_DOCK);
1331 current_screen = SCR_BREAK_PATTERN;
1336 void update_condition(void)
1339 condition = COND_DOCKED;
1340 else if (energy < 50 || myship.altitude < 32 || myship.cabtemp > 224)
1341 condition = COND_ALERT;
1342 else if (energy < 128 || myship.altitude < 64 || myship.cabtemp > 192)
1343 condition = COND_RED;
1346 condition = COND_GREEN;
1347 if (myship.altitude < 128 || myship.cabtemp >= 128)
1348 condition = COND_YELLOW;
1349 for (i = 0; i < MAX_UNIV_OBJECTS; i++) {
1350 struct univ_object *un = &universe[i];
1353 if (un->flags & FLG_HOSTILE) {
1354 condition = COND_RED;
1357 if (condition == COND_GREEN &&
1358 un->type != SHIP_ASTEROID && un->type != SHIP_CARGO &&
1359 un->type != SHIP_ALLOY && un->type != SHIP_ROCK &&
1360 un->type != SHIP_BOULDER && un->type != SHIP_ESCAPE_CAPSULE &&
1361 un->type != SHIP_CORIOLIS && un->type != SHIP_DODEC)
1362 condition = COND_YELLOW;