xxxxxxxxxx
464
let buttonClickCount = 0;
let SensorDegrees = 255;
let smoothedDegrees = 255; // Smoothened sensor reading
let activateRobot = 0;
let gearImage; // Variable to hold the gear image
let smoothingFactor = 0.1; // Determines the smoothing effect
let turret; // Variable to hold the turret object
let boundaries = []; // Array to hold boundary objects
let rustGremlins = []; // Array to hold RustGremlins instances
let gremlinImages = [];
let bulletImage;
let introplayed = false;
function preload() {
// Introduction audio files
introAudio1 = loadSound('https://intro-to-im.vercel.app/final/intro1.mp3');
introAudio2 = loadSound('https://intro-to-im.vercel.app/final/intro2.mp3');
// Speaker images
speakerImage1 = loadImage('https://intro-to-im.vercel.app/final/pi.png');
gearImage = loadImage('https://intro-to-im.vercel.app/final/gear.png');
backgroundImage = loadImage('https://intro-to-im.vercel.app/final/bgseam.jpg');
turretImage = loadImage('https://intro-to-im.vercel.app/final/turret.png');
gremlinImages.push(loadImage('https://intro-to-im.vercel.app/final/gremelin1.png'));
gremlinImages.push(loadImage('https://intro-to-im.vercel.app/final/gremelin2.png'));
gremlinImages.push(loadImage('https://intro-to-im.vercel.app/final/gremelin3.png'));
bulletImage = loadImage('https://intro-to-im.vercel.app/final/bullet.png');
//Audio
// Audio files
shootSound = loadSound('https://intro-to-im.vercel.app/final/shoot.mp3');
gremlinDeathSound = loadSound('https://intro-to-im.vercel.app/final/die.mp3');
hitBoundarySound = loadSound('https://intro-to-im.vercel.app/final/bounce.mp3');
gameOverSound = loadSound('https://intro-to-im.vercel.app/final/gameover.mp3');
backgroundMusic = loadSound('https://intro-to-im.vercel.app/final/background.mp3');
}
function setup() {
createCanvas(windowWidth, windowHeight);
textSize(18);
imageMode(CENTER);
turret = new Turret(width / 2, height / 2, 20, 10); // Initialize the turret
spawnBoundaries(5); // Spawn 5 boundaries, this number can be adjusted
spawnGremlins(10); // Spawn 10 RustGremlins initially, adjustable
// Start playing background music continuously with volume control
backgroundMusic.setVolume(0.5); // Set the volume to 50%
backgroundMusic.loop();
}
function spawnGremlins(number) {
let centerAvoidanceSize = min(width, height) / 4; // Define an area in the center to avoid
let centerX = width / 2;
let centerY = height / 2;
for (let i = 0; i < number; i++) {
let candidateX, candidateY, fits;
do {
candidateX = random(0, width);
candidateY = random(0, height);
fits = !boundaries.some(boundary => candidateX >= boundary.x && candidateX <= boundary.x + boundary.width &&
candidateY >= boundary.y && candidateY <= boundary.y + boundary.height);
// Ensure Gremlins also avoid the central area
if (fits) {
fits = !(candidateX > centerX - centerAvoidanceSize / 2 && candidateX < centerX + centerAvoidanceSize / 2 &&
candidateY > centerY - centerAvoidanceSize / 2 && candidateY < centerY + centerAvoidanceSize / 2);
}
} while (!fits);
rustGremlins.push(new RustGremlin(candidateX, candidateY, 20)); // Radius can be adjusted
}
}
function spawnBoundaries(number) {
for (let i = 0; i < number; i++) {
let bw = random(width / 10, width / 3); // Boundary width
let bh = random(height / 10, height / 3); // Boundary height
let bx = random(0, width - bw); // Boundary x position
let by = random(0, height - bh); // Boundary y position
// Avoid placing boundaries in the center of the canvas
let centerX = width / 2, centerY = height / 2;
let centerAvoidanceSize = min(width, height) / 4; // Define an area in the center to avoid
// Check if the boundary overlaps with the center region, retry if it does
while (bx < centerX + centerAvoidanceSize / 2 && bx + bw > centerX - centerAvoidanceSize / 2 &&
by < centerY + centerAvoidanceSize / 2 && by + bh > centerY - centerAvoidanceSize / 2) {
bx = random(0, width - bw);
by = random(0, height - bh);
}
boundaries.push(new Boundary(bx, by, bw, bh, 150)); // Opacity set to 150
}
}
function draw() {
background(255);
fill(0);
// Tile the background image
for (let x = 0; x < width*2; x += backgroundImage.width) {
for (let y = 0; y < height*2; y += backgroundImage.height) {
image(backgroundImage, x, y);
}
}
displaySerialStatus();
if (!serialActive) {
return; // Do nothing if the serial is not active yet
}
else{
if(introplayed == false){
background(0);
playIntroSequence();
}
}
/*
if (mouseIsPressed) {
activateRobot = 1;
} else {
activateRobot = 0;
}*/
smoothedDegrees += (SensorDegrees - smoothedDegrees) * smoothingFactor;
boundaries.forEach(boundary => {
boundary.display();
});
rustGremlins.forEach(gremlin => gremlin.display()); // Display each gremlin
turret.update(SensorDegrees);
turret.shoot();
turret.display();
displayGremlinCount();
checkGameOver();
}
function displayGremlinCount() {
push();
textAlign(RIGHT, TOP);
// Setting text characteristics
textSize(24); // Set text size to be larger
fill(255); // Set fill color to white
stroke(0); // Set stroke color to black
strokeWeight(4); // Set stroke weight to make the black border noticeable
textStyle(BOLD); // Make the font bold
// Displaying the text
text('Rust Gremlins Left: ' + rustGremlins.length, width - 20, 10);
pop(); // Restore original drawing state
}
function checkGameOver() {
if (rustGremlins.length === 0) {
textSize(32);
textAlign(CENTER, CENTER);
fill(255);
stroke(0);
strokeWeight(4);
textStyle(BOLD);
text("The castle is repaired", width / 2, height / 2);
gameOverSound.play(); // Play game over sound
noLoop(); // Stop the draw loop
activateRobot = 1; // Set activateRobot to 1 to trigger any final robot activation
setTimeout(function() {
activateRobot = 0; // Reset activateRobot after 4 seconds
stopAllActivities();
}, 10000); // 4000 milliseconds = 4 seconds
}
}
function stopAllActivities() {
// Stop any ongoing activities, sounds, or reset intervals
gameOverSound.stop(); // Ensure sound is stopped
backgroundMusic.stop(); // Stop the background music if it was playing
// Clear any other timeouts or intervals you may have set up
// For example:
// clearTimeout(timeoutVariable);
// clearInterval(intervalVariable);
// Clear the canvas
background(0); // Set the background to black
// Display "Thank you for playing" message
fill(255); // Set text color to white
textSize(32); // Set text size
textAlign(CENTER, CENTER); // Align text to be centered
text("Thank you for playing.\n Reload the page to play.", width / 2, height / 2); // Position the text in the center
// Optionally, hide the canvas or other HTML elements if not needed anymore
// canvas.style.display = 'none'; // Hide the canvas if you don't want to show anything anymore
}
function displaySerialStatus() {
if (!serialActive) {
push(); // Start a new drawing state
// Setting text characteristics
textSize(24); // Set text size to be larger
fill(255); // Set fill color to white
stroke(0); // Set stroke color to black
strokeWeight(4); // Set stroke weight to make the black border noticeable
textStyle(BOLD); // Make the font bold
// Displaying the text
text("Press Space Bar, \nthen choose IOUSBHostDevice...usbmodem... to start.", 20, 30);
pop(); // Restore original drawing state
} else {
push();
textAlign(RIGHT, TOP);
stroke(0);
textSize(12);
//text("Connected", 20, 30);
//text('buttonClickCount = ' + str(buttonClickCount), 20, 50);
//text('SensorDegrees = ' + str(SensorDegrees), 20, 70);
pop();
}
}
function keyPressed() {
if (key == " ") {
setUpSerial();
turret.lastButtonCount = 0;
}
}
function playIntroSequence() {
background(0);
noLoop();
background(0); // Start with a black screen
background(0);
// Play the first audio with subtitles and image for the first part of the intro
introAudio1.play();
displaySubtitleAndImage("Pi:\nThese troublesome Rust Gremlins have frozen my castle's gears!\nHelp me drive them out with our trusty Blaze O' Gears!!", speakerImage1);
introAudio1.onended(() => {
background(0); // Ensure the screen is reset to black
// Once the first audio ends, play the second with its own subtitle and image
introAudio2.play();
displaySubtitleAndImage("Pi:\nGo to the castle, adjust the valve to take aim, and use\nthe switch to unleash its fiery wrath.", speakerImage1);
introAudio2.onended(() => {
introplayed = true;
loop(); // Resume the draw loop to continue with the game
});
});
}
function displaySubtitleAndImage(subtitle, speakerImage) {
background(0); // Ensure the screen is black throughout the cinematic
// Display the subtitle
fill(255); // Set text color to white for contrast
textSize(24); // Larger text for better visibility
textAlign(CENTER, CENTER);
text(subtitle, 2*width / 3, height * 0.65);
// Display the speaker image
imageMode(CENTER);
image(speakerImage, width / 4, height / 2, speakerImage.width / 2, speakerImage.height / 2);
}
function readSerial(data) {
if (data != null) {
let fromArduino = split(trim(data), ",");
if (fromArduino.length == 2) {
buttonClickCount = int(fromArduino[0]);
SensorDegrees = int(parseFloat(fromArduino[1]) * (360 / 5));
}
let sendToArduino = activateRobot;
writeSerial(sendToArduino);
}
}
class Turret {
constructor(x, y, bulletSpeed, bulletRadius) {
this.x = x;
this.y = y;
this.bulletSpeed = bulletSpeed;
this.bulletRadius = bulletRadius;
this.bullets = [];
this.lastButtonCount = buttonClickCount;
}
update(degrees) {
this.direction = degrees;
}
display() {
push();
translate(this.x, this.y);
rotate(radians(this.direction));
image(turretImage, 0, 0, turretImage.width/2, turretImage.height/2); // Adjust size if necessary
pop();
}
shoot() {
// Check if lastButtonCount is more than 2 counts ahead of buttonClickCount and adjust
if (this.lastButtonCount > buttonClickCount + 1) {
// Adjust lastButtonCount to match the current buttonClickCount + 2 to avoid excess
this.lastButtonCount = buttonClickCount ;
}
//console.log('Current clicks:', , 'Last recorded:', this.lastButtonCount);
//text(str(buttonClickCount)+"-"+str(this.lastButtonCount), 20, 90);
if (buttonClickCount > this.lastButtonCount) {
this.bullets.push(new Bullet(this.x, this.y, this.direction, this.bulletSpeed, this.bulletRadius));
this.lastButtonCount = buttonClickCount; // Update lastButtonCount after shooting
shootSound.play(); // Play shooting sound
}
this.bullets.forEach(bullet => {
bullet.move();
bullet.display();
bullet.checkBoundaries();
if (bullet.lifetime <= 0) {
this.bullets.splice(this.bullets.indexOf(bullet), 1);
}
});
}
}
class RustGremlin {
constructor(x, y, radius) {
this.x = x;
this.y = y;
this.radius = radius;
this.image = gremlinImages[int(random(gremlinImages.length))]; // Randomly select an image
this.angle = 0; // Initial rotation angle
this.rotationSpeed = random(0.02, 0.05); // Randomize rotation speed for variety
this.rotationDirection = random([-1, 1]); // Randomize rotation direction
}
display() {
// Calculate rotation for this frame
this.angle += this.rotationSpeed * this.rotationDirection;
// Oscillate the rotation angle between -PI/18 and PI/18 (10 degrees)
let oscillation = sin(frameCount * this.rotationSpeed) * PI / 18;
push();
translate(this.x, this.y);
rotate(oscillation); // Apply the oscillating rotation
image(this.image, -this.radius, -this.radius, this.radius * 5, this.radius * 5);
pop();
}
hitBy(bullet) {
let d = dist(this.x, this.y, bullet.x, bullet.y);
return d < this.radius + bullet.radius;
}
}
class Bullet {
constructor(x, y, direction, speed, radius) {
this.x = x;
this.y = y;
this.direction = direction;
this.speed = speed;
this.radius = radius;
this.lifetime = 200; // Frames before the bullet disappears
}
move() {
this.x += this.speed * cos(radians(this.direction));
this.y += this.speed * sin(radians(this.direction));
this.lifetime--;
this.checkGremlinCollisions();
}
checkGremlinCollisions () {
for (let i = rustGremlins.length - 1; i >= 0; i--) {
if (rustGremlins[i].hitBy(this)) {
gremlinDeathSound.play(); // Play gremlin death sound
rustGremlins.splice(i, 1); // Remove the gremlin if hit
}
}
}
display() {
push();
translate(this.x, this.y);
rotate(radians(this.direction));
image(bulletImage, 0, 0, this.radius * 5, this.radius * 5); // Assuming the image is centered
pop();
}
checkBoundaries() {
boundaries.forEach(boundary => {
// Check for collision before entering the boundary
let futureX = this.x + this.speed * cos(radians(this.direction));
let futureY = this.y + this.speed * sin(radians(this.direction));
if (futureX - this.radius < boundary.x && futureX + this.radius > boundary.x &&
futureY > boundary.y && futureY < boundary.y + boundary.height) {
// Reflect horizontally if hitting the left boundary
hitBoundarySound.play(); // Play boundary hit sound
this.direction = 180 - this.direction;
this.x = boundary.x - this.radius; // Reset position to the edge
} else if (futureX + this.radius > boundary.x + boundary.width && futureX - this.radius < boundary.x + boundary.width &&
futureY > boundary.y && futureY < boundary.y + boundary.height) {
// Reflect horizontally if hitting the right boundary
hitBoundarySound.play(); // Play boundary hit sound
this.direction = 180 - this.direction;
this.x = boundary.x + boundary.width + this.radius; // Reset position to the edge
}
if (futureY - this.radius < boundary.y && futureY + this.radius > boundary.y &&
futureX > boundary.x && futureX < boundary.x + boundary.width) {
// Reflect vertically if hitting the top boundary
hitBoundarySound.play(); // Play boundary hit sound
this.direction = -this.direction;
this.y = boundary.y - this.radius; // Reset position to the edge
} else if (futureY + this.radius > boundary.y + boundary.height && futureY - this.radius < boundary.y + boundary.height &&
futureX > boundary.x && futureX < boundary.x + boundary.width) {
// Reflect vertically if hitting the bottom boundary
hitBoundarySound.play(); // Play boundary hit sound
this.direction = -this.direction;
this.y = boundary.y + boundary.height + this.radius; // Reset position to the edge
}
});
// Reflect from canvas boundaries
if (this.x - this.radius <= 0 || this.x + this.radius >= width) {
this.direction = 180 - this.direction; // Horizontal bounce
hitBoundarySound.play(); // Play boundary hit sound
this.x = constrain(this.x, this.radius, width - this.radius);
}
if (this.y - this.radius <= 0 || this.y + this.radius >= height) {
this.direction = -this.direction; // Vertical bounce
hitBoundarySound.play(); // Play boundary hit sound
this.y = constrain(this.y, this.radius, height - this.radius);
}
}
}
class Boundary {
constructor(x, y, width, height, opacity) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.opacity = opacity;
}
display() {
push();
fill(0, this.opacity);
rect(this.x, this.y, this.width, this.height);
pop();
}
}