//Created by student A and B collaboratively // Holds all functions used in multiple files import { createRequire } from 'module'; const require = createRequire(import.meta.url); var readlineSync = require('readline-sync'); const { exec } = require("child_process"); import { promisify } from "util"; const execPromise = promisify(exec); import './variables.js'; import { inventoryMenuM } from './inventory.js'; import { checkPOI } from './poiscreens.js'; import './poiscreens.js'; import os from 'os'; import { exit } from 'process'; import { naturalEncounter } from './encounters.js'; global.isRawModeEnabled = false; // Track whether raw mode is enabled // Used for basic functions, not game specific export function userInput(question) { // Basic user input functions, takes in the question to be asked. let answer = readlineSync.question(question); return(answer); } export function randomNumber(min, max) { // Takes in two parameters, min and max, and returns a random number between those boundries. while (true) { var randNum = Math.floor(Math.random() * (max+1)); if (randNum >= min) { break; } } return randNum; } // Used for game export function healthCapCalc() { // Calculates HP cap given current radPoints healthCap = healthCap-radPoints; if (health > healthCap) { health = healthCap; } } export function radPointsCalc() { // Calculates radPoints given walk/foodrate if (isRadiated == true) { radPoints = radPoints + 0.5 * (radSeverity * 10 + 5 * (walkRate + foodRate)); } } export function eatFood() { // Calculates food amount after a day of eating if (food == 0) { health = health - walkRate } else { food = food - walkRate * foodRate } } export function checkLose() { // Checks to see if any lose conditions have been met if (health <= 0 && food <= 0) { death("Starvation"); } if (radPoints >= 100) { death("Radiation Poisoning"); } if (health <= 0 && incombat == true) { death("Combat Injury"); } if (health <= 0) { death("Injury"); } if (day >= 365) { timeDeath(); } if (testDeath == true) { death("Test Death"); } } // Checks to see what path the player is on and adds the respective amount to the respective location variable export function calcLocation() { if (walkRate == 1) { if (path == 0) { location++; } if (path == 1) { locationA++; } if (path == 2) { locationB++; } } if (walkRate == 2) { if (path == 0) { location + 2; } if (path == 1) { locationA + 2; } if (path == 2) { locationB + 2; } } if (walkRate == 3) { if (path == 0) { location + 3; } if (path == 1) { locationA + 3; } if (path == 2) { locationB + 3; } } } // sleep() function // First detects what os the user is on and assigns it to osPlat // if osPlat == 'win32' the program runs the shell command 'sleep' with powershell and the desired time parameter // if it is not equal to 'win32' the program just runs 'sleep' with the desired time parameter var osPlat = os.platform(); export async function sleep(time) { if (osPlat === 'win32') { try { await execPromise(`powershell sleep ${time}`); } catch (error) { console.error("Error executing sleep command:", error); } } else { try { await execPromise(`sleep ${time}`); } catch (error) { console.error("Error executing sleep command:", error); } } } // Calculates a random number to see which death screen you recieve if called and imputs the death cause as a parameter. function death(deathCause) { let random = randomNumber(1,100); if (random >= 1 && random <= 33) { death1(deathCause); } if (random >= 34 && random <= 66) { death2(deathCause); } if (random >= 67 && random <= 99) { death3(deathCause); } if (random == 100) { death4(deathCause); } } // death1, death2, and death3 all display a death message, similar to those in Fallout 1, along with the cause given as a parameter function death1(dc) { console.clear(); sleep(2); console.log("Your bones wither away in the wasteland...\n\n"); console.log(" \\|/"); console.log(" _/\\___<0> | "); console.log("-------------------------------\n\n"); console.log("You have died of "+dc+"..."); console.log("Thanks for playing!\n\n"); sleep(5); exit(0); } function death2(dc) { console.clear(); sleep(2); console.log("The Enclave suffers in the wake of your defeat...\n\n"); console.log(" \\|/"); console.log(" _/\\___<0> | "); console.log("-------------------------------\n\n"); console.log("You have died of "+dc+"..."); console.log("Thanks for playing!\n\n"); sleep(5); exit(0); } function death3(dc) { console.clear(); sleep(2); console.log("Not even the radscorpions are interested in your corpse...\n\n"); console.log(" \\|/"); console.log(" _/\\___<0> | "); console.log("-------------------------------\n\n"); console.log("You have died of "+dc+"..."); console.log("Thanks for playing!\n\n"); sleep(5); exit(0); } function death4(dc) { console.clear(); sleep(2); console.log("You have been killed, therefore you are dead...\n\n"); console.log(" \\|/"); console.log(" _/\\___<0> | "); console.log("-------------------------------\n\n"); console.log("You have died of "+dc+"..."); console.log("Thanks for playing!\n\n"); sleep(5); exit(0); } function timeDeath() { // The death screen for passing the time limit console.clear; console.log("Your PIP-Boy starts ringing...\n\n") console.log("BEEP...BEEP...BEEP"); console.log("INCOMING MESAGE FROM: Raven_Rock_Comms-1\n\n"); console.log("|||||||") console.log("|| Enclave Intranet Messager"); console.log("|||||||") console.log("|| Message From: automessager"); console.log("|||||||\n\n") console.log("Dear, "+name+",\n\n"); console.log("You have failed to faithfully execute the tasks laid before you in a timely manner."); console.log("In consequence you have been dishonorably discharged from the Enclave Armed Forces"); console.log("Please return to the nearest active Enclave Military Installation immeditely\n\n"); console.log("Signed, Enclave Internal Messager"); console.clear; } var travelFrame = 0; // Shows the 'frames' of the travel screen, and shows some player stats. export function travelScreen() { if (travelFrame < 0 || travelFrame > 3) { travelFrame = 0; } else if (travelFrame == 0) { console.clear(); console.log("┌──────────────────────────────────────┐"); console.log("│ │"); console.log("│ │"); console.log("│ │"); console.log("│ │"); console.log("│ ◜─◝ │"); console.log("│ ◟_◞ │"); console.log("│ /║\\ │"); console.log("│ \\V/ │"); console.log("│ / \\ │"); console.log("│ ▁\\ ▔╹ │"); console.log("│======================================│"); console.log("│ . . - . . - . . . - │"); console.log("│ - . . . . . . - . .│"); console.log("└──────────────────────────────────────┘"); console.log("Press [SPACE] to open Pip-Boy"); console.log("Current Health: "+health); console.log("Food Amt: "+food); if (path == 0) { console.log("Distance to next POI: " + (pois[poiCounter].location-location)); } if (path == 1) { console.log("Distance to next POI: " + (pois[poiCounter].location-locationA)); } if (path == 2) { console.log("Distance to next POI: " + (pois[poiCounter].location-locationB)); } travelFrame++ } else if (travelFrame == 1) { console.clear(); console.log("┌──────────────────────────────────────┐"); console.log("│ │"); console.log("│ │"); console.log("│ │"); console.log("│ │"); console.log("│ ◜─◝ │"); console.log("│ ◟_◞ │"); console.log("│ /║\\ │"); console.log("│ \\V/ │"); console.log("│ \\ │"); console.log("│ ▁│▔╹ │"); console.log("│======================================│"); console.log("│ - . - . . . - . - . │"); console.log("│ - . . - . . . - .│"); console.log("└──────────────────────────────────────┘"); console.log("Press [SPACE] to open Pip-Boy"); console.log("Current Health: "+health); console.log("Food Amt: "+food); console.log("Distance to next POI: " + (pois[poiCounter].location-location)); travelFrame++ } else if (travelFrame == 2) { console.clear(); console.log("┌──────────────────────────────────────┐"); console.log("│ │"); console.log("│ │"); console.log("│ │"); console.log("│ │"); console.log("│ ◜─◝ │"); console.log("│ ◟_◞ │"); console.log("│ /║\\ │"); console.log("│ \\V/ │"); console.log("│ ╳ │"); console.log("│ _/_\\ │"); console.log("│======================================│"); console.log("│ - . - . . . - . - . │"); console.log("│ - . . - . . . - .│"); console.log("└──────────────────────────────────────┘"); console.log("Press [SPACE] to open Pip-Boy"); console.log("Current Health: "+health); console.log("Food Amt: "+food); console.log("Distance to next POI: " + (pois[poiCounter].location-location)); travelFrame++ } else if (travelFrame == 3) { console.clear(); console.log("┌──────────────────────────────────────┐"); console.log("│ │"); console.log("│ │"); console.log("│ │"); console.log("│ │"); console.log("│ ◜─◝ │"); console.log("│ ◟_◞ │"); console.log("│ /║\\ │"); console.log("│ \\V/ │"); console.log("│ \\ │"); console.log("│ ▁│▔╹ │"); console.log("│======================================│"); console.log("│ - . - . . . - . - . │"); console.log("│ - . . - . . . - .│"); console.log("└──────────────────────────────────────┘"); console.log("Press [SPACE] to open Pip-Boy"); console.log("Current Health: "+health); console.log("Food Amt: "+food); console.log("Distance to next POI: " + (pois[poiCounter].location-location)); travelFrame=0; } } // This function is run everytime the player 'moves' in the world. It first enables raw mode to detect for the pause button // If paused it displays pauseScreen(); // If not it runs calcLocation(), radPointsCalc(), healthCapCalc(), checkLose(), travelScreen(), checkPOI(), naturalEncounter(), eatFood(), adds to the day counter, and then re runs the function. export async function walkPathMain() { enableRawMode(); if (paused) { disableRawMode(); pauseScreen(); enableRawMode(); } calcLocation(); radPointsCalc(); healthCapCalc(); checkLose(); travelScreen(); checkPOI(); naturalEncounter(location); eatFood(); day++ await sleep(0.5); walkPathMain(); } // This checks to see if the spacebar is pressed while raw menu is enabled. // If so it sets paused to true. process.stdin.resume(); process.stdin.on('data', (key) => { if (key.toString() === ' ') { paused = true; // Pause the game } }); // Turns raw mode on, runs the above function when called and sets the variable isRawModeEnabled to true export function enableRawMode() { if (!isRawModeEnabled) { process.stdin.setRawMode(true); process.stdin.resume(); isRawModeEnabled = true; } } // Turns raw mode off and sets the variable isRawModeEnabled to true export function disableRawMode() { if (isRawModeEnabled) { process.stdin.setRawMode(false); process.stdin.pause(); isRawModeEnabled = false; } } // Gives player a menu to resume the game, view inventory, distances, stats, scavenge, and change the speed/food rate var paused = false; export function pauseScreen() { let pauseInput while (true) { console.clear(); console.log ("What would you like to do?"); console.log ("--------------------------"); console.log ("1) Resume"); console.log ("2) View Inventory"); console.log ("3) View Distances"); console.log ("4) View Stats"); console.log ("5) Scavenge"); console.log ("6) Change Speed/Food Rate"); pauseInput = userInput("Enter: "); // Input validation if (pauseInput != "1" && pauseInput != "2" && pauseInput != "3" && pauseInput != "4" && pauseInput != "5" && pauseInput != "6") { console.log("Invalid input. Please enter 1, 2, or 3."); } else { break; } } switch(pauseInput) { // Shows all pois on the main path, and the chosen path if the player choses one. case "1": paused = false; break; case "2": inventoryMenuM(); break; case "3": console.clear(); if (path == 0) { console.log("Current Path: Main"); } if (path == 1) { console.log("Current Path: A (Southern)"); } if (path == 2) { console.log("Current Path: B (Northern)"); } console.log("------------------------"); for (let i = 0; i < 7; i++) { if (pois[i].visited == true) { console.log(pois[i].name+" - "+visited[i]); } if (pois[i].visited == false) { console.log("??? - "+pois[i].location-location+" location points away"); } } if (path == 1) { for (let i = poiCounter; i < 14; i++) { if (pois[i].visited == true) { console.log(pois[i].name+" - "+visited[i]); } if (pois[i].visited == false) { console.log("??? "+pois[i].location-locationA+" location points away"); } } } if (path == 2) { for (let i = poiCounter; i < pois.length; i++) { if (pois[i].visited == true) { console.log(pois[i].name+" - "+visited[i]); } if (pois[i].visited == false) { console.log("??? "+pois[i].location-locationB+" location points away"); } } } userInput("\n[Press Enter to return]"); break; case "4": // Displays many player stat variables. console.clear(); console.log("Player Stats"); console.log("-------------"); console.log("Name: "+name); console.log("Level: "+level); console.log("Current Health: "+health+"/"+healthCap); console.log("Radiation Points: "+radPoints); console.log("Radiation Severity: "+radSeverity); console.log("Walk Rate: "+walkRate); console.log("Food Amt: "+food); console.log("Food Rate: "+foodRate); console.log("Distance to next POI: " + pois[poiCounter].location-location); console.log("Current Path: "+path); userInput("\n[Press Enter to return]"); break; case "5": scavenge(); // Scavenge is curently not used in game break; case "6": // Gives the player a menu to change the walkRate and foodRate variables. console.clear(); console.log("Current Walk Rate: "+walkRate); console.log("Current Food Rate: "+foodRate); console.log("---------------------------"); console.log("What would you like to change?"); console.log("---------------------------"); console.log("1) Walk Rate"); console.log("2) Food Rate"); let rateInput = userInput("Enter: "); switch(rateInput) { case "1": console.log("Current Walk Rate: "+walkRate); let walkRateInput = userInput("Enter new Walk Rate (1-3): "); if (walkRateInput >= 1 && walkRateInput <= 3) { walkRate = walkRateInput; } else { console.log("Invalid input. Walk Rate not changed."); } break; case "2": console.log("Current Food Rate: "+foodRate); let foodRateInput = userInput("Enter new Food Rate (1-3): "); if (foodRateInput >= 1 && foodRateInput <= 3) { foodRate = foodRateInput; } else { console.log("Invalid input. Food Rate not changed."); } break; default: console.log("Invalid input. No changes made."); } userInput("\n[Press Enter to return]"); break; } }