xxxxxxxxxx
2061
// sketch.js
// ----------------------
// 1. Global Variable Declarations
// ----------------------
let page = "start"; // Initial page
let howToPlayButton, mythologyButton, connectButton, fullscreenButton, backButton; // Buttons
let timer = 45; // Total game time in seconds for word game
let countdown = timer;
let timerStart = false;
let gameOver = false;
let gameResult = "";
let score = 0;
let gameStartInitiated = false; // Flag for transitioning from gameStart to game
// Typewriter effect variables
let typewriterText = "Press the button to start your adventure!";
let currentText = "";
let textIndex = 0;
let typewriterSpeed = 20;
// Word display variables
let words = [
"The", "Mowallis", "must", "enter", "the",
"Sundarbans", "by", "boat:", "there", "are",
"no", "roads", "in", "the", "mangrove",
"forest", "that", "is", "dissected", "many",
"times", "by", "river", "and", "tidal",
"channels."
];
let currentWordIndex = 0;
let wordInterval = 4000; // first 5 words every 4s
let fastInterval = 2000; // subsequent words every 2s
let wordDisplay = "";
let wordColor = "white";
// Background Text for mythology and gameStart
let backgroundText = "The Mowallis must enter the Sundarbans by boat: there are no roads in the mangrove forest that is dissected many times by river and tidal channels.\n\nThe honey hunters work in groups of 5-8 people, spending their days venturing into the forest to seek out nests. Their nights are spent on their boats anchored as far as possible out into the river channels. The reason for this is the presence of tigers in the forest. Even the security of a boat is no guarantee of safety as tigers are reported to swim out at night and carry men away (Burton 1933).";
// Variables for serial communication
let latestData = "waiting for data";
let serial; // Serial port object
// Images
let startBg, nightBg;
// Typewriter interval ID
let typewriterIntervalID;
// Variable to store the setTimeout ID for word transitions
let wordTimeoutID;
// Debugging Mode
let debugMode = true; // Set to false when using actual sensors
// Feedback Variables
let feedbackMessage = "";
let feedbackColor;
let feedbackTimeout;
let wordTouched = false;
// ----------------------
// 2. Function Declarations
// ----------------------
// Function to set the current page and manage button visibility
function setPage(newPage) {
page = newPage;
if (page === "start") {
connectButton.show();
howToPlayButton.show();
mythologyButton.show();
fullscreenButton.show(); // Show fullscreen button on Start page
backButton.hide(); // Hide Back button on Start page
} else if (page === "howToPlay" || page === "mythology" || page === "secondGameIntro") {
connectButton.hide();
howToPlayButton.hide();
mythologyButton.hide();
fullscreenButton.hide(); // Hide fullscreen button on How to Play, Mythology, and secondGameIntro pages
backButton.show(); // Show Back button on these pages
} else if (page === "gameStart" || page === "game" || page === "won" || page === "lost" || page === "basketGame") {
connectButton.hide();
howToPlayButton.hide();
mythologyButton.hide();
fullscreenButton.hide();
backButton.hide(); // Hide Back button on game-related pages
}
}
// Function to toggle fullscreen mode
function toggleFullscreen() {
let fs = fullscreen(); // Get the current fullscreen state
fullscreen(!fs); // Toggle fullscreen state
if (!fs) {
console.log("Entered fullscreen mode.");
} else {
console.log("Exited fullscreen mode.");
}
}
// Function to style buttons (applied to all buttons including backButton)
function styleButtons(buttonColors = {}) {
// Default colors if not provided
const defaults = {
connect: '#855D08', // Default green
howToPlay: '#B77607', // Default blue
mythology: '#8C5506', // Default red
fullscreen: '#E1D8D8', // Default gray
back: '#555555' // Default gray
};
// Merge provided colors with defaults
const colors = { defaults, buttonColors };
// Common button styling
let buttonStyles = `
color: rgb(255,255,255);
font-size: 15px;
border: none;
border-radius: 5px;
padding: 8px 20px;
text-align: center;
text-decoration: none;
display: inline-block;
margin: 5px;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
`;
// Apply styles to each button
connectButton.style(buttonStyles + `background-color: ${colors.connect};`);
howToPlayButton.style(buttonStyles + `background-color: ${colors.howToPlay};`);
mythologyButton.style(buttonStyles + `background-color: ${colors.mythology};`);
fullscreenButton.style(buttonStyles + `background-color: ${colors.fullscreen};`);
backButton.style(buttonStyles + `background-color: ${colors.back};`);
// Add hover effects dynamically
[connectButton, howToPlayButton, mythologyButton, fullscreenButton, backButton].forEach((btn, index) => {
const baseColor = Object.values(colors)[index];
btn.mouseOver(() => {
btn.style('background-color', shadeColor(baseColor, -10)); // Darken color on hover
btn.style('transform', 'translateY(-2px)');
btn.style('box-shadow', '0 6px 8px rgba(0, 0, 0, 0.15)');
});
btn.mouseOut(() => {
btn.style('background-color', baseColor); // Reset to original color
btn.style('transform', 'translateY(0px)');
btn.style('box-shadow', '0 4px 6px rgba(0, 0, 0, 0.1)');
});
});
}
// Helper function to adjust color brightness
function shadeColor(color, percent) {
let f = parseInt(color.slice(1), 16),
t = percent < 0 ? 0 : 255,
p = percent < 0 ? percent * -1 : percent,
R = f >> 16,
G = (f >> 8) & 0x00FF,
B = f & 0x0000FF;
return (
'#' +
(
0x1000000 +
(Math.round((t - R) * p) + R) * 0x10000 +
(Math.round((t - G) * p) + G) * 0x100 +
(Math.round((t - B) * p) + B)
)
.toString(16)
.slice(1)
);
}
// ----------------------
// 3. preload() Function
// ----------------------
function preload() {
// Load backgrounds
startBg = loadImage('start.png', () => {
console.log("start.png loaded successfully.");
}, () => {
console.error("Failed to load start.png.");
});
nightBg = loadImage('night.png', () => {
console.log("night.png loaded successfully.");
}, () => {
console.error("Failed to load night.png.");
});
}
// ----------------------
// 4. setup() Function
// ----------------------
function setup() {
createCanvas(windowWidth, windowHeight);
textAlign(CENTER, CENTER);
// Creating Back Button first
backButton = createButton('Back');
backButton.position(width / 2 - backButton.width / 2, height - 100);
backButton.mousePressed(() => {
setPage("start"); // Navigate back to Start page
console.log("Navigated back to Start page.");
});
backButton.hide(); // Hide initially since it's not needed on Start page
// Create all other buttons
createButtons();
// Apply styles to all buttons including backButton
styleButtons();
// Set initial page to "start" to manage button visibility
setPage("start");
// Initialize feedbackColor inside setup()
feedbackColor = color(255);
// Start typewriter effect
typewriterIntervalID = setInterval(() => {
if (textIndex < typewriterText.length) {
currentText += typewriterText[textIndex];
textIndex++;
}
}, typewriterSpeed);
// If in debug mode, ensure typewriter is complete for easier testing
if (debugMode) {
currentText = typewriterText;
textIndex = typewriterText.length;
clearInterval(typewriterIntervalID);
}
// Initialize serial communication
initializeSerial();
}
// ----------------------
// 5. draw() Function
// ----------------------
function draw() {
// Choose background based on page
if (page === "start" && startBg && startBg.width > 0) {
imageMode(CORNER);
image(startBg, 0, 0, width, height);
} else if ((page === "gameStart" || page === "game" || page === "won" || page === "lost" || page === "secondGameIntro" || page === "basketGame") && nightBg && nightBg.width > 0) {
imageMode(CORNER);
image(nightBg, 0, 0, width, height);
} else {
background(30); // dark fallback background for a professional look
}
switch(page) {
case "start":
handleStartPage();
break;
case "gameStart":
handleGameStartPage();
break;
case "game":
handleGamePage();
break;
case "howToPlay":
handleHowToPlay();
break;
case "mythology":
drawMythologyPage();
break;
case "won":
handleWonPage();
break;
case "lost":
handleLostPage();
break;
case "secondGameIntro":
handleSecondGameIntro();
break;
case "basketGame":
handleBasketGame();
break;
default:
break;
}
// Display feedback box
if (feedbackMessage !== "") {
push();
// Define box dimensions and position
let boxWidth = width * 0.3;
let boxHeight = 50;
let boxX = width / 2 - boxWidth / 2;
let boxY = height - 80; // 80 pixels from bottom
// Draw off-white rectangle
fill(240); // off-white
noStroke();
rect(boxX, boxY, boxWidth, boxHeight, 10);
// Draw feedback text
fill(feedbackColor);
textSize(24);
text(feedbackMessage, width / 2, boxY + boxHeight / 2);
pop();
}
}
// ----------------------
// 6. createButtons() Function
// ----------------------
function createButtons() {
// Create Connect Button
connectButton = createButton('Connect to Serial');
connectButton.position(width / 2 - connectButton.width / 2, height / 2 - 220);
connectButton.mousePressed(() => {
connectToSerial(); // Initialize serial connection
connectButton.hide();
console.log("Serial connection initiated.");
});
// Create How to Play button
howToPlayButton = createButton('How to Play');
howToPlayButton.position(width / 2 - howToPlayButton.width / 2, height / 2 + 205);
howToPlayButton.mousePressed(() => {
setPage("howToPlay");
console.log("Navigated to How to Play page.");
});
// Create Mythology Button
mythologyButton = createButton('Read the Mythology');
mythologyButton.position(width / 2 - mythologyButton.width / 2, height / 2 + 255);
mythologyButton.mousePressed(() => {
setPage("mythology");
console.log("Navigated to Mythology page.");
});
// Create Fullscreen Button
fullscreenButton = createButton('Fullscreen');
fullscreenButton.size(200, 50);
fullscreenButton.mousePressed(toggleFullscreen); // Link to toggle function
// Position buttons initially
positionButtons();
// Show or hide buttons based on the current page
if (page === "start") {
connectButton.show();
howToPlayButton.show();
mythologyButton.show();
fullscreenButton.show(); // Show fullscreen button on start page
} else {
connectButton.hide();
howToPlayButton.hide();
mythologyButton.hide();
fullscreenButton.hide(); // Hide fullscreen button on other pages
}
console.log("Buttons created and positioned.");
}
// ----------------------
// 7. positionButtons() Function
// ----------------------
function positionButtons() {
// Center X position
let centerX = width / 2;
// Calculate Y positions with relative offsets
connectButton.position(centerX - connectButton.width / 2, height / 2 - 220);
howToPlayButton.position(centerX - howToPlayButton.width / 2, height / 2 + 205);
mythologyButton.position(centerX - mythologyButton.width / 2, height / 2 + 255);
fullscreenButton.position(centerX - fullscreenButton.width / 2, height / 2 + 275); // Positioned below other buttons
}
// ----------------------
// 8. Pages Handling Functions
// ----------------------
function handleStartPage() {
fill(255);
textSize(48);
stroke(0);
strokeWeight(2);
text("Sundarban and Bonbibi", width / 2, height / 13);
noStroke();
drawInstructionBox(currentText);
}
function handleGameStartPage() {
fill(255);
textSize(36);
stroke(0);
strokeWeight(2);
text("Get Ready!", width / 2, height / 2 - 50);
noStroke();
fill(255);
textSize(24);
text(backgroundText, width / 2, height / 2 + 50, width * 0.8);
}
function handleGamePage() {
// Display timer and score at the top
displayTimerAndScore();
// Display latest data at the bottom-left for user reference
displaySensorData();
// Display current word with a glowing effect
if (wordDisplay !== "") {
push();
textAlign(CENTER, CENTER);
textSize(64);
let glowColor = wordColor === "yellow" ? color(255, 255, 0) : color(255);
strokeWeight(4);
stroke(glowColor);
fill(glowColor);
text(wordDisplay, width / 2, height / 2);
pop();
}
}
function handleWonPage() {
fill(color(0, 200, 0));
noStroke();
textSize(48);
text("You Escaped Successfully! You Win!", width / 2, height / 2 - 50);
fill(255);
textSize(32);
text("Your Score: " + score, width / 2, height / 2);
textSize(24);
text("Press 'R' to Continue", width / 2, height / 2 + 50);
}
function handleLostPage() {
fill(color(200, 0, 0));
noStroke();
textSize(48);
text("Time's Up! You Lose!", width / 2, height / 2 - 50);
fill(255);
textSize(32);
text("Your Score: " + score, width / 2, height / 2);
textSize(24);
text("Press 'R' to Restart", width / 2, height / 2 + 50);
}
function handleSecondGameIntro() {
fill(255);
textSize(36);
stroke(0);
strokeWeight(2);
text("Take Flowers for the Bees\nWhile Going to Sundarban", width / 2, height / 4);
noStroke();
fill(255);
textSize(24);
text("Press the physical button to start the basket game.", width / 2, height / 2);
}
function handleBasketGame() {
// Display timer and score at the top
displayTimerAndScore();
// Display latest data at the bottom-left for user reference
displaySensorData();
// Draw water particles
for (let i = 0; i < vehicles.length; i++) {
vehicles[i].update();
vehicles[i].show();
}
// Draw flowers
for (let i = flowers.length - 1; i >= 0; i--) {
flowers[i].update();
flowers[i].show();
// Check collision with basket
if (dist(flowers[i].pos.x, flowers[i].pos.y, basketX, basketY) < 40) {
score++; // Increment score
flowers.splice(i, 1); // Remove collected flower
}
// Remove flowers out of bounds
if (flowers[i].pos.y > height) {
flowers.splice(i, 1);
}
}
// Generate flowers every 30 frames from random positions
if (frameCount % 30 === 0) {
let f = new Flower(random(50, width - 50), -50, random(30, 60), random(TWO_PI), floor(random(6)));
flowers.push(f);
}
// Draw basket
drawBasket();
// Check for game over condition
if (timer <= 0 && !gameOver) {
gameOver = true;
gameResult = "Time's Up! You Lose!";
setPage("lost");
console.log("Timer ended. Time's up!");
}
}
// ----------------------
// 9. Instruction Box Function
// ----------------------
function drawInstructionBox(textContent) {
textSize(18);
let boxWidth = width * 0.4;
let boxHeight = 60;
let boxX = width / 2 - boxWidth / 2;
let boxY = height / 1.5 - boxHeight / 12;
noStroke();
fill('rgb(165,88,8)');
rect(boxX, boxY, boxWidth, boxHeight, 10);
fill(255);
text(textContent, width / 2, boxY + boxHeight /2);
}
// ----------------------
// 10. Display Timer and Score Function
// ----------------------
function displayTimerAndScore() {
push();
textAlign(CENTER, CENTER);
textSize(24);
noStroke();
fill(0, 150);
rectMode(CENTER);
rect(width / 2, 50, 220, 60, 10);
fill(255);
text("Time: " + countdown + "s | Score: " + score, width / 2, 50);
pop();
}
// ----------------------
// 11. Display Sensor Data Function
// ----------------------
function displaySensorData() {
push();
textAlign(LEFT, CENTER);
textSize(16);
noStroke();
fill(0,150);
rectMode(CORNER);
rect(20, height - 60, 320, 40, 10);
fill(255);
text("Latest Data: " + latestData, 40, height - 40);
pop();
}
// ----------------------
// 12. Game Logic Functions
// ----------------------
function setupGameElements() {
currentWordIndex = 0;
wordDisplay = "";
wordColor = "white";
countdown = timer;
gameOver = false;
gameResult = "";
score = 0;
wordInterval = 4000;
console.log("Game elements reset.");
// Reset gameStartInitiated for next possible restart
gameStartInitiated = false;
}
function setNextWord() {
if (gameOver) return;
if (currentWordIndex >= words.length) {
// No more words => Win
gameOver = true;
gameResult = "You Escaped Successfully! You Win!";
setPage("won");
console.log("All words displayed. Player wins.");
return;
}
wordDisplay = words[currentWordIndex];
wordColor = random(['yellow', 'white']);
currentWordIndex++;
wordTouched = false; // Reset touch flag
if (currentWordIndex < 5) {
wordInterval = 4000;
} else {
wordInterval = 2000;
}
// Clear any existing timeout to prevent overlaps
if (wordTimeoutID) {
clearTimeout(wordTimeoutID);
}
// Store the timeout ID to manage it later
wordTimeoutID = setTimeout(setNextWord, wordInterval);
}
function handleSerialEvent(event) {
console.log("Received event:", event);
// If the event is "ButtonPressed" and on secondGameIntro, start basket game
if (event === "ButtonPressed" && page === "secondGameIntro") {
setPage("basketGame");
setupBasketGameElements();
startTimer();
console.log("Basket game started.");
return;
}
// If the event is "ButtonPressed" and on start page, start word game
if (event === "ButtonPressed" && page === "start") {
setPage("gameStart");
console.log("Game is starting...");
// Proceed to game page immediately to eliminate lag
setPage("game");
setupGameElements();
setNextWord();
startTimer();
console.log("Word game started without delay.");
gameStartInitiated = false; // Reset the flag for potential future games
return;
}
// Handle sensor data for word game
if (page === "game" && !gameOver && !wordTouched) {
let trimmedEvent = event.trim();
let values = trimmedEvent.split(',');
console.log("Parsed values:", values);
if (values.length >= 2) { // Expecting at least two values
let distanceYellow = parseFloat(values[0].trim());
let distanceWhite = parseFloat(values[1].trim());
// Optionally log the third value if present
if (values.length > 2) {
console.warn("Extra sensor data received and ignored:", values.slice(2));
}
if (!isNaN(distanceYellow) && !isNaN(distanceWhite)) {
console.log(`Sensor Readings - Yellow: ${distanceYellow}cm, White: ${distanceWhite}cm`);
// Define touch threshold
let touchThreshold = 10; // in centimeters
if (wordColor === "yellow") {
if (distanceYellow > 0 && distanceYellow < touchThreshold) {
// Correct touch
score++;
console.log("Yellow sensor touched correctly! Score:", score);
wordDisplay = "";
clearTimeout(wordTimeoutID); // Clear the existing setTimeout
setNextWord(); // Proceed to the next word
// Set feedback
feedbackMessage = "Correct!";
feedbackColor = color(0, 200, 0); // green
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
} else if (distanceWhite > 0 && distanceWhite < touchThreshold) {
// Incorrect sensor touched
console.log("White sensor touched incorrectly.");
feedbackMessage = "Incorrect!";
feedbackColor = color(200, 0, 0); // red
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
}
} else if (wordColor === "white") {
if (distanceWhite > 0 && distanceWhite < touchThreshold) {
// Correct touch
score++;
console.log("White sensor touched correctly! Score:", score);
wordDisplay = "";
clearTimeout(wordTimeoutID); // Clear the existing setTimeout
setNextWord(); // Proceed to the next word
// Set feedback
feedbackMessage = "Correct!";
feedbackColor = color(0, 200, 0); // green
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
} else if (distanceYellow > 0 && distanceYellow < touchThreshold) {
// Incorrect sensor touched
console.log("Yellow sensor touched incorrectly.");
feedbackMessage = "Incorrect!";
feedbackColor = color(200, 0, 0); // red
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
}
}
} else {
console.log("Invalid sensor data received.");
}
} else {
console.log("Unexpected sensor data format.");
}
}
// Handle sensor data for basket game
if (page === "basketGame" && !gameOver) {
let trimmedEvent = event.trim();
let value = parseFloat(trimmedEvent);
console.log("Potentiometer Reading:", value);
if (!isNaN(value)) {
// Map potentiometer value to basketX position
basketX = map(value, 0, 1023, 50, width - 50);
// Constrain basketX to stay within canvas
basketX = constrain(basketX, 50, width - 50);
} else {
console.log("Invalid potentiometer value:", event);
}
}
}
function startTimer() {
if (timerStart) return; // Prevent multiple timers
timerStart = true;
// Start countdown timer
timerInterval = setInterval(() => {
if (page !== "game" && page !== "basketGame") {
clearInterval(timerInterval);
return;
}
if (countdown > 0) {
countdown--;
console.log(`Timer: ${countdown}s left.`);
} else {
if (!gameOver) {
gameOver = true;
if (page === "game") {
gameResult = "Time's Up! You Lose!";
} else if (page === "basketGame") {
gameResult = "Time's Up! You Lose!";
}
setPage(page === "game" ? "lost" : "lost");
clearInterval(timerInterval);
console.log("Timer ended. Time's up!");
}
}
}, 1000);
}
// ----------------------
// 13. Restart Functionality
// ----------------------
function keyPressed() {
if (debugMode) {
// Simulate yellow sensor activation with 'Y' key
if (key === 'Y' || key === 'y') {
let simulatedData = "5,15"; // distanceYellow = 5cm (active), distanceWhite = 15cm (inactive)
console.log("Simulated Yellow Sensor Activation:", simulatedData);
readSerial(simulatedData);
}
// Simulate white sensor activation with 'W' key
if (key === 'W' || key === 'w') {
let simulatedData = "15,5"; // distanceYellow = 15cm (inactive), distanceWhite = 5cm (active)
console.log("Simulated White Sensor Activation:", simulatedData);
readSerial(simulatedData);
}
// Simulate button press with 'B' key for second game
if (key === 'B' || key === 'b') {
let simulatedData = "ButtonPressed";
console.log("Simulated Button Press:", simulatedData);
readSerial(simulatedData);
}
// Simulate potentiometer values with 'P' key for basket game
if (key === 'P' || key === 'p') {
let simulatedData = "600"; // Example potentiometer value
console.log("Simulated Potentiometer Activation:", simulatedData);
readSerial(simulatedData);
}
} else {
// Existing restart functionality
if (key === 'r' || key === 'R') {
setupGameElements();
setPage("start");
console.log("Game restarted.");
currentText = "";
textIndex = 0;
clearInterval(typewriterIntervalID); // Clear existing interval
typewriterIntervalID = setInterval(() => {
if (textIndex < typewriterText.length) {
currentText += typewriterText[textIndex];
textIndex++;
}
}, typewriterSpeed);
}
}
}
// ----------------------
// 14. Window Resizing Function
// ----------------------
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
connectButton.position(width / 2 - connectButton.width / 2, height / 2 - 220);
howToPlayButton.position(width / 2 - howToPlayButton.width / 2, height / 2 + 205);
mythologyButton.position(width / 2 - mythologyButton.width / 2, height / 2 + 255);
fullscreenButton.position(width / 2 - fullscreenButton.width / 2, height / 2 + 275); // Positioned below other buttons
backButton.position(width / 2 - backButton.width / 2, height - 100); // Reposition Back button if visible
}
// ----------------------
// 15. Helper Functions (Optional Enhancements)
// ----------------------
// Function to calculate responsive text size (optional enhancement)
function calcTextSize(baseSize) {
return min(windowWidth, windowHeight) / 800 * baseSize;
}
// Function to update text sizes when window is resized (optional enhancement)
function updateTextSizes() {
// Currently, text sizes are set in individual page handlers using calcTextSize()
// This function can be expanded if you have global text sizes
}
// ----------------------
// 16. Serial Communication Functions
// ----------------------
// Initialize serial communication
function initializeSerial() {
// Create a new instance of p5.SerialPort
serial = new p5.SerialPort();
// List all available ports
serial.list();
// Register event callbacks
serial.on('connected', serverConnected);
serial.on('list', gotList);
serial.on('data', serialEvent);
serial.on('error', gotError);
serial.on('open', gotOpen);
serial.on('close', gotClose);
}
// Callback when connected to the server
function serverConnected() {
console.log("Connected to Server");
}
// Callback when list of ports is received
function gotList(thelist) {
console.log("List of Serial Ports:");
for (let i = 0; i < thelist.length; i++) {
console.log(i + ": " + thelist[i]);
}
}
// Callback when the serial port is opened
function gotOpen() {
console.log("Serial Port is Open");
}
// Callback for incoming serial data
function serialEvent() {
let inString = serial.readLine(); // Read the incoming string
if (inString.length > 0) {
readSerial(inString); // Process the data
}
}
// Callback for errors
function gotError(theerror) {
console.log(theerror);
}
// Callback when the serial port is closed
function gotClose() {
console.log("Serial Port is Closed");
}
// Function to handle Connect button press
function connectToSerial() {
serial.open(); // Open the first available port
}
// ----------------------
// 17. Single readSerial Function
// ----------------------
function readSerial(data) {
if (data.trim().length > 0) {
latestData = data.trim();
handleSerialEvent(latestData);
}
}
// ----------------------
// 18. Basket Game Classes and Functions
// ----------------------
// Class for water particles
class Vehicle {
constructor(x, y) {
this.pos = createVector(x, y); // Position
this.vel = createVector(0, random(1, 3)); // Velocity (vertical flow)
this.acc = createVector(0, 0); // Acceleration
}
update() {
this.vel.add(this.acc); // Update velocity
this.pos.add(this.vel); // Update position
this.acc.mult(0); // Reset acceleration
// Wrap around vertically for continuous flow
if (this.pos.y > height) {
this.pos.y = 0;
this.pos.x = random(width); // Random horizontal position on reset
}
}
show() {
stroke(173, 216, 230, 150); // Light blue color
strokeWeight(2);
point(this.pos.x, this.pos.y); // Render as a point
}
}
// Class for flowers
class Flower {
constructor(x, y, size, rotation, type) {
this.pos = createVector(x, y); // Position
this.size = size; // Size of the flower
this.rotation = rotation; // Rotation angle
this.type = type; // Flower type (e.g., daisy, tulip, etc.)
this.lifespan = 255; // Lifespan for fading effect
}
update() {
this.pos.y += 3; // Move vertically downward
this.lifespan -= 2; // Decrease lifespan over time
}
show() {
push();
translate(this.pos.x, this.pos.y);
rotate(this.rotation);
drawFlower(0, 0, this.size, this.type); // Call drawFlower function
pop();
}
}
// Draw different types of flowers
function drawFlower(x, y, size, type) {
switch (type) {
case 0:
drawDaisy(x, y, size);
break;
case 1:
drawTulip(x, y, size);
break;
case 2:
drawRose(x, y, size);
break;
case 3:
drawSunflower(x, y, size);
break;
case 4:
drawLily(x, y, size);
break;
case 5:
drawMarigold(x, y, size);
break;
}
}
// Flower-drawing functions
function drawDaisy(x, y, size) {
let petalCount = 9;
let petalLength = size;
let petalWidth = size / 3;
stroke(0);
fill('#D9E4E6');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, petalLength);
}
pop();
fill('#F2F2F2');
noStroke();
ellipse(x, y, size / 2);
}
function drawTulip(x, y, size) {
let petalCount = 6;
let petalWidth = size / 2;
stroke(0);
fill('#AEB7FE');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, size);
}
pop();
fill('#EDEAE6');
noStroke();
ellipse(x, y, size / 3);
}
function drawRose(x, y, size) {
let petalCount = 10;
let petalWidth = size / 3;
stroke(0);
fill('#D87373');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, size / 1.5);
}
pop();
fill('#F5E6E8');
noStroke();
ellipse(x, y, size / 4);
}
function drawSunflower(x, y, size) {
let petalCount = 20;
let petalLength = size * 1.5;
let petalWidth = size / 2;
stroke(0);
fill('#FACA49');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, petalLength);
}
pop();
fill('#6E4B1B');
noStroke();
ellipse(x, y, size);
}
function drawLily(x, y, size) {
let petalCount = 6;
let petalWidth = size / 2;
stroke(0);
fill('#998D30');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, size);
}
pop();
fill('#FBE7E7');
noStroke();
ellipse(x, y, size / 4);
}
function drawMarigold(x, y, size) {
let petalCount = 12;
let petalLength = size;
let petalWidth = size / 2;
stroke(0);
fill('#F4A263');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, petalLength);
}
pop();
fill('#FFC107');
noStroke();
ellipse(x, y, size / 3);
}
// Draw basket based on basketX and basketY
function drawBasket() {
fill("#FFD700"); // Gold color for basket
rectMode(CENTER);
rect(basketX, basketY - 10, 60, 20, 5); // Basket rectangle
}
// ----------------------
// 19. Basket Game Setup and Logic
// ----------------------
// Basket Game Variables
let vehicles = []; // Water particles
let flowers = []; // Flower objects
let basketX = 200; // Initial Basket position (center)
let basketY; // Basket fixed at bottom
// Potentiometer Variables
let potValue = 200; // Default potentiometer value
function setupBasketGameElements() {
basketY = height - 50; // Position basket at the bottom
basketX = width / 2; // Center basket
score = 0;
countdown = timer;
gameOver = false;
// Initialize water particles
vehicles = [];
for (let x = 0; x <= width; x += 10) {
for (let y = -200; y < height; y += 10) {
let v = new Vehicle(x, y);
vehicles.push(v);
}
}
// Initialize flowers
flowers = [];
}
// ----------------------
// 20. Basket Game Handling Function
// ----------------------
function handleBasketGame() {
// Display timer and score at the top
displayTimerAndScore();
// Display latest data at the bottom-left for user reference
displaySensorData();
// Draw water particles
for (let i = 0; i < vehicles.length; i++) {
vehicles[i].update();
vehicles[i].show();
}
// Draw flowers
for (let i = flowers.length - 1; i >= 0; i--) {
flowers[i].update();
flowers[i].show();
// Check collision with basket
if (dist(flowers[i].pos.x, flowers[i].pos.y, basketX, basketY) < 40) {
score++; // Increment score
flowers.splice(i, 1); // Remove collected flower
}
// Remove flowers out of bounds
if (flowers[i].pos.y > height) {
flowers.splice(i, 1);
}
}
// Generate flowers every 30 frames from random positions
if (frameCount % 30 === 0) {
let f = new Flower(random(50, width - 50), -50, random(30, 60), random(TWO_PI), floor(random(6)));
flowers.push(f);
}
// Draw basket
drawBasket();
// Check for game over condition
if (timer <= 0 && !gameOver) {
gameOver = true;
gameResult = "Time's Up! You Lose!";
setPage("lost");
console.log("Timer ended. Time's up!");
}
}
// ----------------------
// 21. Restart and Continue Functionality
// ----------------------
function keyPressed() {
if (debugMode) {
// Simulate yellow sensor activation with 'Y' key
if (key === 'Y' || key === 'y') {
let simulatedData = "5,15"; // distanceYellow = 5cm (active), distanceWhite = 15cm (inactive)
console.log("Simulated Yellow Sensor Activation:", simulatedData);
readSerial(simulatedData);
}
// Simulate white sensor activation with 'W' key
if (key === 'W' || key === 'w') {
let simulatedData = "15,5"; // distanceYellow = 15cm (inactive), distanceWhite = 5cm (active)
console.log("Simulated White Sensor Activation:", simulatedData);
readSerial(simulatedData);
}
// Simulate button press with 'B' key for second game
if (key === 'B' || key === 'b') {
let simulatedData = "ButtonPressed";
console.log("Simulated Button Press:", simulatedData);
readSerial(simulatedData);
}
// Simulate potentiometer values with 'P' key for basket game
if (key === 'P' || key === 'p') {
let simulatedData = "600"; // Example potentiometer value
console.log("Simulated Potentiometer Activation:", simulatedData);
readSerial(simulatedData);
}
} else {
// Existing restart functionality
if (key === 'r' || key === 'R') {
if (page === "won") {
setPage("secondGameIntro");
console.log("Navigated to Second Game Intro page.");
} else {
setupGameElements();
setPage("start");
console.log("Game restarted.");
currentText = "";
textIndex = 0;
clearInterval(typewriterIntervalID); // Clear existing interval
typewriterIntervalID = setInterval(() => {
if (textIndex < typewriterText.length) {
currentText += typewriterText[textIndex];
textIndex++;
}
}, typewriterSpeed);
}
}
}
}
// ----------------------
// 22. Serial-Controlled Basket Movement
// ----------------------
// Handle serial events and update game based on data
function handleSerialEvent(event) {
console.log("Handling Serial Event:", event);
// If the event is "ButtonPressed" and on secondGameIntro, start basket game
if (event === "ButtonPressed" && page === "secondGameIntro") {
setPage("basketGame");
setupBasketGameElements();
startTimer();
console.log("Basket game started.");
return;
}
// If the event is "ButtonPressed" and on start page, start word game
if (event === "ButtonPressed" && page === "start") {
setPage("gameStart");
console.log("Word game is starting...");
// Proceed to game page immediately to eliminate lag
setPage("game");
setupGameElements();
setNextWord();
startTimer();
console.log("Word game started without delay.");
gameStartInitiated = false; // Reset the flag for potential future games
return;
}
// Handle sensor data for word game
if (page === "game" && !gameOver && !wordTouched) {
let trimmedEvent = event.trim();
let values = trimmedEvent.split(',');
console.log("Parsed values:", values);
if (values.length >= 2) { // Expecting at least two values
let distanceYellow = parseFloat(values[0].trim());
let distanceWhite = parseFloat(values[1].trim());
// Optionally log the third value if present
if (values.length > 2) {
console.warn("Extra sensor data received and ignored:", values.slice(2));
}
if (!isNaN(distanceYellow) && !isNaN(distanceWhite)) {
console.log(`Sensor Readings - Yellow: ${distanceYellow}cm, White: ${distanceWhite}cm`);
// Define touch threshold
let touchThreshold = 10; // in centimeters
if (wordColor === "yellow") {
if (distanceYellow > 0 && distanceYellow < touchThreshold) {
// Correct touch
score++;
console.log("Yellow sensor touched correctly! Score:", score);
wordDisplay = "";
clearTimeout(wordTimeoutID); // Clear the existing setTimeout
setNextWord(); // Proceed to the next word
// Set feedback
feedbackMessage = "Correct!";
feedbackColor = color(0, 200, 0); // green
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
} else if (distanceWhite > 0 && distanceWhite < touchThreshold) {
// Incorrect sensor touched
console.log("White sensor touched incorrectly.");
feedbackMessage = "Incorrect!";
feedbackColor = color(200, 0, 0); // red
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
}
} else if (wordColor === "white") {
if (distanceWhite > 0 && distanceWhite < touchThreshold) {
// Correct touch
score++;
console.log("White sensor touched correctly! Score:", score);
wordDisplay = "";
clearTimeout(wordTimeoutID); // Clear the existing setTimeout
setNextWord(); // Proceed to the next word
// Set feedback
feedbackMessage = "Correct!";
feedbackColor = color(0, 200, 0); // green
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
} else if (distanceYellow > 0 && distanceYellow < touchThreshold) {
// Incorrect sensor touched
console.log("Yellow sensor touched incorrectly.");
feedbackMessage = "Incorrect!";
feedbackColor = color(200, 0, 0); // red
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
}
}
} else {
console.log("Invalid sensor data received.");
}
} else {
console.log("Unexpected sensor data format.");
}
}
// Handle sensor data for basket game
if (page === "basketGame" && !gameOver) {
let trimmedEvent = event.trim();
let potValue = parseFloat(trimmedEvent);
console.log("Potentiometer Reading:", potValue);
if (!isNaN(potValue)) {
// Map potentiometer value to basketX position
basketX = map(potValue, 0, 1023, 50, width - 50);
// Constrain basketX to stay within canvas
basketX = constrain(basketX, 50, width - 50);
} else {
console.log("Invalid potentiometer value:", event);
}
}
}
// ----------------------
// 17. Single readSerial Function
// ----------------------
function readSerial(data) {
if (data.trim().length > 0) {
latestData = data.trim();
handleSerialEvent(latestData);
}
}
// ----------------------
// 18. Basket Game Classes and Functions
// ----------------------
// Class for flowers
// Draw different types of flowers
function drawFlower(x, y, size, type) {
switch (type) {
case 0:
drawDaisy(x, y, size);
break;
case 1:
drawTulip(x, y, size);
break;
case 2:
drawRose(x, y, size);
break;
case 3:
drawSunflower(x, y, size);
break;
case 4:
drawLily(x, y, size);
break;
case 5:
drawMarigold(x, y, size);
break;
}
}
// Flower-drawing functions
function drawDaisy(x, y, size) {
let petalCount = 9;
let petalLength = size;
let petalWidth = size / 3;
stroke(0);
fill('#D9E4E6');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, petalLength);
}
pop();
fill('#F2F2F2');
noStroke();
ellipse(x, y, size / 2);
}
function drawTulip(x, y, size) {
let petalCount = 6;
let petalWidth = size / 2;
stroke(0);
fill('#AEB7FE');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, size);
}
pop();
fill('#EDEAE6');
noStroke();
ellipse(x, y, size / 3);
}
function drawRose(x, y, size) {
let petalCount = 10;
let petalWidth = size / 3;
stroke(0);
fill('#D87373');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, size / 1.5);
}
pop();
fill('#F5E6E8');
noStroke();
ellipse(x, y, size / 4);
}
function drawSunflower(x, y, size) {
let petalCount = 20;
let petalLength = size * 1.5;
let petalWidth = size / 2;
stroke(0);
fill('#FACA49');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, petalLength);
}
pop();
fill('#6E4B1B');
noStroke();
ellipse(x, y, size);
}
function drawLily(x, y, size) {
let petalCount = 6;
let petalWidth = size / 2;
stroke(0);
fill('#998D30');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, size);
}
pop();
fill('#FBE7E7');
noStroke();
ellipse(x, y, size / 4);
}
function drawMarigold(x, y, size) {
let petalCount = 12;
let petalLength = size;
let petalWidth = size / 2;
stroke(0);
fill('#F4A263');
push();
translate(x, y);
for (let i = 0; i < petalCount; i++) {
rotate(TWO_PI / petalCount);
ellipse(0, -size / 2, petalWidth, petalLength);
}
pop();
fill('#FFC107');
noStroke();
ellipse(x, y, size / 3);
}
// ----------------------
// 19. Basket Game Setup and Logic
// ----------------------
function setupBasketGameElements() {
basketY = height - 50; // Position basket at the bottom
basketX = width / 2; // Center basket
score = 0;
countdown = timer;
gameOver = false;
// Initialize water particles
vehicles = [];
for (let x = 0; x <= width; x += 10) {
for (let y = -200; y < height; y += 10) {
let v = new Vehicle(x, y);
vehicles.push(v);
}
}
// Initialize flowers
flowers = [];
}
// ----------------------
// 20. Basket Game Handling Function
// ----------------------
function handleBasketGame() {
// Display timer and score at the top
displayTimerAndScore();
// Display latest data at the bottom-left for user reference
displaySensorData();
// Draw water particles
for (let i = 0; i < vehicles.length; i++) {
vehicles[i].update();
vehicles[i].show();
}
// Draw flowers
for (let i = flowers.length - 1; i >= 0; i--) {
flowers[i].update();
flowers[i].show();
// Check collision with basket
if (dist(flowers[i].pos.x, flowers[i].pos.y, basketX, basketY) < 40) {
score++; // Increment score
flowers.splice(i, 1); // Remove collected flower
}
// Remove flowers out of bounds
if (flowers[i].pos.y > height) {
flowers.splice(i, 1);
}
}
// Generate flowers every 30 frames from random positions
if (frameCount % 30 === 0) {
let f = new Flower(random(50, width - 50), -50, random(30, 60), random(TWO_PI), floor(random(6)));
flowers.push(f);
}
// Draw basket
drawBasket();
// Check for game over condition
if (timer <= 0 && !gameOver) {
gameOver = true;
gameResult = "Time's Up! You Lose!";
setPage("lost");
console.log("Timer ended. Time's up!");
}
}
// ----------------------
// 21. Restart and Continue Functionality
// ----------------------
function keyPressed() {
if (debugMode) {
// Simulate yellow sensor activation with 'Y' key
if (key === 'Y' || key === 'y') {
let simulatedData = "5,15"; // distanceYellow = 5cm (active), distanceWhite = 15cm (inactive)
console.log("Simulated Yellow Sensor Activation:", simulatedData);
readSerial(simulatedData);
}
// Simulate white sensor activation with 'W' key
if (key === 'W' || key === 'w') {
let simulatedData = "15,5"; // distanceYellow = 15cm (inactive), distanceWhite = 5cm (active)
console.log("Simulated White Sensor Activation:", simulatedData);
readSerial(simulatedData);
}
// Simulate button press with 'B' key for second game
if (key === 'B' || key === 'b') {
let simulatedData = "ButtonPressed";
console.log("Simulated Button Press:", simulatedData);
readSerial(simulatedData);
}
// Simulate potentiometer values with 'P' key for basket game
if (key === 'P' || key === 'p') {
let simulatedData = "600"; // Example potentiometer value
console.log("Simulated Potentiometer Activation:", simulatedData);
readSerial(simulatedData);
}
} else {
// Existing restart functionality
if (key === 'r' || key === 'R') {
if (page === "won") {
setPage("secondGameIntro");
console.log("Navigated to Second Game Intro page.");
} else {
setupGameElements();
setPage("start");
console.log("Game restarted.");
currentText = "";
textIndex = 0;
clearInterval(typewriterIntervalID); // Clear existing interval
typewriterIntervalID = setInterval(() => {
if (textIndex < typewriterText.length) {
currentText += typewriterText[textIndex];
textIndex++;
}
}, typewriterSpeed);
}
}
}
}
// ----------------------
// 22. Serial-Controlled Basket Movement
// ----------------------
// Handle serial events and update game based on data
function handleSerialEvent(event) {
console.log("Handling Serial Event:", event);
// If the event is "ButtonPressed" and on secondGameIntro, start basket game
if (event === "ButtonPressed" && page === "secondGameIntro") {
setPage("basketGame");
setupBasketGameElements();
startTimer();
console.log("Basket game started.");
return;
}
// If the event is "ButtonPressed" and on start page, start word game
if (event === "ButtonPressed" && page === "start") {
setPage("gameStart");
console.log("Word game is starting...");
// Proceed to game page immediately to eliminate lag
setPage("game");
setupGameElements();
setNextWord();
startTimer();
console.log("Word game started without delay.");
gameStartInitiated = false; // Reset the flag for potential future games
return;
}
// Handle sensor data for word game
if (page === "game" && !gameOver && !wordTouched) {
let trimmedEvent = event.trim();
let values = trimmedEvent.split(',');
console.log("Parsed values:", values);
if (values.length >= 2) { // Expecting at least two values
let distanceYellow = parseFloat(values[0].trim());
let distanceWhite = parseFloat(values[1].trim());
// Optionally log the third value if present
if (values.length > 2) {
console.warn("Extra sensor data received and ignored:", values.slice(2));
}
if (!isNaN(distanceYellow) && !isNaN(distanceWhite)) {
console.log(`Sensor Readings - Yellow: ${distanceYellow}cm, White: ${distanceWhite}cm`);
// Define touch threshold
let touchThreshold = 10; // in centimeters
if (wordColor === "yellow") {
if (distanceYellow > 0 && distanceYellow < touchThreshold) {
// Correct touch
score++;
console.log("Yellow sensor touched correctly! Score:", score);
wordDisplay = "";
clearTimeout(wordTimeoutID); // Clear the existing setTimeout
setNextWord(); // Proceed to the next word
// Set feedback
feedbackMessage = "Correct!";
feedbackColor = color(0, 200, 0); // green
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
} else if (distanceWhite > 0 && distanceWhite < touchThreshold) {
// Incorrect sensor touched
console.log("White sensor touched incorrectly.");
feedbackMessage = "Incorrect!";
feedbackColor = color(200, 0, 0); // red
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
}
} else if (wordColor === "white") {
if (distanceWhite > 0 && distanceWhite < touchThreshold) {
// Correct touch
score++;
console.log("White sensor touched correctly! Score:", score);
wordDisplay = "";
clearTimeout(wordTimeoutID); // Clear the existing setTimeout
setNextWord(); // Proceed to the next word
// Set feedback
feedbackMessage = "Correct!";
feedbackColor = color(0, 200, 0); // green
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
} else if (distanceYellow > 0 && distanceYellow < touchThreshold) {
// Incorrect sensor touched
console.log("Yellow sensor touched incorrectly.");
feedbackMessage = "Incorrect!";
feedbackColor = color(200, 0, 0); // red
wordTouched = true;
// Clear previous feedback timeout
if (feedbackTimeout) {
clearTimeout(feedbackTimeout);
}
// Clear feedback after 2 seconds
feedbackTimeout = setTimeout(() => {
feedbackMessage = "";
}, 2000);
}
}
} else {
console.log("Invalid sensor data received.");
}
} else {
console.log("Unexpected sensor data format.");
}
}
// Handle sensor data for basket game
if (page === "basketGame" && !gameOver) {
let trimmedEvent = event.trim();
let potValue = parseFloat(trimmedEvent);
console.log("Potentiometer Reading:", potValue);
if (!isNaN(potValue)) {
// Map potentiometer value to basketX position
basketX = map(potValue, 0, 1023, 50, width - 50);
// Constrain basketX to stay within canvas
basketX = constrain(basketX, 50, width - 50);
} else {
console.log("Invalid potentiometer value:", event);
}
}
}
// ----------------------
// 17. Single readSerial Function
// ----------------------
function readSerial(data) {
if (data.trim().length > 0) {
latestData = data.trim();
handleSerialEvent(latestData);
}
}
// ----------------------
// 18. Serial Communication Functions
// ----------------------
// Initialize serial communication
function initializeSerial() {
// Create a new instance of p5.SerialPort
serial = new p5.SerialPort();
// List all available ports
serial.list();
// Register event callbacks
serial.on('connected', serverConnected);
serial.on('list', gotList);
serial.on('data', serialEvent);
serial.on('error', gotError);
serial.on('open', gotOpen);
serial.on('close', gotClose);
}
// Callback when connected to the server
function serverConnected() {
console.log("Connected to Server");
}
// Callback when list of ports is received
function gotList(thelist) {
console.log("List of Serial Ports:");
for (let i = 0; i < thelist.length; i++) {
console.log(i + ": " + thelist[i]);
}
}
// Callback when the serial port is opened
function gotOpen() {
console.log("Serial Port is Open");
}
// Callback for incoming serial data
function serialEvent() {
let inString = serial.readLine(); // Read the incoming string
if (inString.length > 0) {
readSerial(inString); // Process the data
}
}
// Callback for errors
function gotError(theerror) {
console.log(theerror);
}
// Callback when the serial port is closed
function gotClose() {
console.log("Serial Port is Closed");
}
// Function to handle Connect button press
function connectToSerial() {
serial.open(); // Open the first available port
}
// ----------------------
// 19. Restart and Continue Functionality
// ----------------------
// Restart game or continue to second game
function keyPressed() {
if (debugMode) {
// Simulate yellow sensor activation with 'Y' key
if (key === 'Y' || key === 'y') {
let simulatedData = "5,15"; // distanceYellow = 5cm (active), distanceWhite = 15cm (inactive)
console.log("Simulated Yellow Sensor Activation:", simulatedData);
readSerial(simulatedData);
}
// Simulate white sensor activation with 'W' key
if (key === 'W' || key === 'w') {
let simulatedData = "15,5"; // distanceYellow = 15cm (inactive), distanceWhite = 5cm (active)
console.log("Simulated White Sensor Activation:", simulatedData);
readSerial(simulatedData);
}
// Simulate button press with 'B' key for second game
if (key === 'B' || key === 'b') {
let simulatedData = "ButtonPressed";
console.log("Simulated Button Press:", simulatedData);
readSerial(simulatedData);
}
// Simulate potentiometer values with 'P' key for basket game
if (key === 'P' || key === 'p') {
let simulatedData = "600"; // Example potentiometer value
console.log("Simulated Potentiometer Activation:", simulatedData);
readSerial(simulatedData);
}
} else {
// Existing restart functionality
if (key === 'r' || key === 'R') {
if (page === "won") {
setPage("secondGameIntro");
console.log("Navigated to Second Game Intro page.");
} else {
setupGameElements();
setPage("start");
console.log("Game restarted.");
currentText = "";
textIndex = 0;
clearInterval(typewriterIntervalID); // Clear existing interval
typewriterIntervalID = setInterval(() => {
if (textIndex < typewriterText.length) {
currentText += typewriterText[textIndex];
textIndex++;
}
}, typewriterSpeed);
}
}
}
}
// ----------------------
// 14. Window Resizing Function
// ----------------------
function windowResized() {
resizeCanvas(windowWidth, windowHeight);
connectButton.position(width / 2 - connectButton.width / 2, height / 2 - 220);
howToPlayButton.position(width / 2 - howToPlayButton.width / 2, height / 2 + 205);
mythologyButton.position(width / 2 - mythologyButton.width / 2, height / 2 + 255);
fullscreenButton.position(width / 2 - fullscreenButton.width / 2, height / 2 + 275); // Positioned below other buttons
backButton.position(width / 2 - backButton.width / 2, height - 100); // Reposition Back button if visible
}
// ----------------------
// 15. Helper Functions (Optional Enhancements)
// ----------------------
// Function to calculate responsive text size (optional enhancement)
function calcTextSize(baseSize) {
return min(windowWidth, windowHeight) / 800 * baseSize;
}
// Function to update text sizes when window is resized (optional enhancement)
function updateTextSizes() {
// Currently, text sizes are set in individual page handlers using calcTextSize()
// This function can be expanded if you have global text sizes
}