diff --git a/src/game/phases/setup.ts b/src/game/phases/setup.ts new file mode 100644 index 0000000..da2a297 --- /dev/null +++ b/src/game/phases/setup.ts @@ -0,0 +1,264 @@ +import GameState, { PHASE_ENUM } from '../gameState'; +import GameFlow from '../gameFlow'; +import { MessageService, MessageFileName } from '../../utils/MessageService' + +export class HandleSetup { + static async handleSetupPhase(gameState: GameState, gameFlow: GameFlow, userInput?: string): Promise { + userInput = userInput?.toLowerCase() + let responseMessage = ""; + // Logic to handle initial setup + switch (gameState.subPhase) { + case 0: + // Weapons Purchase Subphase + if (!userInput || userInput === 'start' || userInput === 'restart') { + // Send instructions to the user for the current subphase + responseMessage = `${MessageService.getRandomMessage( + MessageFileName.Oregon_WeaponPurchase, + {} + )}`; + // The lack of userInput argument triggers a request for input. + } else { + // Process user input here and decide whether to advance subphase or send an error message + const purchaseResult = HandleSetup.handleWeaponPurchase(gameState, userInput); + if (purchaseResult.success) { + // If the purchase was successful, advance to the next subphase and recursively call handleSetupPhase without userInput to get the next set of instructions + gameState.subPhase++; + return HandleSetup.handleSetupPhase(gameState, gameFlow); + } else { + // If there was an error, include the error message in responseMessage + responseMessage = `${purchaseResult.errorMessage}`; + responseMessage += `\n\n${MessageService.getRandomMessage( + MessageFileName.Oregon_WeaponPurchase, + {} + )}` + // Do not advance subphase; possibly prompt the same instructions with an error message + } + } + break; + case 1: + //OXEN TEAM Purchase + if (!userInput) { + responseMessage = `${MessageService.getRandomMessage( + MessageFileName.Oregon_OxenPurchase, + {} + )}`; + } else { + const purchaseResult = HandleSetup.handleGenericPurchase(gameState, PurchaseOption.OXEN, userInput, 200, 300); + if (purchaseResult.success) { + gameState.subPhase++; + return HandleSetup.handleSetupPhase(gameState, gameFlow); // Call setup phase again for next subphase + } else { + responseMessage = purchaseResult.errorMessage || ''; + responseMessage += `\n\n${MessageService.getRandomMessage( + MessageFileName.Oregon_OxenPurchase, + {} + )}` + } + } + break; + case 2: + //FOOD Purchase + if (!userInput) { + responseMessage = `${MessageService.getRandomMessage( + MessageFileName.Oregon_FoodPurchase, + {} + )}`; + } else { + const purchaseResult = HandleSetup.handleGenericPurchase(gameState, PurchaseOption.FOOD, userInput); + if (purchaseResult.success) { + gameState.subPhase++; + return HandleSetup.handleSetupPhase(gameState, gameFlow); // Call setup phase again for next subphase + } else { + responseMessage = purchaseResult.errorMessage || ''; + responseMessage += `\n\n${MessageService.getRandomMessage( + MessageFileName.Oregon_FoodPurchase, + {} + )}` + } + } + break; + case 3: + //AMMUNITION Purchase + if (!userInput) { + responseMessage = `${MessageService.getRandomMessage( + MessageFileName.Oregon_AmmoPurchase, + {} + )}`; + } else { + const purchaseResult = HandleSetup.handleGenericPurchase(gameState, PurchaseOption.AMMO, userInput); + if (purchaseResult.success) { + gameState.subPhase++; + return HandleSetup.handleSetupPhase(gameState, gameFlow); // Call setup phase again for next subphase + } else { + responseMessage = purchaseResult.errorMessage || ''; + responseMessage += `\n\n${MessageService.getRandomMessage( + MessageFileName.Oregon_AmmoPurchase, + {} + )}` + } + } + break; + case 4: + //CLOTHING Purchase + if (!userInput) { + responseMessage = `${MessageService.getRandomMessage( + MessageFileName.Oregon_ClothingPurchase, + {} + )}`; + } else { + const purchaseResult = HandleSetup.handleGenericPurchase(gameState, PurchaseOption.CLOTHING, userInput); + if (purchaseResult.success) { + gameState.subPhase++; + return HandleSetup.handleSetupPhase(gameState, gameFlow); // Call setup phase again for next subphase + } else { + responseMessage = purchaseResult.errorMessage || ''; + responseMessage += `\n\n${MessageService.getRandomMessage( + MessageFileName.Oregon_ClothingPurchase, + {} + )}` + } + } + break; + case 5: + //MISCELLANEOUS SUPPLIES Purchase + if (!userInput) { + responseMessage = `${MessageService.getRandomMessage( + MessageFileName.Oregon_MiscSuppliesPurchase, + {} + )}`; + } else { + const purchaseResult = HandleSetup.handleGenericPurchase(gameState, PurchaseOption.MISC, userInput); + if (purchaseResult.success) { + gameState.subPhase++; + return HandleSetup.handleSetupPhase(gameState, gameFlow); // Call setup phase again for next subphase + } else { + responseMessage = purchaseResult.errorMessage || ''; + responseMessage += `\n\n${MessageService.getRandomMessage( + MessageFileName.Oregon_MiscSuppliesPurchase, + {} + )}` + } + } + break; + case 6: + //Advance Phase + gameState.subPhase = 0; + gameState.phase === PHASE_ENUM.SETUP ? gameState.phase = PHASE_ENUM.START_TURN : gameState.phase = PHASE_ENUM.ACTION_CHOICE; + await gameState.save() + gameFlow.executeCurrentPhase() + break; + default: + responseMessage = `@J something went wrong come and fix it.\n\n ${JSON.stringify(gameState, null, 4)}` + break; + } + + await gameFlow.statusUpdate(responseMessage) + return; + } + + static handleWeaponPurchase(gameState: GameState, userInput: string): { success: boolean; errorMessage?: string } { + + // Check if the userInput is one of the valid choices + if (!Object.keys(weaponOptions).includes(userInput)) { + // If the input is not valid, return an error message + return { + success: false, + errorMessage: "⚠️ Invalid choice. Please select a valid weapon option by replying with !!Oregon [choice]." + }; + } + + // Check if the user has enough money for the selected weapon + const selectedWeapon = weaponOptions[userInput]; + if (gameState.cashLeftAfterInitialPurchases < selectedWeapon.cost) { + // If not enough money, return an error message + return { + success: false, + errorMessage: `⚠️ You do not have enough money for the ${selectedWeapon.name}. You have $${gameState.cashLeftAfterInitialPurchases} remaining.` + }; + } + + // Update gameState with the selected weapon and deduct the cost + gameState.shootingExpertiseLevelChoice = parseInt(userInput); + gameState.cashLeftAfterInitialPurchases -= selectedWeapon.cost; + + // Return a success object, indicating the purchase was successful + return { + success: true + }; + } + + static handleGenericPurchase(gameState: GameState, itemType: PurchaseOption, userInput: string, minAmount?: number, maxAmount?: number): { success: boolean; errorMessage?: string; responseMessage?: string } { + const amount = parseInt(userInput); + if (isNaN(amount) || amount < 0) { + return { + success: false, + errorMessage: "⚠️ Invalid input. Please enter a positive number." + }; + } + + if ((maxAmount && amount > maxAmount) || (minAmount && amount < minAmount)) { + return { + success: false, + errorMessage: `⚠️ Invalid input. Please enter a number between ${minAmount} and ${maxAmount}.` + }; + } + + const totalCost = amount + if (gameState.cashLeftAfterInitialPurchases < totalCost) { + return { + success: false, + errorMessage: `⚠️ You do not have enough money to purchase ${amount} units of ${itemType}. You have $${gameState.cashLeftAfterInitialPurchases} remaining.` + }; + } + let modifier: number + gameState.phase !== PHASE_ENUM.FORT ? modifier = 1 : modifier = 2 / 3; + // Update gameState based on itemType + switch (itemType) { + case PurchaseOption.OXEN: + //Round down after multiplication + gameState.amountSpentOnAnimals += Math.floor(totalCost * modifier); + break; + case PurchaseOption.FOOD: + gameState.amountSpentOnFood += Math.floor(totalCost * modifier); + break; + case PurchaseOption.AMMO: + gameState.amountSpentOnAmmunition += Math.floor(totalCost * 50 * modifier); + break; + case PurchaseOption.CLOTHING: + gameState.amountSpentOnClothing += Math.floor(totalCost * modifier); + break; + case PurchaseOption.MISC: + gameState.amountSpentOnMiscellaneousSupplies += Math.floor(totalCost * modifier); + break; + // Add cases for other item types + } + + gameState.cashLeftAfterInitialPurchases -= totalCost; + + // Optionally, return a success message + return { + success: true + }; + } +} + +interface WeaponOption { + name: string; + cost: number; +} + +const weaponOptions: Record = { + "1": { name: "Frontiersman's Sharpshooter Rifle", cost: 200 }, + "2": { name: "Pioneer's Long Rifle", cost: 150 }, + "3": { name: "Settler's Carbine", cost: 100 }, + "4": { name: "Homesteader's Musket", cost: 50 }, + "5": { name: "Grandpa's Rusty Shotgun", cost: 0 } +}; + +enum PurchaseOption { + OXEN = 'oxen', + FOOD = 'food', + AMMO = 'ammo', + CLOTHING = 'clothing', + MISC = 'misc', +}; \ No newline at end of file