Files
basic-computer-games/84_Super_Star_Trek/java/SuperStarTrekGame.java
Jeff Atwood 05c998658d Merge pull request #679 from taciano-perez/update_superstartrek_java
updated implementation with proper OO design, provided link to video
2022-03-24 13:02:24 -05:00

342 lines
15 KiB
Java

import java.util.stream.IntStream;
/**
* SUPER STARTREK - MAY 16,1978
* **** **** STAR TREK **** ****
* **** SIMULATION OF A MISSION OF THE STARSHIP ENTERPRISE,
* **** AS SEEN ON THE STAR TREK TV SHOW.
* **** ORIGINAL PROGRAM BY MIKE MAYFIELD, MODIFIED VERSION
* **** PUBLISHED IN DEC'S "101 BASIC GAMES", BY DAVE AHL.
* **** MODIFICATIONS TO THE LATTER (PLUS DEBUGGING) BY BOB
* *** LEEDOM - APRIL & DECEMBER 1974,
* *** WITH A LITTLE HELP FROM HIS FRIENDS . . .
*
* Ported to Java in Jan-Mar 2022 by
* Taciano Dreckmann Perez (taciano.perez@gmail.com)
*/
public class SuperStarTrekGame implements GameCallback {
// commands
static final int COMMAND_NAV = 1;
static final int COMMAND_SRS = 2;
static final int COMMAND_LRS = 3;
static final int COMMAND_PHA = 4;
static final int COMMAND_TOR = 5;
static final int COMMAND_SHE = 6;
static final int COMMAND_DAM = 7;
static final int COMMAND_COM = 8;
static final int COMMAND_XXX = 9;
// computer commands
static final int COMPUTER_COMMAND_CUMULATIVE_GALACTIC_RECORD = 1;
static final int COMPUTER_COMMAND_STATUS_REPORT = 2;
static final int COMPUTER_COMMAND_PHOTON_TORPEDO_DATA = 3;
static final int COMPUTER_COMMAND_STARBASE_NAV_DATA = 4;
static final int COMPUTER_COMMAND_DIR_DIST_CALC = 5;
static final int COMPUTER_COMMAND_GALAXY_MAP = 6;
// other constants
static final String COMMANDS = "NAVSRSLRSPHATORSHEDAMCOMXXX";
// game state
final GalaxyMap galaxyMap = new GalaxyMap();
double stardate = Util.toInt(Util.random() * 20 + 20);
int missionDuration = Math.max((25 + Util.toInt(Util.random() * 10)), galaxyMap.getKlingonsInGalaxy()+1); // T9 (mission duration in stardates)
boolean restart = false;
// initial values
final double initialStardate = stardate;
public static void main(String[] args) {
final SuperStarTrekGame game = new SuperStarTrekGame();
printBanner();
while (true) {
game.orders();
game.enterNewQuadrant();
game.restart = false;
game.commandLoop();
}
}
static void printBanner() {
IntStream.range(1, 10).forEach(i -> {
Util.println("");
});
Util.println(
"""
,------*------,
,------------- '--- ------'
'-------- --' / /
,---' '-------/ /--,
'----------------'
THE USS ENTERPRISE --- NCC-1701"
"""
);
}
void orders() {
Util.println("YOUR ORDERS ARE AS FOLLOWS:\n" +
" DESTROY THE " + galaxyMap.getKlingonsInGalaxy() + " KLINGON WARSHIP" + ((galaxyMap.getKlingonsInGalaxy() == 1) ? "" : "S") + " WHICH HAVE INVADED\n" +
" THE GALAXY BEFORE THEY CAN ATTACK FEDERATION HEADQUARTERS\n" +
" ON STARDATE " + initialStardate + missionDuration + " THIS GIVES YOU " + missionDuration + " DAYS. THERE " + ((galaxyMap.getBasesInGalaxy() == 1) ? "IS" : "ARE") + "\n" +
" " + galaxyMap.getBasesInGalaxy() + " STARBASE" + ((galaxyMap.getBasesInGalaxy() == 1) ? "" : "S") + " IN THE GALAXY FOR RESUPPLYING YOUR SHIP");
}
public void enterNewQuadrant() {
galaxyMap.newQuadrant(stardate, initialStardate);
shortRangeSensorScan();
}
void commandLoop() {
while (!this.restart) {
checkShipEnergy();
String cmdStr = "";
while ("".equals(cmdStr)) cmdStr = Util.inputStr("COMMAND");
boolean foundCommand = false;
for (int i = 1; i <= 9; i++) {
if (Util.leftStr(cmdStr, 3).equals(Util.midStr(COMMANDS, 3 * i - 2, 3))) {
switch (i) {
case COMMAND_NAV:
navigation();
foundCommand = true;
break;
case COMMAND_SRS:
shortRangeSensorScan();
foundCommand = true;
break;
case COMMAND_LRS:
longRangeSensorScan();
foundCommand = true;
break;
case COMMAND_PHA:
firePhasers();
foundCommand = true;
break;
case COMMAND_TOR:
firePhotonTorpedo();
foundCommand = true;
break;
case COMMAND_SHE:
shieldControl();
foundCommand = true;
break;
case COMMAND_DAM:
galaxyMap.getEnterprise().damageControl(this);
foundCommand = true;
break;
case COMMAND_COM:
libraryComputer();
foundCommand = true;
break;
case COMMAND_XXX:
endGameFail(false);
foundCommand = true;
break;
default:
printCommandOptions();
foundCommand = true;
}
}
}
if (!foundCommand) printCommandOptions();
}
}
void checkShipEnergy() {
final Enterprise enterprise = galaxyMap.getEnterprise();
if (enterprise.getTotalEnergy() < 10 && (enterprise.getEnergy() <= 10 || enterprise.getDeviceStatus()[Enterprise.DEVICE_SHIELD_CONTROL] != 0)) {
Util.println("\n** FATAL ERROR ** YOU'VE JUST STRANDED YOUR SHIP IN ");
Util.println("SPACE");
Util.println("YOU HAVE INSUFFICIENT MANEUVERING ENERGY,");
Util.println(" AND SHIELD CONTROL");
Util.println("IS PRESENTLY INCAPABLE OF CROSS");
Util.println("-CIRCUITING TO ENGINE ROOM!!");
endGameFail(false);
}
}
void printCommandOptions() {
Util.println("ENTER ONE OF THE FOLLOWING:");
Util.println(" NAV (TO SET COURSE)");
Util.println(" SRS (FOR SHORT RANGE SENSOR SCAN)");
Util.println(" LRS (FOR LONG RANGE SENSOR SCAN)");
Util.println(" PHA (TO FIRE PHASERS)");
Util.println(" TOR (TO FIRE PHOTON TORPEDOES)");
Util.println(" SHE (TO RAISE OR LOWER SHIELDS)");
Util.println(" DAM (FOR DAMAGE CONTROL REPORTS)");
Util.println(" COM (TO CALL ON LIBRARY-COMPUTER)");
Util.println(" XXX (TO RESIGN YOUR COMMAND)\n");
}
void navigation() {
float course = Util.toInt(Util.inputFloat("COURSE (0-9)"));
if (course == 9) course = 1;
if (course < 1 || course >= 9) {
Util.println(" LT. SULU REPORTS, 'INCORRECT COURSE DATA, SIR!'");
return;
}
final Enterprise enterprise = galaxyMap.getEnterprise();
final double[] deviceStatus = enterprise.getDeviceStatus();
Util.println("WARP FACTOR (0-" + ((deviceStatus[Enterprise.DEVICE_WARP_ENGINES] < 0) ? "0.2" : "8") + ")");
float warp = Util.inputFloat("");
if (deviceStatus[Enterprise.DEVICE_WARP_ENGINES] < 0 && warp > .2) {
Util.println("WARP ENGINES ARE DAMAGED. MAXIMUM SPEED = WARP 0.2");
return;
}
if (warp == 0) return;
if (warp > 0 && warp <= 8) {
int n = Util.toInt(warp * 8);
if (enterprise.getEnergy() - n >= 0) {
galaxyMap.klingonsMoveAndFire(this);
repairDamagedDevices(course, warp, n);
galaxyMap.moveEnterprise(course, warp, n, stardate, initialStardate, missionDuration, this);
} else {
Util.println("ENGINEERING REPORTS 'INSUFFICIENT ENERGY AVAILABLE");
Util.println(" FOR MANEUVERING AT WARP " + warp + "!'");
if (enterprise.getShields() < n - enterprise.getEnergy() || deviceStatus[Enterprise.DEVICE_SHIELD_CONTROL] < 0) return;
Util.println("DEFLECTOR CONTROL ROOM ACKNOWLEDGES " + enterprise.getShields() + " UNITS OF ENERGY");
Util.println(" PRESENTLY DEPLOYED TO SHIELDS.");
}
} else {
Util.println(" CHIEF ENGINEER SCOTT REPORTS 'THE ENGINES WON'T TAKE");
Util.println(" WARP " + warp + "!'");
}
}
void repairDamagedDevices(final float course, final float warp, final int N) {
final Enterprise enterprise = galaxyMap.getEnterprise();
// repair damaged devices and print damage report
enterprise.repairDamagedDevices(warp);
if (Util.random() > .2) return; // 80% chance no damage nor repair
int randomDevice = Util.fnr(); // random device
final double[] deviceStatus = enterprise.getDeviceStatus();
if (Util.random() >= .6) { // 40% chance of repair of random device
enterprise.setDeviceStatus(randomDevice, deviceStatus[randomDevice] + Util.random() * 3 + 1);
Util.println("DAMAGE CONTROL REPORT: " + Enterprise.printDeviceName(randomDevice) + " STATE OF REPAIR IMPROVED\n");
} else { // 60% chance of damage of random device
enterprise.setDeviceStatus(randomDevice, deviceStatus[randomDevice] - (Util.random() * 5 + 1));
Util.println("DAMAGE CONTROL REPORT: " + Enterprise.printDeviceName(randomDevice) + " DAMAGED");
}
}
void longRangeSensorScan() {
// LONG RANGE SENSOR SCAN CODE
galaxyMap.longRangeSensorScan();
}
void firePhasers() {
galaxyMap.firePhasers(this);
}
void firePhotonTorpedo() {
galaxyMap.firePhotonTorpedo(stardate, initialStardate, missionDuration, this);
}
void shieldControl() {
galaxyMap.getEnterprise().shieldControl();
}
void shortRangeSensorScan() {
// SHORT RANGE SENSOR SCAN & STARTUP SUBROUTINE
galaxyMap.shortRangeSensorScan(stardate);
}
void libraryComputer() {
// REM LIBRARY COMPUTER CODE
if (galaxyMap.getEnterprise().getDeviceStatus()[Enterprise.DEVICE_LIBRARY_COMPUTER] < 0) {
Util.println("COMPUTER DISABLED");
return;
}
while (true) {
final float commandInput = Util.inputFloat("COMPUTER ACTIVE AND AWAITING COMMAND");
if (commandInput < 0) return;
Util.println("");
int command = Util.toInt(commandInput) + 1;
if (command >= COMPUTER_COMMAND_CUMULATIVE_GALACTIC_RECORD && command <= COMPUTER_COMMAND_GALAXY_MAP) {
switch (command) {
case COMPUTER_COMMAND_CUMULATIVE_GALACTIC_RECORD:
galaxyMap.cumulativeGalacticRecord(true);
return;
case COMPUTER_COMMAND_STATUS_REPORT:
statusReport();
return;
case COMPUTER_COMMAND_PHOTON_TORPEDO_DATA:
galaxyMap.photonTorpedoData();
return;
case COMPUTER_COMMAND_STARBASE_NAV_DATA:
galaxyMap.starbaseNavData();
return;
case COMPUTER_COMMAND_DIR_DIST_CALC:
galaxyMap.directionDistanceCalculator();
return;
case COMPUTER_COMMAND_GALAXY_MAP:
galaxyMap.cumulativeGalacticRecord(false);
return;
}
} else {
// invalid command
Util.println("FUNCTIONS AVAILABLE FROM LIBRARY-COMPUTER:");
Util.println(" 0 = CUMULATIVE GALACTIC RECORD");
Util.println(" 1 = STATUS REPORT");
Util.println(" 2 = PHOTON TORPEDO DATA");
Util.println(" 3 = STARBASE NAV DATA");
Util.println(" 4 = DIRECTION/DISTANCE CALCULATOR");
Util.println(" 5 = GALAXY 'REGION NAME' MAP");
Util.println("");
}
}
}
void statusReport() {
Util.println(" STATUS REPORT:");
Util.println("KLINGON" + ((galaxyMap.getKlingonsInGalaxy() > 1)? "S" : "") + " LEFT: " + galaxyMap.getKlingonsInGalaxy());
Util.println("MISSION MUST BE COMPLETED IN " + .1 * Util.toInt((initialStardate + missionDuration - stardate) * 10) + " STARDATES");
if (galaxyMap.getBasesInGalaxy() >= 1) {
Util.println("THE FEDERATION IS MAINTAINING " + galaxyMap.getBasesInGalaxy() + " STARBASE" + ((galaxyMap.getBasesInGalaxy() > 1)? "S" : "") + " IN THE GALAXY");
} else {
Util.println("YOUR STUPIDITY HAS LEFT YOU ON YOUR OWN IN");
Util.println(" THE GALAXY -- YOU HAVE NO STARBASES LEFT!");
}
galaxyMap.getEnterprise().damageControl(this);
}
public void incrementStardate(double increment) {
this.stardate += increment;
}
public void endGameFail(final boolean enterpriseDestroyed) { // 6220
if (enterpriseDestroyed) {
Util.println("\nTHE ENTERPRISE HAS BEEN DESTROYED. THEN FEDERATION ");
Util.println("WILL BE CONQUERED");
}
Util.println("\nIT IS STARDATE " + stardate);
Util.println("THERE WERE " + galaxyMap.getKlingonsInGalaxy() + " KLINGON BATTLE CRUISERS LEFT AT");
Util.println("THE END OF YOUR MISSION.");
repeatGame();
}
public void endGameSuccess() {
Util.println("CONGRATULATION, CAPTAIN! THE LAST KLINGON BATTLE CRUISER");
Util.println("MENACING THE FEDERATION HAS BEEN DESTROYED.\n");
Util.println("YOUR EFFICIENCY RATING IS " + (Math.sqrt(1000 * (galaxyMap.getRemainingKlingons() / (stardate - initialStardate)))));
repeatGame();
}
void repeatGame() {
Util.println("\n");
if (galaxyMap.getBasesInGalaxy() != 0) {
Util.println("THE FEDERATION IS IN NEED OF A NEW STARSHIP COMMANDER");
Util.println("FOR A SIMILAR MISSION -- IF THERE IS A VOLUNTEER,");
final String reply = Util.inputStr("LET HIM STEP FORWARD AND ENTER 'AYE'");
if ("AYE".equals(reply)) {
this.restart = true;
} else {
System.exit(0);
}
}
}
}