Created
October 10, 2024 04:40
-
-
Save mbutler/bf2720590b7472a302aa6a296ef099c0 to your computer and use it in GitHub Desktop.
classic 70s Star Trek BASIC game in JS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Star Trek Game in JavaScript | |
// Original BASIC version by Mike Mayfield, converted to JavaScript | |
"use strict"; | |
// Constants | |
const GALAXY_SIZE = 8; | |
const SECTOR_SIZE = 8; | |
const MAX_DOCK_STARS = 9; | |
const COMMANDS = ["NAV", "SRS", "LRS", "PHA", "TOR", "SHE", "DAM", "COM", "XXX"]; | |
// Global Variables | |
let galaxy = []; | |
let quadrantNames = []; | |
let devices = []; | |
let commandFunctions = {}; | |
let gameState = {}; | |
// Initialize the game | |
function initializeGame() { | |
setupQuadrantNames(); | |
setupDevices(); | |
setupGalaxy(); | |
setupEnterprise(); | |
printIntro(); | |
mainGameLoop(); | |
} | |
// Setup functions | |
function setupQuadrantNames() { | |
quadrantNames = [ | |
["ANTARES", "RIGEL", "PROCYON", "VEGA", "CANOPUS", "ALTAIR", "SAGITTARIUS", "POLLUX"], | |
["SIRIUS", "DENEB", "CAPELLA", "BETELGEUSE", "ALDEBARAN", "REGULUS", "ARCTURUS", "SPICA"], | |
]; | |
} | |
function setupDevices() { | |
devices = [ | |
"WARP ENGINES", | |
"SHORT RANGE SENSORS", | |
"LONG RANGE SENSORS", | |
"PHASER CONTROL", | |
"PHOTON TUBES", | |
"DAMAGE CONTROL", | |
"SHIELD CONTROL", | |
"LIBRARY-COMPUTER", | |
]; | |
} | |
function setupGalaxy() { | |
for (let x = 0; x < GALAXY_SIZE; x++) { | |
galaxy[x] = []; | |
for (let y = 0; y < GALAXY_SIZE; y++) { | |
let klingons = 0; | |
let starbases = 0; | |
let stars = getRandomInt(1, 8); | |
let random = Math.random(); | |
if (random > 0.98) { | |
klingons = 3; | |
} else if (random > 0.95) { | |
klingons = 2; | |
} else if (random > 0.80) { | |
klingons = 1; | |
} | |
if (Math.random() > 0.96) { | |
starbases = 1; | |
} | |
galaxy[x][y] = { | |
klingons: klingons, | |
starbases: starbases, | |
stars: stars, | |
}; | |
} | |
} | |
} | |
function setupEnterprise() { | |
gameState = { | |
stardate: getRandomInt(2000, 2200), | |
timeRemaining: 25 + getRandomInt(0, 10), | |
initialStardate: 0, | |
totalKlingons: 0, | |
klingonsDestroyed: 0, | |
starbasesDestroyed: 0, | |
energy: 3000, | |
torpedoes: 10, | |
shields: 0, | |
quadrantX: getRandomInt(0, GALAXY_SIZE - 1), | |
quadrantY: getRandomInt(0, GALAXY_SIZE - 1), | |
sectorX: getRandomInt(0, SECTOR_SIZE - 1), | |
sectorY: getRandomInt(0, SECTOR_SIZE - 1), | |
docked: false, | |
damage: new Array(8).fill(0), | |
galaxyMap: [], | |
shipCondition: "GREEN", | |
destroyed: false, | |
resigned: false, | |
}; | |
gameState.initialStardate = gameState.stardate; | |
// Count total Klingons and Starbases | |
for (let x = 0; x < GALAXY_SIZE; x++) { | |
for (let y = 0; y < GALAXY_SIZE; y++) { | |
gameState.totalKlingons += galaxy[x][y].klingons; | |
if (galaxy[x][y].starbases > 0) { | |
gameState.starbases = (gameState.starbases || 0) + 1; | |
} | |
} | |
} | |
// Ensure at least one starbase exists | |
if (!gameState.starbases) { | |
galaxy[gameState.quadrantX][gameState.quadrantY].starbases = 1; | |
gameState.starbases = 1; | |
} | |
} | |
// Utility functions | |
function getRandomInt(min, max) { | |
return Math.floor(Math.random() * (max - min + 1) + min); | |
} | |
function printIntro() { | |
console.clear(); | |
console.log(" ,------*------,"); | |
console.log(" ,------------- '--- ------'"); | |
console.log(" '-------- --' / /"); | |
console.log(" ,---' '-------/ /--,"); | |
console.log(" '----------------'"); | |
console.log(""); | |
console.log(" THE USS ENTERPRISE --- NCC-1701"); | |
console.log(""); | |
console.log("YOUR ORDERS ARE AS FOLLOWS:"); | |
console.log(` DESTROY THE ${gameState.totalKlingons} KLINGON WARSHIPS WHICH HAVE INVADED`); | |
console.log(` THE GALAXY BEFORE THEY CAN ATTACK FEDERATION HEADQUARTERS`); | |
console.log(` ON STARDATE ${gameState.stardate + gameState.timeRemaining} THIS GIVES YOU ${gameState.timeRemaining} DAYS.`); | |
console.log( | |
` THERE ${gameState.starbases === 1 ? "IS" : "ARE"} ${gameState.starbases} STARBASE${ | |
gameState.starbases === 1 ? "" : "S" | |
} IN THE GALAXY FOR RESUPPLYING YOUR SHIP` | |
); | |
console.log(""); | |
} | |
// Main Game Loop | |
function mainGameLoop() { | |
while (!gameState.destroyed && !gameState.resigned && gameState.timeRemaining > 0) { | |
enterQuadrant(); | |
commandLoop(); | |
if (gameState.totalKlingons <= 0) { | |
victory(); | |
break; | |
} | |
if (gameState.destroyed) { | |
defeat(); | |
break; | |
} | |
if (gameState.resigned) { | |
resignation(); | |
break; | |
} | |
if (gameState.timeRemaining <= 0) { | |
outOfTime(); | |
break; | |
} | |
} | |
} | |
// Entering a Quadrant | |
function enterQuadrant() { | |
let quadrant = galaxy[gameState.quadrantX][gameState.quadrantY]; | |
console.log(`Now entering ${getQuadrantName(gameState.quadrantX, gameState.quadrantY)} Quadrant...`); | |
console.log(""); | |
gameState.klingonsInQuadrant = quadrant.klingons; | |
gameState.starbasesInQuadrant = quadrant.starbases; | |
gameState.starsInQuadrant = quadrant.stars; | |
gameState.sectorMap = createSectorMap(); | |
if (gameState.klingonsInQuadrant > 0) { | |
console.log("COMBAT AREA CONDITION RED"); | |
if (gameState.shields < 200) { | |
console.log(" SHIELDS DANGEROUSLY LOW"); | |
} | |
} | |
if (gameState.docked) { | |
gameState.docked = false; | |
console.log("Leaving starbase..."); | |
} | |
shortRangeScan(); | |
} | |
// Create Sector Map | |
function createSectorMap() { | |
let map = Array.from({ length: SECTOR_SIZE }, () => Array(SECTOR_SIZE).fill(" ")); | |
// Place Enterprise | |
map[gameState.sectorX][gameState.sectorY] = "<E>"; | |
// Place Stars | |
for (let i = 0; i < gameState.starsInQuadrant; i++) { | |
let [x, y] = getRandomEmptySector(map); | |
map[x][y] = " * "; | |
} | |
// Place Klingons | |
gameState.klingonPositions = []; | |
for (let i = 0; i < gameState.klingonsInQuadrant; i++) { | |
let [x, y] = getRandomEmptySector(map); | |
map[x][y] = "+K+"; | |
gameState.klingonPositions.push({ | |
x: x, | |
y: y, | |
energy: 300 + getRandomInt(0, 200), | |
}); | |
} | |
// Place Starbases | |
if (gameState.starbasesInQuadrant > 0) { | |
let [x, y] = getRandomEmptySector(map); | |
map[x][y] = ">!<"; | |
gameState.starbasePosition = { x: x, y: y }; | |
} | |
return map; | |
} | |
function getRandomEmptySector(map) { | |
let x, y; | |
do { | |
x = getRandomInt(0, SECTOR_SIZE - 1); | |
y = getRandomInt(0, SECTOR_SIZE - 1); | |
} while (map[x][y] !== " "); | |
return [x, y]; | |
} | |
// Command Loop | |
function commandLoop() { | |
let command = ""; | |
while (!gameState.destroyed && !gameState.resigned) { | |
if (gameState.klingonsInQuadrant > 0) { | |
klingonAttack(); | |
if (gameState.destroyed) { | |
break; | |
} | |
} | |
if (gameState.energy <= 0) { | |
console.log("You have run out of energy!"); | |
gameState.destroyed = true; | |
break; | |
} | |
command = promptCommand(); | |
executeCommand(command); | |
if (command === "NAV" || command === "XXX") { | |
break; | |
} | |
} | |
} | |
// Prompt Command | |
function promptCommand() { | |
let input = prompt("Command? ").toUpperCase(); | |
while (!COMMANDS.includes(input)) { | |
console.log("Enter one of the following:"); | |
console.log(" NAV (TO SET COURSE)"); | |
console.log(" SRS (FOR SHORT RANGE SENSOR SCAN)"); | |
console.log(" LRS (FOR LONG RANGE SENSOR SCAN)"); | |
console.log(" PHA (TO FIRE PHASERS)"); | |
console.log(" TOR (TO FIRE PHOTON TORPEDOES)"); | |
console.log(" SHE (TO RAISE OR LOWER SHIELDS)"); | |
console.log(" DAM (FOR DAMAGE CONTROL REPORTS)"); | |
console.log(" COM (TO CALL ON LIBRARY-COMPUTER)"); | |
console.log(" XXX (TO RESIGN YOUR COMMAND)"); | |
input = prompt("Command? ").toUpperCase(); | |
} | |
return input; | |
} | |
// Execute Command | |
function executeCommand(command) { | |
switch (command) { | |
case "NAV": | |
navigate(); | |
break; | |
case "SRS": | |
shortRangeScan(); | |
break; | |
case "LRS": | |
longRangeScan(); | |
break; | |
case "PHA": | |
firePhasers(); | |
break; | |
case "TOR": | |
fireTorpedoes(); | |
break; | |
case "SHE": | |
shieldControl(); | |
break; | |
case "DAM": | |
damageReport(); | |
break; | |
case "COM": | |
libraryComputer(); | |
break; | |
case "XXX": | |
gameState.resigned = true; | |
break; | |
default: | |
console.log("Invalid command."); | |
} | |
} | |
// Navigation | |
function navigate() { | |
let course = parseFloat(prompt("Course (1-9)? ")); | |
if (course === 9) course = 1; | |
if (course < 1 || course >= 9) { | |
console.log(" Lt. Sulu reports, 'Incorrect course data, sir!'"); | |
return; | |
} | |
let warp = parseFloat(prompt("Warp Factor (0-8)? ")); | |
if (warp <= 0 || warp > 8) { | |
console.log(" Chief Engineer Scott reports 'The engines won't take warp " + warp + "!'"); | |
return; | |
} | |
let distance = Math.round(warp * 8); | |
if (gameState.energy - distance < 0) { | |
console.log("Engineering reports 'Insufficient energy available for maneuvering at warp " + warp + "!'"); | |
return; | |
} | |
moveEnterprise(course, distance); | |
} | |
function moveEnterprise(course, distance) { | |
let direction = getDirectionVector(course); | |
for (let i = 0; i < distance; i++) { | |
let newX = gameState.sectorX + direction.dx; | |
let newY = gameState.sectorY + direction.dy; | |
if (newX < 0 || newX >= SECTOR_SIZE || newY < 0 || newY >= SECTOR_SIZE) { | |
// Move to new Quadrant | |
gameState.quadrantX += Math.sign(direction.dx); | |
gameState.quadrantY += Math.sign(direction.dy); | |
if (gameState.quadrantX < 0) gameState.quadrantX = 0; | |
if (gameState.quadrantX >= GALAXY_SIZE) gameState.quadrantX = GALAXY_SIZE - 1; | |
if (gameState.quadrantY < 0) gameState.quadrantY = 0; | |
if (gameState.quadrantY >= GALAXY_SIZE) gameState.quadrantY = GALAXY_SIZE - 1; | |
gameState.sectorX = (newX + SECTOR_SIZE) % SECTOR_SIZE; | |
gameState.sectorY = (newY + SECTOR_SIZE) % SECTOR_SIZE; | |
gameState.energy -= distance; | |
gameState.timeRemaining--; | |
enterQuadrant(); | |
return; | |
} | |
if (gameState.sectorMap[newX][newY] === " ") { | |
gameState.sectorMap[gameState.sectorX][gameState.sectorY] = " "; | |
gameState.sectorX = newX; | |
gameState.sectorY = newY; | |
gameState.sectorMap[gameState.sectorX][gameState.sectorY] = "<E>"; | |
} else { | |
console.log("Warp engines shut down at sector " + (gameState.sectorX + 1) + "," + (gameState.sectorY + 1) + " due to bad navigation."); | |
break; | |
} | |
} | |
gameState.energy -= distance; | |
gameState.timeRemaining--; | |
shortRangeScan(); | |
} | |
function getDirectionVector(course) { | |
const angles = [0, 45, 90, 135, 180, 225, 270, 315]; | |
let angle = angles[Math.floor(course) - 1]; | |
let rad = (angle * Math.PI) / 180; | |
let dx = Math.round(Math.sin(rad)); | |
let dy = Math.round(Math.cos(rad)); | |
return { dx: dx, dy: dy }; | |
} | |
// Short Range Scan | |
function shortRangeScan() { | |
console.log("Short Range Sensor Scan for Quadrant " + (gameState.quadrantX + 1) + "," + (gameState.quadrantY + 1)); | |
console.log("---------------------------------"); | |
for (let y = 0; y < SECTOR_SIZE; y++) { | |
let row = ""; | |
for (let x = 0; x < SECTOR_SIZE; x++) { | |
row += gameState.sectorMap[x][y]; | |
} | |
console.log(row); | |
} | |
console.log("---------------------------------"); | |
console.log("Stardate: " + gameState.stardate); | |
console.log("Condition: " + gameState.shipCondition); | |
console.log("Quadrant: " + (gameState.quadrantX + 1) + "," + (gameState.quadrantY + 1)); | |
console.log("Sector: " + (gameState.sectorX + 1) + "," + (gameState.sectorY + 1)); | |
console.log("Photon Torpedoes: " + gameState.torpedoes); | |
console.log("Total Energy: " + (gameState.energy + gameState.shields)); | |
console.log("Shields: " + gameState.shields); | |
console.log("Klingons Remaining:" + gameState.totalKlingons); | |
} | |
// Long Range Scan | |
function longRangeScan() { | |
console.log("Long Range Scan for Quadrant " + (gameState.quadrantX + 1) + "," + (gameState.quadrantY + 1)); | |
console.log("-------------------"); | |
for (let x = gameState.quadrantX - 1; x <= gameState.quadrantX + 1; x++) { | |
let row = ": "; | |
for (let y = gameState.quadrantY - 1; y <= gameState.quadrantY + 1; y++) { | |
if (x >= 0 && x < GALAXY_SIZE && y >= 0 && y < GALAXY_SIZE) { | |
let q = galaxy[x][y]; | |
row += | |
" " + | |
String(q.klingons * 100 + q.starbases * 10 + q.stars).padStart(3, "0") + | |
" "; | |
} else { | |
row += " *** "; | |
} | |
} | |
console.log(row + ":"); | |
} | |
console.log("-------------------"); | |
} | |
// Fire Phasers | |
function firePhasers() { | |
if (gameState.klingonsInQuadrant <= 0) { | |
console.log("Science Officer Spock reports, 'Sensors show no enemy ships in this quadrant.'"); | |
return; | |
} | |
console.log("Phasers locked on target; Energy available = " + gameState.energy + " units"); | |
let energyToFire = parseFloat(prompt("Number of units to fire? ")); | |
if (energyToFire <= 0 || energyToFire > gameState.energy) { | |
console.log("Invalid amount of energy."); | |
return; | |
} | |
gameState.energy -= energyToFire; | |
let totalDamage = energyToFire; | |
let damagePerKlingon = totalDamage / gameState.klingonsInQuadrant; | |
for (let i = 0; i < gameState.klingonPositions.length; i++) { | |
let klingon = gameState.klingonPositions[i]; | |
if (klingon.energy > 0) { | |
let distance = Math.sqrt( | |
Math.pow(klingon.x - gameState.sectorX, 2) + Math.pow(klingon.y - gameState.sectorY, 2) | |
); | |
let damage = damagePerKlingon / distance; | |
klingon.energy -= damage; | |
if (klingon.energy <= 0) { | |
console.log("*** Klingon destroyed at sector " + (klingon.x + 1) + "," + (klingon.y + 1) + " ***"); | |
gameState.sectorMap[klingon.x][klingon.y] = " "; | |
gameState.klingonsInQuadrant--; | |
gameState.totalKlingons--; | |
gameState.klingonPositions.splice(i, 1); | |
i--; | |
if (gameState.totalKlingons <= 0) { | |
victory(); | |
return; | |
} | |
} else { | |
console.log( | |
damage.toFixed(2) + | |
" unit hit on Klingon at sector " + | |
(klingon.x + 1) + | |
"," + | |
(klingon.y + 1) + | |
" (Remaining energy: " + | |
klingon.energy.toFixed(2) + | |
")" | |
); | |
} | |
} | |
} | |
} | |
// Fire Torpedoes | |
function fireTorpedoes() { | |
if (gameState.torpedoes <= 0) { | |
console.log("All photon torpedoes expended"); | |
return; | |
} | |
let course = parseFloat(prompt("Photon torpedo course (1-9)? ")); | |
if (course === 9) course = 1; | |
if (course < 1 || course >= 9) { | |
console.log("Ensign Chekov reports, 'Incorrect course data, sir!'"); | |
return; | |
} | |
gameState.torpedoes--; | |
gameState.energy -= 2; | |
let direction = getDirectionVector(course); | |
let x = gameState.sectorX; | |
let y = gameState.sectorY; | |
console.log("Torpedo track:"); | |
while (true) { | |
x += direction.dx; | |
y += direction.dy; | |
if (x < 0 || x >= SECTOR_SIZE || y < 0 || y >= SECTOR_SIZE) { | |
console.log("Torpedo missed"); | |
break; | |
} | |
console.log(" " + (x + 1) + "," + (y + 1)); | |
let object = gameState.sectorMap[x][y]; | |
if (object === " * ") { | |
console.log("Star at " + (x + 1) + "," + (y + 1) + " absorbed torpedo energy."); | |
break; | |
} else if (object === "+K+") { | |
console.log("*** Klingon destroyed ***"); | |
gameState.sectorMap[x][y] = " "; | |
gameState.klingonsInQuadrant--; | |
gameState.totalKlingons--; | |
removeKlingonAtPosition(x, y); | |
if (gameState.totalKlingons <= 0) { | |
victory(); | |
return; | |
} | |
break; | |
} else if (object === ">!<") { | |
console.log("*** Starbase destroyed ***"); | |
gameState.starbasesInQuadrant--; | |
gameState.starbases--; | |
gameState.sectorMap[x][y] = " "; | |
if (gameState.starbases <= 0) { | |
console.log("That does it, Captain!! You are hereby relieved of command and sent to the nearest starbase for trial!"); | |
gameState.destroyed = true; | |
} | |
break; | |
} | |
} | |
} | |
function removeKlingonAtPosition(x, y) { | |
for (let i = 0; i < gameState.klingonPositions.length; i++) { | |
if (gameState.klingonPositions[i].x === x && gameState.klingonPositions[i].y === y) { | |
gameState.klingonPositions.splice(i, 1); | |
return; | |
} | |
} | |
} | |
// Shield Control | |
function shieldControl() { | |
console.log("Energy available = " + (gameState.energy + gameState.shields)); | |
let shieldLevel = parseFloat(prompt("Number of units to shields? ")); | |
if (shieldLevel < 0 || shieldLevel > gameState.energy + gameState.shields) { | |
console.log("Shield control reports 'This is not the Federation treasury.'"); | |
return; | |
} | |
gameState.energy += gameState.shields - shieldLevel; | |
gameState.shields = shieldLevel; | |
console.log("Deflector control room report:"); | |
console.log(" 'Shields now at " + gameState.shields + " units per your command.'"); | |
} | |
// Damage Report | |
function damageReport() { | |
console.log("Device State of Repair"); | |
for (let i = 0; i < devices.length; i++) { | |
console.log( | |
devices[i].padEnd(25, " ") + (gameState.damage[i] >= 0 ? gameState.damage[i].toFixed(2) : "INOPERABLE") | |
); | |
} | |
} | |
// Library Computer | |
function libraryComputer() { | |
console.log("Functions available from library-computer:"); | |
console.log(" 0 = Cumulative Galactic Record"); | |
console.log(" 1 = Status Report"); | |
console.log(" 2 = Photon Torpedo Data"); | |
console.log(" 3 = Starbase Nav Data"); | |
console.log(" 4 = Direction/Distance Calculator"); | |
console.log(" 5 = Galaxy 'Region Name' Map"); | |
let func = parseInt(prompt("Computer active and awaiting command? ")); | |
switch (func) { | |
case 0: | |
cumulativeGalacticRecord(); | |
break; | |
case 1: | |
statusReport(); | |
break; | |
case 2: | |
photonTorpedoData(); | |
break; | |
case 3: | |
starbaseNavData(); | |
break; | |
case 4: | |
directionDistanceCalculator(); | |
break; | |
case 5: | |
galaxyRegionNameMap(); | |
break; | |
default: | |
console.log("Invalid function."); | |
} | |
} | |
function cumulativeGalacticRecord() { | |
console.log("Computer record of galaxy for quadrant " + (gameState.quadrantX + 1) + "," + (gameState.quadrantY + 1)); | |
console.log(" 1 2 3 4 5 6 7 8"); | |
console.log(" ----- ----- ----- ----- ----- ----- ----- -----"); | |
for (let y = 0; y < GALAXY_SIZE; y++) { | |
let row = (y + 1).toString(); | |
for (let x = 0; x < GALAXY_SIZE; x++) { | |
let q = galaxy[x][y]; | |
row += " "; | |
row += String(q.klingons * 100 + q.starbases * 10 + q.stars).padStart(3, "0"); | |
} | |
console.log(row); | |
} | |
console.log(" ----- ----- ----- ----- ----- ----- ----- -----"); | |
} | |
function statusReport() { | |
console.log(" Status Report:"); | |
console.log("Klingons left: " + gameState.totalKlingons); | |
console.log("Mission must be completed in " + gameState.timeRemaining + " stardates"); | |
if (gameState.starbases > 0) { | |
console.log("The Federation is maintaining " + gameState.starbases + " starbase(s) in the galaxy"); | |
} else { | |
console.log("Your stupidity has left you on your own in the galaxy -- you have no starbases left!"); | |
} | |
} | |
function photonTorpedoData() { | |
if (gameState.klingonsInQuadrant <= 0) { | |
console.log("Science Officer Spock reports, 'Sensors show no enemy ships in this quadrant.'"); | |
return; | |
} | |
console.log("From Enterprise to Klingon Battle Cruiser(s):"); | |
for (let klingon of gameState.klingonPositions) { | |
directionDistanceCalculatorCustom( | |
gameState.sectorX, | |
gameState.sectorY, | |
klingon.x, | |
klingon.y, | |
"Klingon at sector " + (klingon.x + 1) + "," + (klingon.y + 1) | |
); | |
} | |
} | |
function starbaseNavData() { | |
if (gameState.starbasesInQuadrant <= 0) { | |
console.log("Mr. Spock reports, 'Sensors show no starbases in this quadrant.'"); | |
return; | |
} | |
console.log("From Enterprise to Starbase:"); | |
directionDistanceCalculatorCustom( | |
gameState.sectorX, | |
gameState.sectorY, | |
gameState.starbasePosition.x, | |
gameState.starbasePosition.y, | |
"Starbase at sector " + (gameState.starbasePosition.x + 1) + "," + (gameState.starbasePosition.y + 1) | |
); | |
} | |
function directionDistanceCalculator() { | |
let x1 = parseInt(prompt("Enter initial coordinates X1 (1-8): ")) - 1; | |
let y1 = parseInt(prompt("Enter initial coordinates Y1 (1-8): ")) - 1; | |
let x2 = parseInt(prompt("Enter final coordinates X2 (1-8): ")) - 1; | |
let y2 = parseInt(prompt("Enter final coordinates Y2 (1-8): ")) - 1; | |
directionDistanceCalculatorCustom(x1, y1, x2, y2, "Coordinates"); | |
} | |
function directionDistanceCalculatorCustom(x1, y1, x2, y2, label) { | |
let dx = x2 - x1; | |
let dy = y2 - y1; | |
let distance = Math.sqrt(dx * dx + dy * dy); | |
let angle = (Math.atan2(dy, dx) * 180) / Math.PI; | |
if (angle < 0) angle += 360; | |
let direction = Math.round(angle / 45) + 1; | |
if (direction > 8) direction = 1; | |
console.log(label); | |
console.log("Direction = " + direction); | |
console.log("Distance = " + distance.toFixed(2)); | |
} | |
function galaxyRegionNameMap() { | |
console.log(" The Galaxy"); | |
console.log(" 1 2 3 4 5 6 7 8"); | |
console.log(" ----- ----- ----- ----- ----- ----- ----- -----"); | |
for (let y = 0; y < GALAXY_SIZE; y++) { | |
let row = (y + 1).toString(); | |
for (let x = 0; x < GALAXY_SIZE; x++) { | |
let name = getQuadrantName(x, y).padEnd(5, " ").substring(0, 5); | |
row += " " + name; | |
} | |
console.log(row); | |
} | |
console.log(" ----- ----- ----- ----- ----- ----- ----- -----"); | |
} | |
function getQuadrantName(x, y) { | |
let row = y < 4 ? 0 : 1; | |
let col = x; | |
return quadrantNames[row][col]; | |
} | |
// Klingon Attack | |
function klingonAttack() { | |
for (let klingon of gameState.klingonPositions) { | |
if (klingon.energy > 0) { | |
let distance = Math.sqrt( | |
Math.pow(klingon.x - gameState.sectorX, 2) + Math.pow(klingon.y - gameState.sectorY, 2) | |
); | |
let damage = (klingon.energy / distance) * (2 + Math.random()); | |
if (gameState.shields >= damage) { | |
gameState.shields -= damage; | |
console.log( | |
damage.toFixed(2) + | |
" unit hit on Enterprise from sector " + | |
(klingon.x + 1) + | |
"," + | |
(klingon.y + 1) | |
); | |
console.log(" <Shields down to " + gameState.shields.toFixed(2) + " units>"); | |
} else { | |
console.log( | |
"*** Shields gone *** " + | |
damage.toFixed(2) + | |
" unit hit on Enterprise from sector " + | |
(klingon.x + 1) + | |
"," + | |
(klingon.y + 1) | |
); | |
gameState.destroyed = true; | |
break; | |
} | |
} | |
} | |
} | |
// Victory | |
function victory() { | |
console.log("Congratulations, Captain! The last Klingon battle cruiser menacing the Federation has been destroyed."); | |
let rating = (1000 * Math.pow(gameState.initialKlingons / (gameState.stardate - gameState.initialStardate), 2)).toFixed( | |
2 | |
); | |
console.log("Your efficiency rating is " + rating); | |
} | |
// Defeat | |
function defeat() { | |
console.log("The Enterprise has been destroyed. The Federation will be conquered."); | |
} | |
// Resignation | |
function resignation() { | |
console.log("You have resigned your command."); | |
} | |
// Out of Time | |
function outOfTime() { | |
console.log("It is stardate " + gameState.stardate); | |
console.log("There were " + gameState.totalKlingons + " Klingon battle cruisers left at the end of your mission."); | |
console.log("The Federation is in need of a new starship commander for a similar mission."); | |
} | |
// Instructions for "Super Star Trek" Game | |
// Original BASIC code translated to JavaScript | |
"use strict"; | |
// Function to simulate BASIC's PRINT statements with formatting | |
function print(text = "", tab = 0) { | |
if (tab > 0) { | |
console.log(" ".repeat(tab) + text); | |
} else { | |
console.log(text); | |
} | |
} | |
// Function to simulate BASIC's INPUT statement | |
function input(promptText) { | |
// For simplicity, we use prompt() for user input | |
return prompt(promptText); | |
} | |
// Main function to display the instructions | |
function displayInstructions() { | |
// Clear the console (simulating multiple newlines) | |
console.clear(); | |
for (let i = 1; i <= 12; i++) { | |
print(); | |
} | |
// Display the title with formatting | |
print("*************************************", 10); | |
print("* *", 10); | |
print("* *", 10); | |
print("* * * SUPER STAR TREK * * *", 10); | |
print("* *", 10); | |
print("* *", 10); | |
print("*************************************", 10); | |
for (let i = 1; i <= 8; i++) { | |
print(); | |
} | |
// Ask the user if they need instructions | |
let needInstructions = input("Do you need instructions (Y/N)? ").toUpperCase(); | |
if (needInstructions === "N") { | |
return; | |
} | |
print(); | |
// Display the instructions | |
print(" Instructions for 'Super Star Trek'"); | |
print(); | |
print("1. When you see 'COMMAND ?' printed, enter one of the legal"); | |
print(" commands (NAV, SRS, LRS, PHA, TOR, SHE, DAM, COM, or XXX)."); | |
print("2. If you should type in an illegal command, you'll get a short"); | |
print(" list of the legal commands printed out."); | |
print("3. Some commands require you to enter data (for example, the"); | |
print(" 'NAV' command comes back with 'COURSE (1-9)?'.) If you"); | |
print(" type in illegal data (like negative numbers), that command"); | |
print(" will be aborted"); | |
print(); | |
print(" The galaxy is divided into an 8 x 8 quadrant grid,"); | |
print("and each quadrant is further divided into an 8 x 8 sector grid."); | |
print(); | |
print(" You will be assigned a starting point somewhere in the"); | |
print("galaxy to begin a tour of duty as commander of the starship"); | |
print("'Enterprise'; your mission: to seek and destroy the fleet of"); | |
print("Klingon warships which are menacing the United Federation of"); | |
print("Planets."); | |
print(); | |
print(" You have the following commands available to you as captain"); | |
print("of the starship Enterprise:"); | |
print(); | |
print("'NAV' Command = Warp Engine Control --"); | |
print(" Course is in a circular numerical 4 3 2"); | |
print(" vector arrangement as shown . . ."); | |
print(" Integer and real values may be ..."); | |
print(" used. (Thus, course 1.5 is half- 5 ---*--- 1"); | |
print(" way between 1 and 2) ..."); | |
print(" . . ."); | |
print(" Values may approach 9.0, which 6 7 8"); | |
print(" itself is equivalent to 1.0"); | |
print(" COURSE"); | |
print(" One warp factor is the size of"); | |
print(" one quadrant. Therefore, to get"); | |
print(" from quadrant 6,5 to 5,5, you would"); | |
print(" use course 3, warp factor 1."); | |
print(); | |
print("'SRS' Command = Short Range Sensor Scan"); | |
print(" Shows you a scan of your present quadrant."); | |
print(); | |
print(" Symbology on your sensor screen is as follows:"); | |
print(" <*> = Your starship's position"); | |
print(" +K+ = Klingon battle cruiser"); | |
print(" >!< = Federation starbase (Refuel/Repair/Re-Arm here!)"); | |
print(" * = Star"); | |
print(); | |
print(" A condensed 'Status Report' will also be presented."); | |
print(); | |
print("'LRS' Command = Long Range Sensor Scan"); | |
print(" Shows conditions in space for one quadrant on each side"); | |
print(" of the Enterprise (which is in the middle of the scan)."); | |
print(" The scan is coded in the form '###', where the units digit"); | |
print(" is the number of stars, the tens digit is the number of"); | |
print(" starbases, and the hundreds digit is the number of"); | |
print(" Klingons."); | |
print(); | |
print(" Example - 207 = 2 Klingons, no starbases, & 7 stars."); | |
print(); | |
print("'PHA' Command = Phaser Control."); | |
print(" Allows you to destroy the Klingon battle cruisers by"); | |
print(" zapping them with suitably large units of energy to"); | |
print(" deplete their shield power. (Remember, Klingons have"); | |
print(" phasers too!)"); | |
print(); | |
print("'TOR' Command = Photon Torpedo Control"); | |
print(" Torpedo course is the same as used in Warp Engine Control."); | |
print(" If you hit the Klingon vessel, he is destroyed and"); | |
print(" cannot fire back at you. If you miss, you are subject to"); | |
print(" his phaser fire. In either case, you are also subject to"); | |
print(" the phaser fire of all other Klingons in the quadrant."); | |
print(); | |
print(" The Library-Computer ('COM' command) has an option to"); | |
print(" compute torpedo trajectory for you (Option 2)."); | |
print(); | |
print("'SHE' Command = Shield Control"); | |
print(" Defines the number of energy units to be assigned to the"); | |
print(" shields. Energy is taken from total ship's energy. Note"); | |
print(" that the status display total energy includes shield energy."); | |
print(); | |
print("'DAM' Command = Damage Control Report"); | |
print(" Gives the state of repair of all devices. Where a negative"); | |
print(" 'state of repair' shows that the device is temporarily"); | |
print(" damaged."); | |
print(); | |
print("'COM' Command = Library-Computer"); | |
print(" The Library-Computer contains six options:"); | |
print(" Option 0 = Cumulative Galactic Record"); | |
print(" This option shows computer memory of the results of all"); | |
print(" previous short and long range sensor scans."); | |
print(" Option 1 = Status Report"); | |
print(" This option shows the number of Klingons, Stardates,"); | |
print(" and Starbases remaining in the game."); | |
print(" Option 2 = Photon Torpedo Data"); | |
print(" Which gives directions and distance from the Enterprise"); | |
print(" to all Klingons in your quadrant."); | |
print(" Option 3 = Starbase Nav Data"); | |
print(" This option gives direction and distance to any"); | |
print(" starbase within your quadrant."); | |
print(" Option 4 = Direction/Distance Calculator"); | |
print(" This option allows you to enter coordinates for"); | |
print(" direction/distance calculations."); | |
print(" Option 5 = Galactic 'Region Name' Map"); | |
print(" This option prints the names of the sixteen major"); | |
print(" galactic regions referred to in the game."); | |
} | |
// Call the function to display instructions | |
displayInstructions(); | |
// Note: In a real game, the code would proceed to the main game logic here. | |
// Start the game | |
initializeGame(); | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment