Final Project

sketch
/*
 * Michelle Kim
 * michell4
 * Section B
 */

var count = 0;
var clouds = [];
var opacity = 0;

var hearts;
var hornet;
var blm;
var vote;
var covid;
var crowd;

var alarm;
var yawn;
var shower;
var gettingReady;
var ready;
var cameraShutter;
var hornetSound;
var blmSound;

var hornetX = 600;
var blmX = 600;
var voteX = 600;
var covidX = 600;
var crowdX = 600;

function preload() {
    //preloads all images and sounds
    hearts = loadImage("https://i.imgur.com/WArKv90.gif");
    hornet = loadImage("https://i.imgur.com/WSVUTau.png");
    blm = loadImage("https://i.imgur.com/JAVgfbG.png");
    vote = loadImage("https://i.imgur.com/sRU4uIO.png");
    covid = loadImage("https://i.imgur.com/BY6kaKa.jpg");
    crowd = loadImage("https://i.imgur.com/63dcnGS.png");
    tear = loadImage("https://i.imgur.com/ie45BVh.png");

    alarm = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/alarm.wav");
    yawn = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/yawn-1.wav");
    shower = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/shower.wav");
    gettingReady = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/gettingready.wav");
    ready = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/ready.wav");
    huh = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/huh.wav");
    cameraShutter = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/camerashutter.wav");
    ding = loadSound("hhttps://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/ding.wav");
    hornetSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/hornetsound.wav");
    blmSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/blmsound.wav");
    ehSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/eh.wav");
    cough = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/cough.wav");
    awSound = loadSound("https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/aw.wav");
}

function setup() {
    createCanvas(600, 600);
    useSound();
    background(220);
    for(var i = 0; i < 3; i++){
        var initialX = random(width);
        var initialY = random(0, 200);
        clouds[i] = makeClouds(initialX, initialY);
    }
    frameRate(3);
}

function soundSetup() {
    alarm.setVolume(0.2);
    yawn.setVolume(0.3);
    shower.setVolume(0.2);
    gettingReady.setVolume(0.2);
    ready.setVolume(0.2);
    huh.setVolume(0.3);
    hornetSound.setVolume(0.3);
    blmSound.setVolume(0.2);
    cameraShutter.setVolume(0.2);
    ding.setVolume(0.3);
    ehSound.setVolume(0.8);
    cough.setVolume(0.2);
    awSound.setVolume(0.2);
}

function draw() {
    background(171, 245, 255);
    //opening scene
    if (count < 10) {
        fill(0);
        square(0, 0, width);
        textSize(30);
        textFont("Helvetica");
        fill(255);
        text("One fine morning...", 150, (height / 2));
    }
    //alarm ringing
    if (count >= 10 & count < 20) {
        clock();
        if (count == 10) {
            alarm.play();
        }
        if (count == 17) {
            yawn.play();
        }
    }
    //character getting ready
    if (count >= 20 & count < 30) {
        fill(0);
        square(0, 0, width);
        if (count == 20) {
            shower.play();
        }
        if (count == 25) {
            shower.stop();
            gettingReady.play();
        }
        if (count == 28) {
            gettingReady.stop();
        }
        if (count == 25) {
            ready.play();
        }
    }
    //boy introduction
    if (count >= 30 & count < 40) {
        boy();
        if (count >= 35 & count < 38) {
            image(hearts, 430, 310, width * 0.1, height * 0.1);
        }
    }
    //clouds
    if (count >= 40) {
        updateClouds();
        removeClouds();
        addClouds();
    }
    //boy on phone
    if (count >= 40 & count < 70) {
        boyWalking();
    }
    //hornet
    if (count >= 40 & count < 105) {
        image(hornet, hornetX, 300, width * 0.05, height * 0.05);
        hornetX -= 10;
        if (count == 40) {
            hornetSound.play();
        }
        if (count >= 55 & count < 60) {
            textSize(40);
            textFont("Georgia");
            fill(0);
            text("!", 170, 350);
            if (count == 56) {
                huh.play();
            }
        }
    }
    //snap of hornet
    if (count >= 70 & count < 75) {
        phonePicture();
        image(hornet, 240, 250, width * 0.2, height * 0.2);
        if (count == 73) {
            cameraShutter.play();
        }
    }
    //boy walking after hornet
    if (count >= 75 & count < 165) {
        boyWalking();
        if (count >= 80 & count < 100) {
            image(hearts, random(150, 170), random(400, 420),
                  width * 0.2, height * 0.2);
            bubble();
            textSize(40);
            textFont("Helvetica");
            fill(150);
            text("Woah!", 295, 415);
        }
        if (count == 80) {
            ding.play();
        }
    }
    //blm protest
    if (count >= 110 & count <= 220) {
        image(blm, blmX, 200, width, height);
        if (count == 110) {
            blmSound.play();
        }
        if (count == 220) {
            blmSound.stop();
        }
        if (count >= 140 & count < 145) {
            textSize(40);
            textFont("Georgia");
            fill(0);
            text("!", 170, 350);
            if (count == 141) {
                huh.play();
            }
        }
        blmX -= 10;
        if (count < 165 || count > 170) {
        boyWalking();
        }
    }
    //snap of blm protest
    if (count >= 165 & count < 170) {
        phonePicture();
        image(blm, 175, 250, width * 0.4, height * 0.35);
        if (count == 168) {
            cameraShutter.play();
        }
    }
    //boy walking after blm protest
    if (count >= 220) {
        boyWalking();
    }
    if (count >= 190 & count < 210) {
        image(hearts, random(150, 170), random(400, 420),
              width * 0.2, height * 0.2);
        bubble();
        textSize(30);
        textFont("Helvetica");
        fill(150);
        text("Inspiring!", 290, 410);
        if (count == 190) {
            ding.play();
        }
    }
    //vote sign
    if (count >= 225 & count < 320) {
        noStroke(0);
        fill(0);
        rect(voteX + 140, 400, 20, 400);
        image(vote, voteX, 250, width * 0.5, height * 0.5);
        voteX -= 10;
        if (count < 265 || count > 270) {
        boyWalking();
        }
        if (count >= 250 & count < 255) {
            textSize(40);
            textFont("Georgia");
            fill(0);
            text("!", 170, 350);
            if (count == 251) {
                huh.play();
            }
        }
        if (count >= 280 & count < 300) {
            image(hearts, random(150, 170), random(400, 420),
                  width * 0.2, height * 0.2);
            bubble();
            textSize(25);
            textFont("Helvetica");
            fill(150);
            text("Awesome!", 290, 410);
            if (count == 280) {
                ding.play();
            }
        }
    }
    //snap of vote sign
    if (count >= 265 & count < 270) {
        phonePicture();
        image(vote, 210, 230, width * 0.3, height * 0.3);
        if (count == 268) {
            cameraShutter.play();
        }
    }
    //covid sign
    if (count >= 325 & count < 420) {
        noStroke();
        fill(0);
        rect(covidX + 50, 200, 5, 400);
        rect(covidX + 250, 200, 5, 400);
        image(covid, covidX, 100, width * 0.5, height * 0.4);
        covidX -= 10;
        boyWalking();
        if (count == 360) {
            ehSound.play();
        }
        if (count >= 345 & count < 350) {
            textSize(40);
            textFont("Georgia");
            fill(0);
            text("!", 170, 350);
            if (count == 346) {
                huh.play();
            }
        }
    }
    //crowd
    if (count >= 425 & count < 510) {
        tint(255, 50);
        image(crowd, crowdX, 300, width * 1.1, height * 0.8);
        if (count < 490) {
            crowdX -= 10;
        }
        if (count == 495) {
            cough.play();
        }
        boyWalking();
        if (count >= 505 & count < 510) {
            textSize(40);
            textFont("Georgia");
            fill(0);
            text("!", 170, 350);
            if (count == 506) {
                huh.play();
            }
        }
    }
    //boy sad face
    if (count >= 510 & count < 520) {
        boyFace();
        if (count >= 15 & count < 520) {
            tint(255, 255);
            image(tear, 250, 320, width * 0.03, height * 0.05);
        }
        if (count == 513) {
            awSound.play();
        }
    }
    //end scene
    if (count >= 520) {
        background(0);
        textFont("Helvetica");
        textSize(50);
        fill(255);
        text("Wear a mask.", 150, 300);
        textSize(20);
        text("Spread awareness.", 210, 340);
    }
    count ++;
}

function clock() {
    background(255);
    stroke(0);
    strokeWeight(20);
    line(180, 180, 200, 200);
    line(400, 200, 420, 180);
    line(290, 125, 310, 125);
    line(300, 125, 300, 150);
    strokeWeight(5);
    //black
    fill(255);
    //bells
    arc(190, 190, 155, 155, radians(140), radians(310), CHORD);
    arc(410, 190, 155, 155, radians(230), radians(40), CHORD);
    fill(0);
    //body
    circle((width / 2), (height / 2), 300);
    //bells
    arc(180, 180, 120, 120, radians(130), radians(320), CHORD);
    arc(420, 180, 120, 120, radians(220), radians(50), CHORD);
    //bells
    circle(132, 132, 30);
    circle(468, 132, 30);
    //legs
    circle(210, 430, 50);
    circle(390, 430, 50);
    //white
    noStroke();
    fill(255);
    //body
    circle((width / 2), (height / 2), 250);
    //black
    stroke(0);
    strokeWeight(5);
    fill(0);
    //inside details
    circle((width / 2), (height / 2), 20);
    line((width / 2), 190, (width / 2), (height / 2));
    line((width / 2), (height / 2), 270, 370);
    //highlights
    stroke(255);
    strokeWeight(5);
    fill(255);
    line(292, 122, 308, 122);
    noFill();
    //bells
    arc(132, 132, 20, 20, radians(150), radians(200));
    arc(468, 132, 20, 20, radians(340), radians(30));
    //body
    arc(290, 280, 250, 230, radians(200), radians(250));
    arc(310, 320, 250, 230, radians(20), radians(70));
    //legs
    arc(210, 430, 30, 30, radians(140), radians(210));
    arc(392, 430, 30, 30, radians(330), radians(40));
}

function boy() {
    background(255);
    stroke(0);
    strokeWeight(3);
    //arms
    fill(255, 245, 215);
    line(180, 380, 220, 390);
    circle(170, 375, 20);
    line(380, 390, 420, 380);
    circle(430, 375, 20);
    //shirt
    fill(174, 255, 242);
    rect(220, 370, 50, 40);
    rect(330, 370, 50, 40);
    quad(270, 330, 330, 330, 360, 470, 240, 470);
    fill(126, 225, 208);
    square(310, 390, 20);
    //face
    fill(255, 245, 215);
    circle(230, (height / 2), 20);
    circle(370, (height / 2), 20);
    circle((width / 2), (height / 2), 140);
    line(300, 215, 300, 230);
    line(290, 220, 300, 230);
    line(310, 220, 300, 230);
    fill(0);
    circle(290, 290, 5);
    circle(310, 290, 5);
    noFill();
    arc(300, 310, 70, 60, 0, radians(180));
    //phone
    fill(0);
    rect(430, 330, 20, 40);
    noFill();
    stroke(255);
    strokeWeight(1);
    rect(432, 332, 16, 30);
    circle(440, 367, 5);
}

function updateClouds() {
    //update clouds
    for (var i = 0; i < clouds.length; i++) {
        clouds[i].move();
        clouds[i].display();
    }
}

function removeClouds() {
    //get rid of clouds
    var keepClouds = [];
    for (var i = 0; i < clouds.length; i++) {
        if (clouds[i].x + 50 > 0) {
            keepClouds.push(clouds[i]);
        }
    }
    clouds = keepClouds;
}

function addClouds() {
    //add new clouds
    var newClouds = 0.05;
    if (random(0,1) < newClouds) {
        var y = random(0, 200);
        clouds.push(makeClouds(width, y));
    }
}

function moveClouds() {
    //clouds move to left
    this.x += this.speed;
}

function displayClouds() {
    //cloud drawing
    fill(255);
    noStroke();
    circle(this.x, this.y + 50, 50);
    circle(this.x + 40, this.y + 50, 70);
    circle(this.x + 80, this.y + 70, 40);
    circle(this.x + 90, this.y + 40, 50);
}

function makeClouds(cloudsX, cloudsY) {
    var cloud = {x: cloudsX,
                 y: cloudsY,
                 speed: -4,
                 move: moveClouds,
                 display: displayClouds}
    return cloud;
}

function boyWalking() {
    stroke(0);
    strokeWeight(3);
    //shirt
    fill(174, 255, 242);
    quad(70, 450, 120, 450, 150, height + 2, 60, height + 2);
    quad(90, 520, 110, 520, 115, 550, 85, 550);
    //face
    fill(255, 245, 215);
    circle(100, 420, 140);
    circle(90, 440, 10);
    fill(0);
    circle(150, 430, 5);
    noFill();
    arc(152, 450, 35, 30, radians(90), radians(180));
    line(100, 335, 100, 350);
    line(90, 340, 100, 350);
    line(110, 340, 100, 350);
    //phone
    noStroke();
    fill(209, 250, 255);
    quad(200, 465, 200, 500, 175, 530, 150, 530);
    stroke(0);
    strokeWeight(5);
    line(200, 500, 175, 530);
    //arms
    strokeWeight(3);
    line(100, 550, 100, 570);
    line(100, 570, 170, 540);
    fill(255, 245, 215);
    circle(170, 540, 20);
}

function phonePicture() {
    //phone camera
    stroke(0);
    strokeWeight(20);
    fill(171, 245, 255);
    rect(200, 100, 200, 400);
    strokeWeight(10);
    line(200, 400, 0, height);
    line(400, 400, width, height);
    stroke(255);
    strokeWeight(5);
    line(250, 220, 250, 240);
    line(250, 220, 270, 220);
    line(350, 220, 350, 240);
    line(330, 220, 350, 220);
    line(250, 380, 250, 360);
    line(250, 380, 270, 380);
    line(350, 380, 350, 360);
    line(350, 380, 330, 380);
}

function bubble() {
    //phone comments
    fill(255);
    noStroke();
    ellipse(350, 400, 150, 100);
    triangle(290, 400, 320, 440, 280, 450);
}

function boyFace() {
    //boy sad face
    background(255);
    strokeWeight(3);
    fill(255, 245, 215);
    circle(100, (height / 2), 50);
    circle(500, (height / 2), 50);
    circle((width / 2), (height / 2), 400);
    line(300, 70, 300, 100);
    line(280, 80, 300, 100);
    line(320, 80, 300, 100);
    fill(0);
    circle(270, 290, 10);
    circle(330, 290, 10);
    noFill();
    arc(300, 420, 140, 120, radians(200), radians(340));
}

For this project, I chose to create a short animation that depicted some of the major events that occurred in 2020. The increasing use of technology and social media platforms has definitely raised awareness for important causes and happenings during this year. For example, one of the biggest contributors for raising awareness and protests was social media and the sharing of information through these platforms.

My inspiration for this project was a typical commercial, one that can be watched through advertisements or on TV. I tried to imitate the basic form of story-like commercial with a message at the end. The main message that I am conveying through this short clip is spread more awareness about the importance of masks. With the currently increasing cases of COVID-19, I decided to focus on this specific theme because unfortunately, masks are still being refused by many people around the world.

If I had more time, I would have liked to add an additional scene that depicted the detrimental effects of contracting COVID-19. I believe that this would have added more of an impact to my “commercial”.

To interact with my program, simply click on the canvas to start the clip. Note: Use volume for sound.

Final Project

For my project I created a game to raise awareness towards mask wearing so the person is trying to move through covid particles to reach the mask. And basically gets saved if he does so without colliding with a covid body. If you reach the mask, the words, “You were saved show up” while if you collide with a cover particle it says, “You’ve been infected, happy recovery!”
If I had more time I would have implemented multiple levels where the speed of the covid particles begin to vary making it more difficult and instead of a generic score the levels would be how many months you have gone without being infected. Thus, again endorsing mask wearing as a norm.

Instructions use your mouse and YOU DO NOT WANT TO GET COVID SO STAY AWAY FROM THOSE NASTY RED THINGS!!

sketch
//Aadya Bhartia
//Section A 

/* The objective of the game is for the user to reach the mask and to
avoid the covid particles! Once you reach the mask you win the game and if you 
bang into a particle you lose. The purpose of the game is to encourage mask wearing 
*/

var mask;//parameter for object creation
var rad = 40; // for size of covid particle 
//person 
var personX;
var personY;
//array to store covid symbols 
var covidX = [];
var covidY = [];
var dx = [];
var dy = [];
//variables to store images 
var covid;
var person;
var maskImg;

function preload(){
    //preloading images 
	covid = loadImage("https://i.imgur.com/uT2allv.png");
    person = loadImage("https://i.imgur.com/uVjRV7S.png");
    maskImg = loadImage("https://i.imgur.com/1olzsvy.png");
}

function setup() {
    createCanvas(500, 500);
    background(220);
    //storing covid particles random x and position as well as speed 
    for(var i = 0; i<15; i++){
    	covidX[i] = random(width);
        covidY[i] = random(height);
        dx[i] = random(-5,5);
        dy[i] = random(-5,5);
    }
    mask = makeMask();
    //text formatting
    frameRate(10); 
    textAlign(CENTER);
    textFont('New Roman');
    textStyle(BOLD);
}

function draw() {
	//constraining mouse within canvas 
    var x = constrain(mouseX, 0, width-10);
	var y = constrain(mouseY, 0, height-10);
	background(220);
    //displaying mask and checking if person has touched maks 
    mask.show();
    mask.check();

	for(var i = 0; i<10; i++){
        fill(0);
    	image(covid, covidX[i], covidY[i], rad, rad);
        covidX[i] += dx[i];
        covidY[i] += dy[i];
        //to bounce covid particles off the sides 
        if(covidX[i] >= (width) || covidX[i] <= 0){
            dx[i] = -1*dx[i];
        }
        if(covidY[i] >= (height) || covidY[i] <= 0){
            dy[i] = -1*dy[i];
        }
        //when person bangs into covid particle 
        if(covidY[i]> y-15  & covidY[i]< y+15 ){
            if(covidX[i]> x-15  && covidX[i]< x+15 ){
                finishGame();
            }
        }
    }
    //person
    fill(255,0,0);
    //displaying mouse as person
    image(person,x, y, 60,100);

}
//displaying mask
function maskDisplay(){
    image(maskImg, this.x, this.y, 60,60);
}

//game won if person reaches the maks 
function achieved(){
    var d = dist(mouseX, mouseY, this.x, this.y);
    if(d<20){
        wonGame();
    }
}

//constructor
function makeMask(){
    var m = {x : random(30, width-30),
             y : random(30, height-30),
             show : maskDisplay, check : achieved}
    return m;
}
//message for when game has been lost 
function finishGame(){
    fill("black");
    textSize(30);
    text("You've been infected, happy recovery!", width/2, height/2);
    noLoop();
}
//message for when game has been won
function wonGame(){
    fill("black");
    textSize(40);
    text("YOU WERE SAVED!!!", width/2, height/2);
    noLoop();
}


Final Project

2020 was full of surprises that shook our world and changed the lives of many. My final project comments on the concept of misfortune and plays with the sense of control that many felt like they didn’t have in the face of a global pandemic, monumental election, civil rights movements, wildfires, and more. The 2020 slot machine presents the user with an attractive and exciting prospect of winning, but ultimately delivers the raw reality of this year.

To play, click and drag down on the lever, then release. The reels will spin and generate your 2020. Observe the effects of a jackpot!

I was inspired to create a slot machine for my final project because when presented with the project theme “2020,” I couldn’t narrow down to one single event that I wanted to focus on. I instead wanted to take a more macro approach and reflect on the year as a whole and satirize it with a classic casino game. With more time, I would add a greater jackpot effect, more buttons, and user interaction opportunities to the slot machine.

Icon Key
sketchDownload
//2020 SLOT MACHINE
//Elysha Tsai

//All illustrated assets made by me
var bg;
var symbolLinks = [
    "https://i.imgur.com/gj4Ztah.png", //COVID
    "https://i.imgur.com/a8qsuTb.png", //AUSTRALIAN BUSHFIRES
    "https://i.imgur.com/TLZnWQ9.png", //BLM
    "https://i.imgur.com/1tdvXwb.png", //ELECTION
    "https://i.imgur.com/SsV7YIF.png", //2020
    "https://i.imgur.com/EHQEMEP.png", //MURDER HORNETS
]
var textLinks = [
    "https://i.imgur.com/qBgKflt.png", //COVID
    "https://i.imgur.com/jTMJxU5.png", //AUSTRALIAN BUSHFIRES
    "https://i.imgur.com/L47KJMN.png", //BLM
    "https://i.imgur.com/IAmvG2D.png", //ELECTION
    "https://i.imgur.com/051k4xi.png", //2020
    "https://i.imgur.com/sRj9Ipi.png", //MURDER HORNETS
]

var maxlinks = 6;

//assign links to 3 separate reels
var reelA;
var reelB;
var reelC;

var reelimages=[];
var reeltext=[];
var reelAindex=0
var defaultText;
var textimage;
var jackpotWin;

var count =0;
var doneReel=0;
var jackpotindex=0;

//light object variables
var light =[];
var x; //position of light
var speed;

var leverpull;
//var jackpotwin;
var ball;
var slotmachine;
var chair;

function preload() {

  //IMAGES
  bg= loadImage("https://i.imgur.com/Mt81CeD.jpg"); //green gradient background
  ball= loadImage("https://i.imgur.com/5UZk7nN.png"); //lever ball
  slotmachine= loadImage("https://i.imgur.com/3OioKHj.png");
  chair= loadImage("https://i.imgur.com/KXMlSo1.png");

  defaultText= loadImage("https://i.imgur.com/dcfoYh5.png");
  jackpotWin= loadImage("https://i.imgur.com/ih2wbgn.png");

  for (var i=0; i<maxlinks; i++) { 
    reelimages[i] = loadImage(symbolLinks[i]);
  } 
  for (var i=0; i<maxlinks; i++) { 
    reeltext[i] = loadImage(textLinks[i]);
  }

//initial array assignment
  reelA=reelimages[2];
  reelB=reelimages[0];
  reelC=reelimages[4];


}

function setup() {
    createCanvas(600, 450);
    frameRate(10); //mechanical feeling of lever

    //textimage = defaultText;


    //setup light object
    var dist =0;
    for (var i =0; i<1000; i++){
      light[i]= varLight(dist);
      dist +=12; //distance b/w lights
    }
}
/*
function soundSetup() { 
    leverpull.setVolume(1);
    jackpotwin.setVolume(1.2);
    */

function draw() {
  image(bg, -50, -50, 700, 550);

  //draw lightstrip
  push();
  noStroke();
  fill(110, 186, 173); //green
  rect(0, 14, width, 12);
  pop();

  //draw lights
  for(var i = 0; i < light.length; i++){
        light[i].display();
        light[i].move();
  }

  image(slotmachine, 0, 0, 600, 450);
  image(chair, 0, 0, 600, 450);

  //draw text
  push();
  //imageMode(CENTER)
  //image(jackpotWin, 0, 0);
  pop();

  
//JACKPOT
//background flashes when hit jackpot after reels stop changing
  if ((reelA == reelB) & (reelB == reelC) && doneReel){

    //background flashes when hit jackpot after reels stop changing
    rect(0, 0, 600, 450);
    image(slotmachine, 0, 0, 600, 450);
    image(chair, 0, 0, 600, 450);
    image(chair, 0, 0, 600, 450);
     
    count ++;

    if(count==1){
      fill(255, 0, 0);
      }else if(count==2){
        fill(0, 255, 0);
      }else if(count==3){
        fill(0, 0, 255);
        count = 0; 
      }
      
      image(jackpotWin, 0, 0);
      //textimage= reeltext[jackpotindex];
      //assign jackpot symbol to text

      //displayText();
  }

    
  

  //draw images in a row
  image(reelA, 119.5, 181.5, 53, 138);
  image(reelB, 178.5, 181.5, 53, 138);
  image(reelC, 237.5, 181.5, 53, 138);

  lever(); 
}

function lever(){

//hold down mouse to pull down lever
    if (mouseIsPressed  
       & mouseX> 330 && mouseX < 370){

    var x1 = 350; //base x
    var y1 = 293; //base y
    var x2 = constrain(mouseX, 350, 350);//lever only moves vertically
    var y2 = constrain(mouseY, 191, 333);

    
  //stick
    strokeWeight(8);
    stroke(248, 230, 194); //cream
    strokeCap(ROUND);
    line(x1, y1 + (y2/40), x2, y2);
  //ball handle
    push();
    imageMode(CENTER)
    image(ball, x2, y2, 25, 25);
    pop();

    var d = dist(x1, y1, x2, y2); //line length determined by distance from base and handle
    
    }else{ //default lever state
  
  //stick
    strokeWeight(8);
    stroke(248, 230, 194);
    strokeCap(ROUND);
    line(350, 293, 350, 191);
  
  //ball handle
    push();
    imageMode(CENTER)
    image(ball, 350, 191, 25, 25); 
    pop();
      

    }
  }
  
// sleep time expects milliseconds
function sleep (time) {
  return new Promise((resolve) => setTimeout(resolve, time));
}
// based on https://stackoverflow.com/questions/951021/what-is-the-javascript-version-of-sleep

// actual wait time based on clock
function waittime(milliseconds) {
  
  const date = Date.now();

  let currentDate = null;

  do {
    
    currentDate = Date.now();

  } while (currentDate - date < milliseconds);

}


function mouseReleased(){

  if (mouseX> 330 & mouseX < 370){   //end position of lever

    reelA=reelimages[int(random(0,maxlinks))];
    reelB=reelimages[int(random(0,maxlinks))];
    reelC=reelimages[int(random(0,maxlinks))]; 
    
    doneReel=0 // start reel turning, not done yet

    //randomize symbol display with lever push
    // for each reel sleep for a random time between 0 and 200ms then show image
    // left to right
    for (var i =0; i<20; i++){
      
      sleep(50).then (()=> {
        reelAindex=int(random(0,maxlinks));
        reelA=reelimages[reelAindex];
          sleep(50).then(()=> {
          reelB=reelimages[int(random(0,maxlinks))];
            sleep(50).then(()=> {
            reelC=reelimages[int(random(0,maxlinks))]; 
          });
        });
      })
      
      waittime(int(random(0,50))) // wait random time up to 100ms before changing 
    }  
    // wait some time for all reels to settle before calling it done
    sleep(500).then(()=> {
      doneReel=1
    })
  }
}

//lightstrip functions

  function varLight(basex){
    var light ={lighty:20,
                x: basex,
                lightw:4,
                display: createLight,
                move: movestrip,
                speed: 4.0,
                }
    return light;
  }

  function createLight(){
    strokeWeight(1.5);
    stroke(227, 88, 158);//pink outline
    fill(248, 230, 194); //cream
    ellipse(this.x, this.lighty, this.lightw, this.lightw);
  }

  function movestrip(){
    this.x -= this.speed
  }
/*
function displayText(){
  push();
  imageMode(CENTER);
  image(reeltext[jackpotindex], 200, 148);
  pop();

}
/*
/*
function mousePressed(){
  if (mouseX>330 & mouseX<370 &&){
    jackpotwin.stop();
    leverpull.play();
  }
}
*/

FINAL PROJECT

I was inspired by digital interactive infographics and how they are able to relay important information to educate the audience in a very effective and engaging way. I thought it was important to show the visuals in order to help visual learners better understand and connect the information to the real world. I feel that 2020 was a rough year for many things but the current world has always been harsh to the environment. I had my infographic focused on the environment because I didn’t want people to forget about the horrible things happening to the environment because of the pandemic. I had my birds turn to grayscale to show the decrease in biodiversity due to the accelerating extinction of birds. I intended for the hills to turn to a dimmer color in order to fit the final image where the clouds and sky look gloomy with trees on fire and stone like birds flying across the page. However, I originally intended it to show how satellite images showed that the Amazon Forest is slowly becoming less green and less efficient at processing carbon dioxide due to the pollution. I am pretty happy with how the project turned out and I think I learned a lot during the debugging process and problem solving!

sketch
var trees = [];
var bird = [];
var cloud = [];
var hillVar= 0.009
var r = 188
var g = 219
var b = 245
var tr =14
var tg = 99
var tb = 42
var sky = 0
var newTreeLikelihood = 0.03; 
var newBirdLikelihood= 0.2;
var newCloudLikelihood=0.04;
var textShown = 1
var hillCondition = 0
var clickCondition = 0
var fire = 0
var fx = []
var fy = []
var birdcolor = 0
var birdDiv = ["https://i.imgur.com/L7k89AJ.png",
               "https://i.imgur.com/dnY7u7s.png",
               "https://i.imgur.com/a1m3PWp.png"]

function preload() {
  img = loadImage("https://i.imgur.com/W9zRul7.png");
  brd = loadImage(random(birdDiv));
  brdgray = loadImage("https://i.imgur.com/ile7nc8.png")
  //crackling = loadSound('https://courses.ideate.cmu.edu/15-104/f2020/wp-content/uploads/2020/12/414767__samarobryn__crackling-fire.wav')
}

function soundSetup(){
    crackling.setVolume(7)
}

function setup() {
    createCanvas(480, 480); 
    
    // create an initial collection of buildings


    for (var i = 0; i < 10; i++){
        var rx = random(width);
        var ry = random(10, 50);
        trees[i] = makeTree(rx);
        bird[i] = makeBird(rx);
        for(var f = 0; f< 8; f++){

          fx[f] = trees[i].x-trees[i].breadth*2-350//random(trees[i].x-1,trees[i].x+1);
          fy[f] = -trees[i].nFloors*20//random(trees[i].nFloors*20,-90)
      }
}
frameRate(10);
    }
    

    


function draw() {
    background(r, g, b);
    if (sky == 0) {
    r = 188
    g = 219
    b = 245
  }
  else if (sky==1) {
    r = 177
    g = 194
    b = 199
  } else if (sky == 2) {
    r = 167
    g = 169
    b = 171
  } 



    drawHill() 

    updateAndDisplayCloud();
    removeCloud();
    addNewCloud()


    

    displayHorizon();

    updateAndDisplayTrees();
    removeTreesThatHaveSlippedOutOfView();
    addNewTreesWithSomeRandomProbability(); 

    updateAndDisplayBird();
    removeBird();
    addNewBird()

    instructionbox()

}

function makeclouds(birthLocationX) {
  var k ={x: birthLocationX,
    y:round(random(10,180)),
    sizer:round(random(25,30)),
    speed:-3,
    move:cloudMove,
    display:cloudDisplay,
    click:cloudClick,
    }
    return k 
  //noStroke()
}

function cloudClick(cx,cy) {
  push()

  let dc = dist(cx,cy,this.x+20,this.y);

  if (dc < 30 ) {
      newCloudLikelihood += 0.1
      this.sizer += 3
      textShown = 4
  }
  pop()
}

function cloudMove() {
  this.x += this.speed;
  this.y += random(-3,3);
}

function cloudDisplay() {
  push();
  noStroke();
  fill(r-20,g-20,b-20);
  ellipse(this.x,this.y,this.sizer,this.sizer)
  ellipse(this.x+10,this.y+10,this.sizer,this.sizer)
  ellipse(this.x+30,this.y+10,this.sizer,this.sizer)
  ellipse(this.x+30,this.y-10,this.sizer,this.sizer)
  ellipse(this.x+20,this.y-10,this.sizer,this.sizer)
  ellipse(this.x+40,this.y,this.sizer,this.sizer)

  
  pop()
}

function updateAndDisplayCloud(){
  for (var i = 0; i <cloud.length; i++){
    cloud[i].move();
    cloud[i].display()
  }
}
function removeCloud(){
  var cloudToKeep = [];
  for (var i = 0; i <cloud.length; i++){
    if (cloud[i].x+40>0){
      cloudToKeep.push(cloud[i])
    }
  }
  cloud = cloudToKeep
}

function addNewCloud(){
  
  if(random(0,1)<newCloudLikelihood){
    cloud.push(makeclouds(width));
  }
}



function instructionbox() {
  push()
  rectMode(CENTER)
  fill(255)
  strokeWeight(8)
  rect(240,400,440,100)
  noStroke()
  fill(0)
  textSize(19.7)
  if (textShown==1){
      text("Click on objects to see how WE affected them",40,405)
    } else if (textShown==2) {
      textSize(15)
      text("Scientists have recently found that birds are going extinct",40,395)
      text("much quicker than we thought. Help by donating to RSPB!",40,415)
    } else if ( textShown ==3) {
      textSize(16.5)
      text("Wildfire has burned more than 4% of California in 2020",40,395)
      text("Help by donating to California's wildfire relief fund!",40,415)
    } else if (textShown == 4) {
      textSize(15)
      text("China has surpassed pre-COVID air pollution levels soon",40,395)
      text("after business returned to normal.",40,415)
    } else if (textShown == 5) {
      textSize(15)
      text("Satelite image shows that Amazon forest has became less ",40,395)
      text("green over time due to pollution",40,415)
    }
  
  pop()
}

function birdbox() {
  push()
  rectMode(CENTER)
  fill(255)
  strokeWeight(8)
  rect(240,400,440,100)
  noStroke()
  fill(0)
  textSize(20)
  text("Bird are ded",40,405)

  pop()
}

function mouseClicked(){
  sky += 1
  if (sky>2){
    r -= 10
    g -= 10
    b -= 10
  }

  clickCondition = 1 

  for (var i = 0; i <bird.length; i++){
    bird[i].click(mouseX, mouseY)
  }

  for (var i = 0; i <trees.length; i++){
    trees[i].click(mouseX, mouseY)
  }

  for (var i = 0; i <cloud.length; i++){
    cloud[i].click(mouseX, mouseY)
  }




}

function makeBird(birthLocationX) {
  var k ={x: birthLocationX,
    y:round(random(-60,-280)),
    breadth:round(random(40,70)),
    speed:2,
    birdHeight:round(random(9,15)),
    move:birdMove,
    display:birdDisplay,
    click:birdClick,
    color:color(random(50,240),random(50,240),random(50,240))}
    return k 
}

function birdClick(bx, by) {
  let d = dist(bx, by, this.x, height-190+this.y)
  if (d <= this.breadth){
    this.color = color(40)
    textShown = 2
    birdcolor = 1
  }


}

function birdMove() {
  this.x += this.speed
}

function birdDisplay() {
  push();
  fill(this.color)
  translate(this.x, height-190);
  strokeWeight(1);
  stroke(0);
  push()
  if (birdcolor == 1){
    image(brdgray,-0,this.y,this.breadth,this.breadth)
  } else {
    image(brd,-0,this.y,this.breadth,this.breadth)
  }
  pop()
  pop()
}

function updateAndDisplayBird(){
  for (var i = 0; i <bird.length; i++){
    bird[i].move();
    bird[i].display()
  }
}
function removeBird(){
  var birdToKeep = [];
  for (var i = 0; i <bird.length; i++){
    if (bird[i].x+bird[i].breadth>0){
      birdToKeep.push(bird[i])
    }
  }
  bird = birdToKeep
}

function addNewBird(){
  var newBirdLikelihood=0.1;
  if(random(0,1)<newBirdLikelihood){
    bird.push(makeBird(0));
  }
}



function drawHill() {
  push()
  noStroke()
  fill(tr, tg, tb)
  beginShape()
  for (var h = 0; h<width; h++){
    var a = (h*hillVar)+(millis()*0.0002)
    var y = map(noise(a),0,1,height/8,height/9*5);
    vertex(h,y)
    if (mouseY < h & y-1< mouseX =72 && tg <=87 && tb >=77){
    tr = 72
    tg = 87
    tb = 77
  }  


  pop()

  vertex(480,480);
  vertex(0,480)
  endShape()
  pop()
}

function updateAndDisplayTrees(){
    for (var i = 0; i < trees.length; i++){
        trees[i].move();
        trees[i].display();
    }
}


function removeTreesThatHaveSlippedOutOfView(){
    var treesToKeep = [];
    for (var i = 0; i < trees.length; i++){
        if (trees[i].x + trees[i].breadth > 0) {
            treesToKeep.push(trees[i]);
        }
    }
    trees = treesToKeep; 
}


function addNewTreesWithSomeRandomProbability() {
    if (random(0,1) < newTreeLikelihood) {
        trees.push(makeTree(width));
    }
}



function treeMove() {
    this.x += this.speed;
}
    


function treeDisplay() {
  push()
    var floorHeight = 20;
    var bHeight = this.nFloors * floorHeight; 
    fill(74, 21, 22);  
    push();
    translate(this.x, height - 170);
    rect(0, -bHeight, this.breadth, bHeight);
    noStroke()

    for (var i = 0; i < this.nFloors; i++) {
      fill(2, 61, 21)
      noStroke()

      triangle(this.breadth/2,0-(i * floorHeight)-60,-20,20-(i * floorHeight)-40,this.breadth+20,20-(i * floorHeight)-40);
    }

    var fireSize = random(10,80)

    if( fire == 1){
      for(var f = 0; f < 8; f++){
        image(img,fx[f],fy[f],fireSize,fireSize)
      }
    }

    strokeWeight(3)
    pop();
}



function makeTree(birthLocationX) {
    var bldg = {x: birthLocationX,
                breadth: 15,
                speed: -3.0,
                nFloors: round(random(2,8)),
                move: treeMove,
                click: treeClick,
                display: treeDisplay}
    return bldg;
}

function treeClick(tx,ty) {
  var floorHeight = 20;
  var bHeight = this.nFloors * floorHeight;
  let dtx = dist( tx, height-170, this.x+this.breadth/2, height-170)
  let dty = dist( this.x , ty, this.x, -bHeight/2+height-170 )
  if (dtx<20+this.breadth/2 & dty

Final Project – Responsive Wearables

In 2020, one of the biggest treasure is to experience the time in outdoor. In the time when we need to carefully think about distancing to other people and gearing up ourselves with masks, gloves and sanitizer, I design this interactive game to explore with the idea of using garments as an responsive exterior shell to protect ourselves and create personal boundaries.


By moving the mouse, the character is able to wondering in this projected scenery with colorful reference object. For the design of the character, there are two mode in response to two situations. The character begins with wearing a normal scale clothes. When the player pressed the mouse, the character switch to the inflatables garment set with an “alert” sign.


One of the biggest challenge I encountered with when programming this game is making paneling affect on the canvas. The canvas visually works as a camera frame that shifting with the location of the mouse. If I further develop this program, I hope to have the alert character setting on when the character intersect with every bubble in the arrays.

sketchDownload
//Isabel Xu
//Section A
//Final Project

var cX = 0;
var cY = 0;
var cdx = 0;
var cdy = 0;
var start_locationX = 300;
var start_locationY = 200;
var cir_X = [];
var cir_Y = [];
var cir_c = [];
var animGreen = [];


function preload(){
    //green character image arrays
    let green1 = loadImage("https://i.imgur.com/OXFFPY7.png");
    let green2 = loadImage("https://i.imgur.com/gIvd44d.png");
    let green3 = loadImage("https://i.imgur.com/p7dyFj3.png");
    animGreen = [green1, green2, green3];

    //red character image
    redAlert = loadImage("https://i.imgur.com/BhCQ0FG.png");

}

function setup() {
    createCanvas(600,400);
    frameRate(20);

    //create level of randomness to the bubble
    for (var i = 0; i < 7000; i++) {
        cir_X[i] = random(-5000, width+5000);
        cir_Y[i] = random(-4000, height+4000);
        cir_c[i] = color(235, random(255), random(255));
    }
    imageMode(CENTER);
}

function draw() {
    background(235, 218, 199);
    //movement of the bubble inversely to the character
    let cir_dx = ((mouseX)-300)/20*-1;
    let cir_dy = ((mouseY)-200)/20*-1;
    //create colorful bubble background for visual reference
    for (var i = 0; i < 7000; i++) {
        noStroke();
        drawShape(cir_X[i], cir_Y[i], cir_c[i]);
    
        cir_X[i] += cir_dx;
        cir_Y[i] += cir_dy;

    }

    drawFigure(cir_X[i], cir_Y[i]);
    
}

function drawShape(cir_X, cir_Y, cir_c) {
    fill(cir_c);
    ellipse(cir_X, cir_Y, 30, 15);
}

function drawFigure(){
    if (mouseIsPressed) {
        drawRedCharacter();
    }else{
        drawGreenCharacter();
    }
}
function drawGreenCharacter(){
    //limit the character movement 
    cX = constrain(mouseX,250,350);
    cY = constrain(mouseY,175,225);
    
    //shadow
    fill(110);
    ellipse(start_locationX+5,start_locationY+80,130,40)

    //draw green character
    //create animation effect with multiple image 
    let randomGreen = random(animGreen);
    image(randomGreen,start_locationX,start_locationY,150,180);

    //speed is inversely proportional to the mouse distance
    cdx = (cX - start_locationX)/20;
    cdy = (cY - start_locationY)/20;

    start_locationX += cdx;
    start_locationY += cdy;

}
function drawRedCharacter (){
    cX = constrain(mouseX,250,350);
    cY = constrain(mouseY,175,225);
    
    fill(255,0,0);
    image(redAlert,start_locationX,start_locationY,200,250);
    cdx = (cX - start_locationX)/20;
    cdy = (cY - start_locationY)/20;

    start_locationX += cdx;
    start_locationY += cdy;

}

Final Project

For my final project, I wanted to address something we have all been doing a lot
of during quarantine, which is impulsive online shopping. Seeing all the
devastating climate crisis we’ve experienced this year I wanted to do a little
research on how the actual cost of fast fashion and its damage to the environment. It’s easy to purchase a cute shirt when they’re 15 dollars even if
you didn’t need it, but in reality, it’s contributing to so many problems that
we are actually paying for: forest fires, unpaid labor, toxic waste, etc..
The program is very simple, you hover over with your mouse to see the environmental cost of each of these clothing items, and when you click on them it will be placed into your shopping bag. A certain number of the three symbols will pop up on the screen to show you how much you’ve spent. When you’ve used up around 50,000 litres of water (which doesn’t take a lot to achieve), the end screen will pop up. If I had more time I would make the symbols pop up in a more organized manner and add more levels of interactions.

sketchDownload
//keep track of number of runs
var count = 0;
//used for locating each item of clothing
var item;
//array for textbox, textbox is t
var t = [];
var textInfo = [
    'COTTON T-SHIRT:\n2,700 litres of water\n15kg of CO2\n200+ years to\ndecompose', 
    'JEANS:\n7000 litres of water\n33kg of CO2\n50 years to decompose',
    'SNEAKERS:\n8500 litres of water\n10kg of CO2\n40 years to decompose'
];
var textY = [30, 165, 355];
var textColor = ["lavender", "DarkSlateBlue"];

function preload(){
    rack = loadImage('https://i.imgur.com/YpMqJ3Z.png');
    shirt = loadImage('https://i.imgur.com/54EAgyC.png');
    jeans = loadImage('https://i.imgur.com/qzkFSXI.png');
    shoes = loadImage('https://i.imgur.com/D7paBLS.png');
    bag = loadImage('https://i.imgur.com/Xyv6IGe.png');
    water = loadImage('https://i.imgur.com/3ONgfhP.png');
    co2 = loadImage('https://i.imgur.com/me0Lg0A.png');
    clock = loadImage('https://i.imgur.com/gw7QVpQ.png');
}



//draw water
function drawWater() {
    image(water, this.x, this.y, 20, 20);
}

//draw co2
function drawCarbon(cx, cy) {
    image(co2, this.x, this.y, 20, 20);
}

//draw clock
function drawClock(clx, cly) {
    image(clock, this.x, this.y, 20, 20);
}

//create new water bucket object with position
function makeWater(wx, wy) {
    w = {x: wx, y: wy, 
         drawFunction: drawWater
        }
    return w;
}

//create new co2 object with position
function makeCarbon(cx, cy) {
    c = {x: cx, y: cy, 
         drawFunction: drawCarbon
        }
    return c;
}

//create new clock object with position
function makeClock(clx, cly) {
    cl = {x: clx, y: cly, 
         drawFunction: drawClock
        }
    return cl;
}

//arrays to store new symbol
var waters = [];
var carbons = [];
var clocks = [];
    
function setup() {
    createCanvas(400, 500);
    background("lightsteelblue");
    //set up the base rack
    image(rack, 200, 0, 200, 500);
    imageMode(CENTER);
    image(shirt, 300, 80, 120, 100);
    image(jeans, 300, 250, 80, 150);
    image(shoes, 300, 450, 100, 60);
    //create array of objects of the three info textboxes
    for (var i = 0; i < 3; i ++) {
        t[i] = new Object();
        t[i].info = textInfo[i];
        t[i].x = 170;
        t[i].y = textY[i];
        t[i].w = 110;
        t[i].h = 70;
        t[i].size = 10;
        t[i].c = textColor[i];
        t[i].font = 'verdana';
        t[i].stroke = "thistle"
        t[i].sWeight = 5;
    }

    //make a water bucket
    for (var i = 0; i < 20; i ++) {
        var w = makeWater(random(100), random(20, 100));
        waters.push(w);
    }
    //make a co2
    for (var i = 0; i < 20; i ++) {
        var w = makeCarbon(random(100), random(100, 200));
        carbons.push(c);
    }    
    //make a clock
    for (var i = 0; i < 20; i ++) {
        var cl = makeClock(random(50), random(200, 300));
        clocks.push(cl);
    }    
}


function draw() {
    //instruction
    textSize(20);
    fill("white");
    textFont(t[0].font);
    text('click to shop, hover to see price!', 20, 18);
    //draw textbox 
    if(mouseX >= 200 & mouseX <= 400) {
        if(mouseY >= 3 && mouseY <= 120) {
                stroke(t[0].stroke);
                strokeWeight(t[0].sWeight);
                fill(t[0].c);
                rect(t[0].x-5, t[0].y-5, t[0].w+10, t[0].h+10);
                noStroke();
                textSize(t[0].size);
                fill(t[1].c);
                text(t[0].info, t[0].x, t[0].y, t[0].w, t[0].h);
                item = 1;

        }
        else if(mouseY >= 150 & mouseY <= 300) {
            stroke(t[0].stroke);
            strokeWeight(t[0].sWeight);
            fill(t[0].c);
            rect(t[1].x-5, t[1].y-5, t[1].w+10, t[1].h+10);
            noStroke();
            textSize(t[0].size);
            fill(t[1].c);
            text(t[1].info, t[1].x, t[1].y, t[0].w, t[0].h);
            item = 2;

        }
        else if(mouseY >= 330 & mouseY <= 480) {
            stroke(t[0].stroke);
            strokeWeight(t[0].sWeight);
            fill(t[0].c);
            rect(t[2].x-5, t[2].y-5, t[2].w+10, t[2].h+10);
            noStroke();
            textSize(t[0].size);
            fill(t[1].c);
            text(t[2].info, t[2].x, t[2].y, t[2].w, t[2].h);
            item = 3;
        }
    }
    //place shopping bag
    shoppingBag();

    //place key
    drawKey(0, 400);

    //if 50 buckets of water used, display end page
    if(count >= 50) {
        endPage();
    }  
}
function mousePressed() {

    //when clicked into each area, item goes to shopping bag
    if(item == 1) {
        buyShirt(120, 450);

        //3 water buckets
        push();
        for (var i = 0; i < 3; i ++) {
            var w = waters[i];
            w.drawFunction();
            translate(random(10, 15), random(10, 15));  
        }
        pop();

        //3 co2
        push();
        for (var i = 0; i < 3; i ++) {
            var c = carbons[i];
            c.drawFunction();
            translate(random(10, 15), random(10, 15));  
        }
        pop();

        //20 clocks
        push();
        for (var i = 0; i < 20; i ++) {
            var cl = clocks[i];
            cl.drawFunction();
            translate(random(5, 10), random(5, 10));  
        }
        pop();      
        //add 3 to overall count
        count += 3;
    } 
    else if(item == 2) {
        buyJeans(120, 470);

        //7 water buckets
        push();
        for (var i = 0; i < 7; i ++) {
            var w = waters[i];
            w.drawFunction();
            translate(random(10, 15), random(10, 15));
        }
        pop();
        //6 co2
        push();
        for (var i = 0; i < 6; i ++) {
            var c = carbons[i];
            c.drawFunction();
            translate(random(10, 15), random(10, 15));  
        }
        pop();

        //5 clocks
        push();
        for (var i = 0; i < 5; i ++) {
            var cl = clocks[i];
            cl.drawFunction();
            translate(random(5, 10), random(5, 10));  
        }
        pop();   
        //add 7 to overall count
        count += 7;
    }
    else {
        buyShoes(120, 450);

        //8 water buckets
        push();
        for (var i = 0; i < 8; i ++) {
            var w = waters[i];
            w.drawFunction();
            translate(random(10, 15), random(10, 15));
        }
        pop();

        //2 co2
        push();
        for (var i = 0; i < 2; i ++) {
            var c = carbons[i];
            c.drawFunction();
            translate(random(10, 15), random(10, 15));  
        }
        pop();

        //4 clocks
        push();
        for (var i = 0; i < 4; i ++) {
            var cl = clocks[i];
            cl.drawFunction();
            translate(random(5, 10), random(5, 10));  
        }
        pop();  
        //add 8 to overall count
        count += 8;
    }

}

function shoppingBag() {
    image(bag, 120, 470, 170, 170);
    fill("white");
    textSize(20);
    text('your bag', 75, 480);
}

function drawKey(x, y) {
    //key box
    stroke("thistle");
    strokeWeight(5);
    fill("lavender");
    rect(x, y, 70, 80);
    //symbols
    image(water, x+15, y+20, 20, 20);
    image(co2, x+15, y+45, 20, 20);
    image(clock, x+15, y+65, 20, 20);
    //text 
    textSize(7);
    fill("black");
    noStroke();
    text('1000L water\n\n5kg co2\n\n10 years', x+30, y+15, 40, 80);
    stroke("thistle");
}


//clothing item goes into shopping bag
function buyShirt(x, y) {
    push();
    rotate(radians(random(-2, 2)));
    image(shirt, x, y, 120, 100);
    pop();
    }

function buyJeans(x, y) {
    push();
    rotate(radians(random(-2, 2)));
    image(jeans, x, y, 80, 150);
    pop();
}

function buyShoes(x, y) {
    push();
    rotate(radians(random(-2, 2)));
    image(shoes, x, y, 100, 60);
    pop();
}

function endPage() {
        background("steelblue");
        fill("lavender");
        noStroke();
        textSize(30);
        text('Sorry,\nYou have used up \n50 years worth of \ndrinking water.\n\nRefresh to shop again',
            50, 100)
    
}

Final Project

This final project was inspired by what’s currently transpiring to aquariums within the country during this pandemic. I’m a huge fan of aquariums and am constantly nervous about how much these vital institutions are struggling to stay afloat while visitors can’t go walk through their physical buildings and admire all of the different marine life. I wanted this project to bring a little awareness to this extremely niche but nevertheless devastated part of the current fabric of the world being heavily impacted by the pandemic.

For this project, the viewer just needs to click on the canvas to cycle through
different facts about COVID-19’s impact on aquariums and marine life. They can also make the aquarist move across the screen and view the passing marine life with their mouse. The visuals and designs of the animals were based off the Monterey Bay Aquarium‘s illustrated advertisements. Each animal moves across the screen at different speeds as the illustrated background moves with them.

If I had more time on this project, I definitely would have added a level of
interactivity with the marine life, whether through animations triggered by clicks or sounds to accompany the piece. I’m pretty proud of how it turned out though.

sketch
var teal;
var barColor;

var tankbg;
var kelp;
var sunlight;

var sidewayScroll = 0;
var bgLength = 2100;
var bgHeight = 600;

let turtleCycle = [];
let animatedFrame = 0;
let turtleX = 0;
let time = 0;

let rayY = 0;
let rayX = 0;
var ray;

let seahorseY = 0;
let seahorseX = 0;
var seahorse;

var sharkY = 0;
var sharkX = 0;
var shark;

var orangefishX = 0;
var orangefishY = 0;
var orangefish;

var dx = [];
var c = [];
var x = [];
var y = [];

var employeeLeft;
var employeeRight;

var facts = ["Because the pandemic has reduced tourism, there has been a huge baby sea turtle boom in Florida's beaches.", "Stingrays and other fish have been showing signs of loneliness since the pandemic started due to the lack of visitor interactions.", "An endangered species of seahorse has returned to Dorset in the UK due to the coronavirus lockdown.", "Aquariums around the country are struggling to stay afloat due to COVID-19 closures and lack of financial help from the federal government.", "Many aquariums are now showing exhibits virtually to keep visitors engaged at home."];
var index = 0;

function preload() {
    //exhibit images
    tankbg = loadImage("tankbg.png");
    kelp = loadImage("kelp.png");
    sunlight = loadImage("light.png");

    //turtle images
    turtleCycle[0] = loadImage("turtle-02.png");
    turtleCycle[1] = loadImage("turtle-03.png");
    turtleCycle[2] = loadImage("turtle-04.png");
    turtleCycle[3] = loadImage("turtle-05.png");
    turtleCycle[4] = loadImage("turtle-06.png");
    turtleCycle[5] = loadImage("turtle-05.png");
    turtleCycle[6] = loadImage("turtle-04.png");
    turtleCycle[7] = loadImage("turtle-03.png");

    //stingray image
    ray = loadImage("ray.png");

    //seahorse image
    seahorse = loadImage("seahorse.png");

    //shark
    shark = loadImage("shark.png");

    //orange fish
    orangefish = loadImage("orangefish.png");

    //employee
    employeeLeft = loadImage("employee_left.png");
    employeeRight = loadImage("employee_right.png");

}

function setup() {
    createCanvas(600, 450);

    //tank setup
    teal = color(89, 185, 189);
    barColor = color(20, 48, 67);

    //fish setup
    for (i = 0; i < 100; i++) {
        dx[i] = random(-2, 2);
        c[i] = color(random(100), random(175), random(100), 50);
        x[i] = random(25, width - 25);
        y[i] = random(25, height - 25);
    }

}

function draw() {
    noStroke();
    background(teal);

    //sunlight
    image(sunlight, width/4, 0, 400, bgHeight);

    for (i = 0; i < 100; i++) {
        fish(x[i], y[i], dx[i], c[i]);
        x[i] += dx[i];

        if (x[i] > width - 20){
            dx[i] = -dx[i];
        }
        else if (x[i] < 0){
            dx[i] = -dx[i];
        } 
    }

    //shark
    image(shark, sharkX, sharkY, 189.42, 47.7);

    sharkX = sharkX + 1;
    if (sharkX >= width) {
        sharkX = -300;
        sharkY = random(200, 400);
    }

    //scrolling background
    image(tankbg, sidewayScroll, -150, bgLength, bgHeight);
    image(tankbg, sidewayScroll + bgLength, -150, bgLength, bgHeight);

    //sea turtle
    image(turtleCycle[animatedFrame], turtleX, 100, 200, 97);

    if (time > 13) {
        turtleX += 13;
        animatedFrame += 1;
        if (animatedFrame >= turtleCycle.length) {
            animatedFrame = 0;
        }
        time = 0;
    }
    time++;
    if (turtleX > width) {
        turtleX = -500;
    }

    //orange fish
    image(orangefish, orangefishX, orangefishY, 243, 86.3);

    orangefishX = orangefishX + 2;
    if (orangefishX >= width) {
        orangefishX = -600;
        orangefishY = random(0, 400);
    }

    //seahorse
    image(seahorse, seahorseX, seahorseY, 25, 45);
    image(seahorse, seahorseX + 50, seahorseY + 50, 35, 55);

    seahorseX = seahorseX + 0.5;
    if (seahorseX >= width) {
        seahorseX = -100;
        seahorseY = random(0, 400);
    }

    //scrolling kelp
    image(kelp, sidewayScroll, -150, bgLength, bgHeight);
    image(kelp, sidewayScroll + bgLength, -150, bgLength, bgHeight);

    sidewayScroll -= 1;
    if (sidewayScroll <= -bgLength) {
        sidewayScroll = 0;
    }

    //stingray
    image(ray, rayX, rayY, 150, 192);

    rayY = rayY + 1;
    if (rayY >= height) {
        rayY = -300;
        rayX = random(-400, 100);
    }
    else {
        rayX = rayX + 1;
    }
    
    //glass tank bars
    fill(barColor);
    rect(200, 0, 15, height);
    rect(400, 0, 15, height);

    //aquarium employee
    let canvasEdge = constrain(mouseX, width/4, 400);
    if (mouseX < width/2) {
        image(employeeLeft, canvasEdge, 300, 82, 172.6);
    } else {
        image(employeeRight, canvasEdge, 300, 82, 172.6);
    }
 
    //pandemic facts
    let factsEdge = constrain(mouseX, 50, 400);
    fill(255, 200);
    rect(factsEdge, 200, 175, 85, 10);
    fill(0);
    textSize(10);
    textFont("Volte");
    text(facts[index], factsEdge + 15, 212, 150, 200);

}

//background fish
function fish(x, y, dx, c) {
    fill(c);
    ellipse(x, y, 20, 10);

    if(dx < 0) {
        triangle(x+10, y, x+15, y-5, x+15, y+5);
    }
    else {
        triangle(x-10, y, x-15, y-5, x-15, y+5);
    }
}

//click to cycle through pandemic facts
function mousePressed() {
    index = floor(random(facts.length));
    if (index == facts.length) {
        index = 0;
    }
}
Exhibit background, made in Illustrator
Sea turtle, made in Illustrator
Stingray, made in Illustrator
Spotted Bamboo Shark, made in Illustrator
Seahorse, made in Illustrator
Aquarist, made in Illustrator

Final Project

The project is representing two events we had to deal with in 2020, the so-called Asian murder hornets and the coronavirus.
The project is a game that mimics arcade-style 2D shooter games.
The player is a hornet facing off against cells of the coronavirus and taking them down with glowing green bullets.
Whenever a virus reaches the bottom of the screen, the player loses.

While developing this project, I came across a lot of problems , the most complicated being the interaction between the bullet and virus.
I was not sure how to call a property of an object in an array while comparing it to another property of an object in another array and then making something happen with it.
However, I ended up using a nested for loop to linear search both the virus and bullet and using a conditional to check if the x and y value of those searched are matching.

Creating a project for myself and implementing the coding strategies I’ve learned over this semester definitely cemented what I’ve learned and see the usage of these techniques in my own way.
Overall, I learned about my strength and weaknesses when tackling a coding project through this experience.

sketch

var movement = []
var bullets = [];
var xpos;
var ypos;
var cv;
var score;
var c=0
var virus = [];
var cvimage = [];
var cvlinks = [
"https://i.imgur.com/Nw3KzRI.png", // virus images
"https://i.imgur.com/JlYsbHq.png",
"https://i.imgur.com/rIXhNzt.png",
"https://i.imgur.com/SjJmecC.png"
]
var count = 0;

function preload(){
  for (var i = 0; i<4; i++){ // loading images into array
    cvimage[i] = loadImage(cvlinks[i]);
  }
  hornet = loadImage("https://i.imgur.com/tU0dXCU.png")
  hornetshadow = loadImage("https://i.imgur.com/JErLPWR.png")
}
function setup() {
    createCanvas(500, 500);
    for (var i = 0; i < 100; i++){ // initial background lines
        var rx = random(width);
        var ry = random(height);
        movement[i] = makemovement(rx,ry);
    }
}
function draw() {
    background(255)
    startgame();
    updatemovement();
    removemovement();
    addmovement();
    updatevirus();
    addvirus();
    removevirus();
    drawhornet();
    updateremovebullet();
    bulletvirusinteraction();
    lose();
    count++
}

function startgame(){ // openning of game
  push()
  translate(width/2,height/2)
  fill(0)
  scale(c)
  circle(0,0,5)
  c+=2
  pop()
}
// BACKGROUND MOVEMENT
function updatemovement(){
    for (var i = 0; i < movement.length; i++){ //background lines appearing and moving
        movement[i].move(); //moving
        movement[i].display(); // appearing
    }
}
function removemovement(){
    var movementtokeep = []; // array to keep existing lines
    for (var i = 0; i < movement.length; i++){ //going through lines to see which to keep
        if (movement[i].y + 100 < 500) {
            movementtokeep.push(movement[i]);
        }
    }
    movement = movementtokeep; //return kept lines back inot movement array
}
function addmovement() {
      for (var i = 0; i < 100; i++){ // adding more background lines
          var rx = random(width);
          var ry = random(height);
          movement.push(makemovement(0))
          movement[i] = makemovement(rx,ry);
      }
}
function mvmtMove() {
    this.y += this.speed; // lines movement down canvas
}
function mvmtDisplay() { // white lines
    stroke(255);
    line(this.x,this.y,this.x,this.y+100);
}
function makemovement(startx,starty) { //properties of background object
    var mvmt = {x: startx,
                y: starty,
                speed: 1,
                move: mvmtMove,
                display: mvmtDisplay}
    return mvmt;
}
function drawhornet(){
    xpos=constrain(mouseX,0,width-50); // keep hornet in canvas
    ypos=map(mouseY,0,height,350,450);// keep hornet in bottom portion of the canvas
    image(hornet,xpos,ypos,50,50); // draw hornet
}

// VIRUS

function updatevirus(){
    for (var i = 0; i < virus.length; i++){ // showing and mocing viruses
        virus[i].move();
        virus[i].display();
    }
}
function addvirus() { // creating a new row of virus
    if (count % 100 == 0){
      for (var i = 0; i < 4; i++){
          virus.push(makecv(int(random(10))*50)); // x position of virus is at mulitples of 50
      }
    }
}
function removevirus(){ // if virus is less than height of canvas the virus is kept
    var viruskeep = [];
    for (var i = 0; i < virus.length; i++){
        if (virus[i].y < height) {
            viruskeep.push(virus[i]);
        }
    }
    virus=viruskeep
}
function makecv(startx) { // object propeties of virus with x value being varied from loop from before
    var virus = {x:startx,
                y: -100,
                speed:.5,
                img: random(cvimage),
                move: virusmove,
                display: virusdisplay}
    return virus;
}
function virusmove() { // virus movement
    this.y += this.speed;
}
function virusdisplay() {// show virus
    image(this.img,this.x,this.y,50,50);
}

//BULLET

function mousePressed(){ // when mouse is pressed a new bullet is generated
      var startingbullet = makebullet(xpos, ypos);
      bullets.push(startingbullet);
}
function updateremovebullet(){ //move and show bullet while keeping if under 150 counts to keep array short
  newbullets = [];
  for (var i = 0; i < bullets.length; i++) {
      var b = bullets[i];
      b.stepFunction();
      b.drawFunction();
      if (b.age < 150) {
          newbullets.push(b);
      }
  }
  bullets = newbullets; // kept bullets back into bullets array
}
function bulletmove() { // bullet movement
    this.y -= this.dy;
    this.age ++
  }
function bulletdisplay() { // glowing green bullets
    fill(0,200,0)
    ellipse(this.x,this.y,10)
    fill(0,255,0)
    ellipse(this.x,this.y,5)
}
function makebullet(bulletx, bullety) { // bullet object properties
    b = {x: bulletx,
         y: bullety,
         dy: 4,
         age: 0,
         stepFunction: bulletmove,
         drawFunction: bulletdisplay
        }
    return b;
}
function bulletvirusinteraction (){
  for (var i= virus.length-1;i >0; i--) { // linear search virus backwards so when spliced it doesnt skip one
    for (var j= bullets.length-1;j >0; j--) { // linear search bullets
      if ((bullets[j].x <= virus[i].x+50) // range of bullet and virus x values must match
        & (bullets[j].x >= virus[i].x-50)
        && (bullets[j].y <= virus[i].y+25)){ // range of bullet and virus y values must match
      virus.splice(i,1); //if they match then both bullet and virus disappear from the array
      bullets.splice(j,1);
      }
    }
  }
}
function lose (){
    for (var i= 0 ;i < virus.length; i++) {
      if ( virus[i].y==450){ // if any virus' y value passes 450 then player loses
        gameover(); // trigger gameover screen
      }
    }
}
function gameover (){
  fill(255,0,0);
  rect(0,0,500,500);
  noStroke();
  fill(0);
  textSize(50);
  text('GAME OVER',100, height/2);
  textSize(20);
  frameRate(0); // end frame
}

Final Project

sketch
/*
Lauren Kenny
lkenny@andrew.cmu.edu
Section A

This program draws a grid of masks and covid particles.

*/

// ARRAY OF IMAGE LINKS
var objectLinks = [
    "https://i.imgur.com/1GlKrHl.png",
    "https://i.imgur.com/EJx0rn7.png",
    "https://i.imgur.com/uz2coXH.png",
    "https://i.imgur.com/rxFAYmh.png",
    "https://i.imgur.com/SWkTR5H.png",
    ]

// ARRAY TO STORE LOADED IMAGES
var objectImages = [];

// ARRAYS TO STORE RELEVANT IMAGE VALUES
var objectX = [0, 0, 0, 0, 0];
var objectY = [0, 0, 0, 0, 0];
var objectW = [672/8, 772/5, 847/5, 841/5, 744/8];
var objectH = [656/8, 394/5, 323/5, 410/5, 828/8];

// LOADS IMAGES
function preload() {
    for (var i=0; i<objectLinks.length; i++) {
        objectImages.push(loadImage(objectLinks[i]));
    }
}

function setup() {
    createCanvas(800, 500);
    frameRate(4);
}

function draw() {
    background(180);
    var x=0;
    var y=0;
    // CREATES A GRID OF MASK/COVID IMAGES
    for (let rows=0; rows<=height; rows+=100) {
        x=0;
        for (let cols=0; cols<=width; cols+=150) {
            i = int(random(0, objectImages.length));
            image(objectImages[i], x, y, objectW[i], objectH[i]);
            x+=150;
        }
        y+=100;
    }

    // WHEN THE MOUSE IS PRESSED, TEXT APPEARS
    if (mouseIsPressed) {
        fill(0, 0, 0, 200);
        rect(0, 0, width, height);
        fill(255);
        textSize(35);
        text('beware of covid', width/3, height/2);
        }   
}


Final Project :)

Let’s hope this plays well with WordPress…

This really might not play well with WordPress, so if not feel free to download this and run it like any other sketch. You need to have the p5.js file in the same folder as index.html and sketch.js.

Download Link

So here’s my final project. The theme is supposed to be a survival horror sort of game, but taking one look tells you that isn’t quite the case. Everything is pretty barebones and there are no real enemies, so you don’t need to “survive” anything. You were also supposed to pick up a mask on the way out of your “house,” but I decided to avoid loading textures after testing on another PC. There’s a high chance that this may run slowly for you and I didn’t want it to be even worse by adding additional complexity. So now you need to go find a purple “virus crystallization” and take it to a blue square for scientists to research. Once this happens, your screen will turn grey and that’s it. What happens? Who knows, you can decide that for yourself. 

As for controls, it’s pretty much standard PC keyboard controls. Use WASD as if they were the arrow keys, and click your mouse within the sketch to lock it so you can look around. If the game is too dark, press E to turn on the lights. If the game is too small, press F to enter fullscreen. You can’t see much if you fly around because of the render distance, but if you really want to, press G.

Oh, also, there’s no collisions. You can walk through walls if you really want to. This is mostly because I didn’t have the time to completely reinvent my movement and map generation to work with collisions. The main issue was checking multiple objects in one loop where each one would change my velocity vector, often setting it to the default value. Either way, I still think it’s kind of neat to walk around a maze and see what it looks like.

The maze will be 10×10, so go look for a purple square, then head to the blue one. The maze also changes every time you reload the page, so if you find this especially fun feel free to play again.

sketchAAAAAAAA

renderDistance = 2500;

/**
 * @file sketch.js
 * @brief Demo involving 3D movement, lighting, and map creation.
 *
 * @author Lance Yarlott <lcy@andrew.cmu.edu>
 */

/**
 * @brief creates canvas and generates a maze
 */
function setup() {
    createCanvas(600, 600, WEBGL);
    
    //noStroke();
    var cam = createCamera();
    var cam_dq = 20;
    p = makePlayer(cam, cam_dq, 5);

    locked = false;
    walking = true;
    fs = false;
    tiltAngle = 0;
    panAngle = 0;
    lightsOn = true;
    roomSize = 1000;

    ctr = 0;

    var maze = generateMaze(3, 3);
    
    mapArr = new Array();

    for (var i = 0; i < maze.length; i++) {
        mapArr[i] = new Array();
        for (var j = 0; j < maze[i].length; j++) {
            mapArr[i][j] = roomFromCell(maze[i][j], roomSize, "#990000");
            if (maze[i][j].entrance) p.setCell([i, j]);
        }
    }

    p.update();
}

/**
 * @brief aims to draw maze in 3D, currently NON-non-functional :)
 */
function draw() {
    background(0);

    if (!p.won) {
        p.update();

        enableLight(p.camX, p.camY, p.camZ);

        if (lightsOn) {
            ambientLight(20);
        } else {
            ambientLight(100);
        }

        renderMap(mapArr);

        ctr = (ctr + 1) % 360;

        if (mapArr[p.graphCell[0]][p.graphCell[1]].corrupt) {
            p.inventory.push("mask");
            mapArr[p.graphCell[0]][p.graphCell[1]].corrupt = false;
        }

        if (mapArr[p.graphCell[0]][p.graphCell[1]].exit & 
                                p.inventory.includes("mask")) {
            p.won = true;
        }
    } else {
        p.cam.setPosition(0, 5000, 0);
        background(128);
        noLoop();
    }
}

/**
 * @brief though this is a working function, it's a mess
 * 
 * The x and y assignments are absolutely backwards at times and I should fix
 * them. However, if it ain't broke, don't fix it. Which is to say, I don't want
 * to debug this for another 4 hours. Apologies to any grader that has to read
 * this.
 * 
 * @param {int} sizeX horizontal size for array/graph
 * @param {int} sizeY vertical size for array/graph
 */
function generateMaze(sizeX, sizeY) {
    var maze = new Array();

    for (var i = 0; i < sizeY; i++) {
        maze[i] = new Array();
        for (var j = 0; j < sizeX; j++) {
            maze[i][j] = makeCell(j, i, sizeX, sizeY);
        }
    }

    randX = floor(random(0, sizeY));
    if (randX == 0 || randX == sizeY - 1) {
        randY = floor(random(0, sizeX));
    } else {
        randY = floor(random(0, 2)) * (sizeX - 1);
    }

    randEX = floor(random(0, sizeY));
    randEY = floor(random(0, sizeX));

    while (dist(randX, randY, randEX, randEY) < sizeY / 2) {
        randEX = floor(random(0, sizeY));
        randEY = floor(random(0, sizeX));
    }

    randIX = floor(random(0, sizeY));
    randIY = floor(random(0, sizeX));

    maze[randX][randY].entrance = true;
    maze[randEX][randEY].exit = true;
    maze[randIX][randIY].corrupt = true;

    mazeAlgo(maze, maze[randX][randY]);

    return maze;
}

/**
 * @brief creates a graph node
 * 
 * This is a constructor for a simplified graph data structure
 * 
 * @param {int} cx array column
 * @param {int} cy array row
 * @param {int} sizeX width of graph
 * @param {int} sizeY height of graph
 */
function makeCell(cx, cy, sizeX, sizeY) {
    c = {x: cx, y: cy, sx: sizeX, sy: sizeY, 
        neighbors: new Array(), walls: [1, 1, 1, 1], visited: false,
        entrance: false, exit: false, corrupt: false,
        neighborFunction: getNeighbors};
    c.neighbors = c.neighborFunction();
    return c;
}

/**
 * @brief gets all neighbors for a graph cell in the order of NESW
 * 
 * If a neighbor is invalid, replace index with [-1, -1]
 */
function getNeighbors() {
    var x = this.x;
    var y = this.y;
    var sx = this.sx;
    var sy = this.sy;
    var neighbors = new Array();
    neighbors[0] = (y - 1 >= 0) ? [y-1, x] : [-1, -1];
    neighbors[1] = (x + 1 < sx) ? [y, x+1] : [-1, -1];
    neighbors[2] = (y + 1 < sy) ? [y+1, x] : [-1, -1];
    neighbors[3] = (x - 1 >= 0) ? [y, x-1] : [-1, -1];

    return neighbors;
}

/**
 * @brief updates cell visited and walls parameters
 * 
 * "connects" cell 1 to cell 2, setting wall between the two to 0
 * 
 * @param {object} c1 graph cell
 * @param {object} c2 graph cell
 * @param {int} direction 0-3 variable that dictates direction NESW
 */
function connectCells(c1, c2, direction) {
    c1.visited = true;
    c2.visited = true;
    c1.walls[direction] = 0;
    c2.walls[(direction + 2) % 4] = 0;
}

/**
 * @brief checks if a neighbor's array index is invalid
 * 
 * @param {object} c graph object for neighbor check
 * @param {int} n index for c's neighbor array
 */
function isValidNeighbor(c, n) {
    return c.neighbors[n][0] != -1;
}

/**
 * @brief checks if all neighbors for cell c have a visited bit set
 * 
 * @param {object[][]} mazeArr parent array for graph
 * @param {object} c graph cell object
 */
function allNeighborsVisited(mazeArr, c) {
    for (var i = 0; i < c.neighbors.length; i++) {
        if (isValidNeighbor(c, i)) {
            if (!cellFromNeighbor(mazeArr, c, i).visited) {
                return false;
           }
        }
    }
    return true;
}

/**
 * @brief retrieves a neighbor cell from its parent array, based on the cell c
 * 
 * @param {object[][]} A array of cell objects
 * @param {object} c cell object
 * @param {int} n index into c's neighbor array
 */
function cellFromNeighbor(A, c, n) {
    return A[c.neighbors[n][0]][c.neighbors[n][1]];
}

/**
 * @brief recursive function used to generate a maze, DFS algo
 * 
 * @param {object[][]} mazeArr 2d array that holds graph cells 
 * @param {object} startCell graph cell object
 */
function mazeAlgo(mazeArr, startCell) {
    if (allNeighborsVisited(mazeArr, startCell)) {
        startCell.visited = true;
        return 1;
    } else {
        var visitedCells = new Array();
        randCell = floor(random(0, 4));
        while (!isValidNeighbor(startCell, randCell) || 
                cellFromNeighbor(mazeArr, startCell, randCell).visited) {
            randCell = floor(random(0, 4));
        }

        while (!allNeighborsVisited(mazeArr, startCell) & 
                !arraysEqual(visitedCells, [1,1,1,1])) {
            visitedCells[randCell] = 1;
            connectCells(startCell, cellFromNeighbor(mazeArr, startCell, 
                randCell), randCell);

            mazeAlgo(mazeArr, cellFromNeighbor(mazeArr, startCell, randCell));

            while ((!isValidNeighbor(startCell, randCell) || 
                    cellFromNeighbor(mazeArr, startCell, randCell).visited) &
                    !arraysEqual(visitedCells, [1,1,1,1])) {
                randCell = floor(random(0, 4));
                if (visitedCells[randCell] != 1) visitedCells[randCell] = 1;
            }
        }
    }
}

/**
 * @brief Checks equality of elements in an array, type assumed to be int,
 * length assumed to be equal
 * 
 * @param {int[]} A first int array for comparison
 * @param {int[]} B second int array for comparison
 */
function arraysEqual(A, B) {
    for (var i = 0; i < B.length; i++) {
        if (A[i] != B[i]) return false;
    }
    return true;
}

// TODO: make a functional wall and corridor object

/**
 * @brief room object constructor, rooms assumed to be square
 * 
 * TODO: add check for extra walls active in other rooms
 * 
 * @param {int} x x center of room
 * @param {int} y y center, defaults to 250 for the floor
 * @param {int} z z center
 * @param {int} sx x size of room
 * @param {int} sy y size, defaults to 1 for the floor
 * @param {int} sz z size
 * @param {int[]} walls array of active walls
 * @param {p5.Color} color colors the room, passed onto walls
 */
function makeRoom(x, y=250, z, sx, sy=1, sz, walls, color, entr, exit, corrpt) {
    r = {x: x, y: y, z: z, sx: sx, sy: sy, sz: sz, walls: walls, c: color, 
        wallObjs: new Array(), entrance: entr, exit: exit, corrupt: corrpt,
        drawFunc: drawRoom, update: updateRoom};

    for (var i = 0; i < r.walls.length; i++) {
        if (r.walls[i] == 1) {
            switch(i) {
                case 0:
                    r.wallObjs[i] = makeWall(x, y, z-sx/2, sx, sx, 1, false, 
                        "#FFFFFF");
                    break;
                case 1:
                   r.wallObjs[i] = makeWall(x+sx/2, y, z, sx, sx, 1, true, 
                        "#FFFFFF");
                    break;
                case 2:
                    r.wallObjs[i] = makeWall(x, y, z+sx/2, sx, sx, 1, false, 
                        "#FFFFFF");
                    break;
                case 3:
                    r.wallObjs[i] = makeWall(x-sx/2, y, z, sx, sx, 1, true, 
                        "#FFFFFF");
                    break;
                default:
                    r.walls[i] = -1;
                    break;
            }
        }
    }
    return r;
}

/**
 * @brief draws room, starting from the floor, handles computation of wall 
 * position
 */
function drawRoom() {
    // floor
    push();
    translate(this.x, this.y+this.sx/2, this.z);
    fill(this.c);
    box(this.sx, this.sy, this.sz);
    pop();

    // ceiling
    push();
    translate(this.x, this.y-this.sx/2, this.z);
    fill(this.c);
    box(this.sx, this.sy, this.sz);
    pop();

    if (this.corrupt) {
        drawRotatingDiamond([this.x, this.y, this.z], 200, this.c);
    }

    for (var i = 0; i < this.walls.length; i++) {
        if (this.walls[i] == 1) {
            this.wallObjs[i].drawFunc();
        }
    }
}

function updateRoom() {
    for (var i = 0; i < this.walls.length; i++) {
        if (this.walls[i] == 1) {
            p.mvmtVec = handleCollision(getMovementVector(p), 
                p.panTilt[0], p.samples, this.wallObjs[i].bounds);
        }
    }
}

/**
 * @brief constructor for a wall environment object
 * 
 * depth is set to 1 by default to allow light collisions with the object
 * 
 * @param {int} x x position
 * @param {int} y y position
 * @param {int} z z position
 * @param {int} sx x size (width)
 * @param {int} sy y size (height)
 * @param {boolean} rotated dictates whether the wall is rotated 90 degress
 * @param {p5.Color} color p5.js color variable
 */
function makeWall(x, y, z, sx, sy, sz=1, rotated, color) {
    w = {x: x, y: y, z: z, sx: sx, sy: sy, sz: sz, rotated: rotated, c: color,
        bounds: new Array(), drawFunc: drawWall};

    if (!rotated) w.bounds = [[x - sx/2, z - sz], [x + sx/2, z + sz]];
    else w.bounds = [[x - sz, z - sx/2], [x + sz, z + sx/2]];

    return w;
}

/**
 * @brief wall object method to draw wall on canvas.
 */
function drawWall() {
    push();
    translate(this.x, this.y, this.z);
    if (this.rotated) rotateY(radians(90));
    fill(this.c);
    box(this.sx, this.sy, this.sz);
    pop();
}

/**
 * @brief currently creates a room object based on a graph cell
 * 
 * @param {object} c graph cell describing room information
 * @param {int} s size of room
 * @param {p5.Color} color self explanatory
 */
function roomFromCell(c, s, color) {
    r = makeRoom(c.x * s, 0, c.y * s, s, 1, s, c.walls, color,
        c.entrance, c.exit, c.corrupt);
    return r;
}

function mapUpdate(mapArr) {
    for (var i = 0; i < mapArr.length; i++) {
        for (var j = 0; j < mapArr[i].length; j++) {
            if (arraysEqual(p.graphCell, [i, j])) mapArr[i][j].update();
        }
    }
}

/**
 * @brief renders the map while trying to avoid frame drops
 * 
 * @param {object[][]} mapArr array of room objects to be rendered
 */
function renderMap(mapArr) {
    for (var i = 0; i < mapArr.length; i++) {
        for (var j = 0; j < mapArr[i].length; j++) {
            if (dist(p.xyz[0], p.xyz[1], p.xyz[2], mapArr[i][j].x, 
                    mapArr[i][j].y, mapArr[i][j].z) <= renderDistance){
                if (mapArr[i][j].entrance) {
                    mapArr[i][j].c = "#00FF00";
                } else if (mapArr[i][j].exit) {
                    mapArr[i][j].c = "#0000FF";
                } else if (mapArr[i][j].corrupt) {
                    mapArr[i][j].c = "#FF00FF";
                } else if (arraysEqual(p.graphCell, [i, j])) {
                    mapArr[i][j].c = "#FF0000";
                } else {
                    mapArr[i][j].c = "#808080";
                }
                mapArr[i][j].drawFunc();
            }
        }
    }
}

/**
 * SECTION FOR CAMERA CONTROLS
 */

/**
 * @brief enables 3D lighting with a point light at the camera's position 
 * 
 * @param {int} cx camera x position
 * @param {int} cy camera y position
 * @param {int} cz camera z position
 */
function enableLight(cx, cy, cz) {
    var local = p.cam._getLocalAxes();
    var z = local.z;

    lightFalloff(1, 0, 0);
    spotLight(255, 255, 255, cx, cy, cz, 
        -z[0], 
        -z[1], 
        -z[2], 
        Math.PI, 100);
    spotLight(255, 255, 255, cx, cy, cz, 
        -z[0], 
        -z[1], 
        -z[2], 
        Math.PI / 5, 100);
    spotLight(255, 255, 255, cx, cy, cz, 
        -z[0], 
        -z[1], 
        -z[2], 
        Math.PI / 10, 100);
    spotLight(255, 255, 255, cx, cy, cz, 
        -z[0], 
        -z[1], 
        -z[2], 
        Math.PI / 10, 100);
    spotLight(255, 255, 255, cx, cy, cz, 
            -z[0], 
            -z[1], 
            -z[2], 
            Math.PI / 10, 100);
    lightFalloff(4, 0.01, 0);
    pointLight(255, 255, 255, cx, cy, cz);

    ambientMaterial(255, 255, 255);
}

/**
 * @brief toggles between fullscreen and windowed, changing the resolution
 */
function enterFullscreen() {
    fullscreen(!fs);
    if (!fs) {
        // reduced resolution to avoid scroll wheels
        resizeCanvas(1915, 1075);
    } else {
        resizeCanvas(600, 600);
    }
    fs = !fs;
}

/**
 * @brief used to call enterFullscreen()
 */
function keyPressed() {
    if (key == "f") enterFullscreen();
    if (key == "g") walking = !walking;
    if (key == "e") lightsOn = !lightsOn;
}

/**
 * @brief used to end sprinting
 */
function keyReleased() {
    if (keyCode == SHIFT) {
        p.dq = 15;
    }
    return false;
}

function mousePressed() {
    if (!locked) {
        requestPointerLock();
        locked = !locked;
    } else {
        exitPointerLock();
        locked = !locked;
    }
}

/**
 * @brief used to calculate dot product between two vectors
 * 
 * @param {int[]} A vector
 * @param {int[]} B vector
 */
function dot(A, B){
    if (A.length == B.length) {
        var r = 0;
        for (var i = 0; i < A.length; i++) {
            r += (A[i] * B[i]);
        }
        return r;
    }

    return -1;
}

/**
 * @brief calculates the cross product between two vectors
 * 
 * @param {int[]} A vector
 * @param {int[]} B vector
 */
function cross(A, B) {
    if (A.length == B.length) {
        var r = new Array();

        r[0] = A[1] * B[2] - A[2] * B[1];
        r[1] = A[2] * B[0] - A[0] * B[2];
        r[2] = A[0] * B[1] - A[1] * B[0];

        return r;
    }

    return -1;
}

/**
 * @brief calculates norm (length) of a vector
 * 
 * @param {int[]} A vector
 */
function normalize(A) {
    var r = 0;
    for (var i = 0; i < A.length; i++) {
        r += Math.pow(A[i], 2);
    }
    return Math.sqrt(r);
}

/**
 * PLAYER OBJECT SECTION 
 */

/**
 * @brief makes a player object with several useful parameters
 * 
 * @param {p5.Camera} cam object used for looking 
 * @param {int} dq movement rate (dq is notation used in robotics)
 * @param {int} lookRate surprisingly unused, i don't think i need this
 */
function makePlayer(cam, dq, lookRate) {
    p = {cam: cam, dq: dq, lr: lookRate, xyz: new Array(), panTilt: new Array(),
        camX: 0, camY: 0, camZ: 0, graphCell: new Array(), collision: true,
        keys: new Array(), movementPath: new Array(), mvmtVec: [0, 0, 0],
        samples: 100, width: dq, depth: dq, inventory: new Array(), won: false,
        posUpdate: getPlayerPos, angleUpdate: getPlayerPanTilt, 
        cellUpdate: playerGetCell, setCell: setPosFromCell,
        move: playerMove, look: playerLook, update: updatePlayer};
    return p;
}

/**
 * @brief gets 3d position of player and returns an array of these points
 */
function getPlayerPos() {
    return [this.cam.eyeX, this.cam.eyeY, this.cam.eyeZ];
}

/**
 * @brief returns pan and tilt angle of camera for use in other calculations
 */
function getPlayerPanTilt() {
    var local = this.cam._getLocalAxes();
    var x = local.x;
    var z = local.z;
    let xUnit = [1, 0, 0];
    let yUnit = [0, 1, 0];
    let rotatedUnit = cross(x, [0, -1, 0]);

    let xCross = cross(x, xUnit);
    let zCross = cross([-z[0], -z[1], -z[2]], rotatedUnit);
    
    panAngle = acos(dot(x, xUnit));
    if (dot(yUnit, xCross) < 0) panAngle = -panAngle;

    tiltAngle = acos(dot([-z[0], -z[1], -z[2]], rotatedUnit));
    if (dot([-x[0], -x[1], -x[2]], zCross) < 0) tiltAngle = -tiltAngle;

    return [panAngle, tiltAngle];
}

 /**
 * @brief handles player movement
 * 
 * Implemented in this way to allow for use of multiple keys at a time. This is 
 * called in draw() to update camera position on every redraw.
 */
function playerMove() {
    if (keyIsDown(87)) { // w
        this.keys[0] = 1;
    } else this.keys[0] = 0;
    if (keyIsDown(65)) { // a
        this.keys[3] = 1;
    } else this.keys[3] = 0;
    if (keyIsDown(83)) { // s
        this.keys[2] = 1;
    } else this.keys[2] = 0;
    if (keyIsDown(68)) { // d
        this.keys[1] = 1;
    } else this.keys[1] = 0;
    if (keyIsDown(16)) this.dq = constrain(this.dq+0.5, 0, 30); // shift

    this.cam.move(this.mvmtVec[0], this.mvmtVec[1], this.mvmtVec[2]);
}

/**
 * @brief rotates camera based on mouse movement
 * 
 * Tilts and pans the camera, uses camera axes to calculate look angles for the
 * purpose of bounding tilts.
 */
function playerLook() {
    panAngle = this.panTilt[0];
    tiltAngle = this.panTilt[1];

    vertBound = 80;

    this.cam.pan(radians(-movedX/this.lr));
    if (tiltAngle < radians(vertBound) & tiltAngle > radians(-vertBound)) {
        this.cam.tilt(radians(movedY/this.lr));
    } else if (tiltAngle > radians(vertBound) & movedY < 0) {
        this.cam.tilt(radians(movedY/this.lr));
    } else if (tiltAngle < radians(-vertBound) & movedY > 0) {
        this.cam.tilt(radians(movedY/this.lr));
    }
}

/**
 * @brief converts player position to a cell in the graph array
 */
function playerGetCell() {
    xyz = this.posUpdate();
    x = xyz[0];
    z = xyz[2];

    x += roomSize / 2;
    z += roomSize / 4;

    return [floor(z / roomSize), floor(x / roomSize)];
}

/**
 * @brief sets the player position to a specific graph cell
 * 
 * @param {int[]} coords graph cell (x, y)
 */
function setPosFromCell(coords) {
    this.cam.setPosition(coords[1] * roomSize, this.camY, coords[0] * roomSize);
}

/**
 * @brief updates all player parameters
 */
function updatePlayer() {
    this.xyz = this.posUpdate();
    this.panTilt = this.angleUpdate();

    this.camX = this.xyz[0];
    this.camY = walking ? 0 : this.xyz[1];
    this.camZ = this.xyz[2];

    this.cam.setPosition(this.camX, this.camY, this.camZ);

    this.mvmtVec = getMovementVector(this);
    this.graphCell = this.cellUpdate();
    this.move();
    this.look();
}

// CANCELLED: collision detection

/**
 * IDEA: get four walls from around player, possibly adjacent rooms
 * check if player movement vector collides with or passes through a wall plane
 * if it does, truncate the vector at the wall's edge
 * 
 * -----|---> turns into --->|
 */

 /**
  * @brief gets player's movement vector in xyz
  * 
  * @param {object} player 
  */
function getMovementVector(player) {
    var vector = [0, 0, 0];
    if (player.collision) {
        for (var i = 0; i < player.keys.length; i++) {
            switch(i) {
                case 0:
                    if (player.keys[i] == 1) vector[2] -= player.dq;
                    break;
                case 1:
                    if (player.keys[i] == 1) vector[0] += player.dq;
                    break;
                case 2:
                    if (player.keys[i] == 1) vector[2] += player.dq;
                    break;
                case 3:
                    if (player.keys[i] == 1) vector[0] -= player.dq;
                    break;
            }
        }
        return vector;
    }
    // implies collision is off because i'm not going to account for y changes
    // in noclip mode where collision doesn't matter
    // though i might in the future because it's not hard to add
    // the issue is that there's a *small* chance it'll slow the game down
    return []; 
}

/**
 * @brief converts a movement vector plus player position to world coordinates
 * 
 * Based on math given x and z axes. Movement vector forward composed of 
 * dz*cos(a) - dx*sin(a) and side vector composed to dz*sin(z) + dx*cos(a).
 * No need for y movement because this game has no stairs or ramps.
 * 
 * @param {int[]} movementVec assumed to include x, y, and z, uses only x and z
 * @param {int} angle assumed to be panAngle
 */
function vectorToWorldCoords(playerPos, movementVec, angle, samples) {
    let pointsOverTime = new Array();
    var x = p.cam.eyeX;
    var z = p.cam.eyeZ;
    for (var i = 0; i <= samples; i++) {
        var dz = (i / samples) * (movementVec[2]);
        var dx = (i / samples) * (movementVec[0]);
        pointsOverTime[i] = [z + (dz * cos(radians(angle))) + (dx * 
            sin(radians(angle))), x + (dx * cos(radians(angle))) - (dz * 
            sin(radians(angle)))];
    }

    return pointsOverTime;
}

/**
 * @brief locates point where player intersects wall, if they do
 * 
 * @param {int[]} playerPoints 
 * @param {int[][]} boundingBox 
 */
function findIntersect(playerPoints, boundingBox) {
    for (var i = 0; i < playerPoints.length; i++) {
        if (playerPoints[i][1] >= boundingBox[0][0] - 100 &
            playerPoints[i][1] <= boundingBox[1][0] + 100 &&
            playerPoints[i][0] >= boundingBox[0][1] - 100 &&
            playerPoints[i][0] <= boundingBox[1][1] + 100) {
                return playerPoints[i];
        }
    }
    return -1;
}

/**
 * @brief partially working collision function
 * 
 * @param {object} player 
 * @param {object} wall 
 */
function checkCollisionWithWall(player, wall) {
    let mvmtVec = getMovementVector(player);
    let points = vectorToWorldCoords(player.xyz, mvmtVec, 
        player.panTilt[0], player.samples);
    var boundaryPoint = findIntersect(points, wall.bounds);
    if (boundaryPoint != -1) {
        player.cam.setPosition(boundaryPoint[1], player.xyz[1], 
            boundaryPoint[0]);
    }
}

/**
 * @brief is supposed to handle collisions by limiting x or z movement
 * 
 * @param {int[]} movementVec 
 * @param {int} angle 
 * @param {int} samples 
 * @param {int[][]} boundingBox 
 */
function handleCollision(movementVec, angle, samples, boundingBox) {
    let vel = new Array();
    var x = p.cam.eyeX;
    var z = p.cam.eyeZ;
    for (var i = 0; i <= samples; i++) {
        var dz = (i / samples) * (movementVec[2]);
        var dx = (i / samples) * (movementVec[0]);

        xOnly = [z, x + (dx * cos(radians(angle))) - (dz * 
            sin(radians(angle)))];
        yOnly = [z + (dz * cos(radians(angle))) + (dx * 
            sin(radians(angle))), x];


        var xCh = checkCollision(xOnly, boundingBox);
        if (!xCh) {
            vel[0] = dx;
        }

        var yCh = checkCollision(yOnly, boundingBox);
        if (!yCh) {
            vel[2] = dz;
        }

        vel[1] = 0;
    }

    return vel;
}

/**
 * @brief checks if point is within a bounding box
 * 
 * @param {int[]} playerPoints 
 * @param {int[][]} boundingBox 
 */
function checkCollision(playerPoints, boundingBox) {
    if (playerPoints[1] >= boundingBox[0][0] - 100 &
        playerPoints[1] <= boundingBox[1][0] + 100 &&
        playerPoints[0] >= boundingBox[0][1] - 100 &&
        playerPoints[0] <= boundingBox[1][1] + 100) {
            return true;
    }
    return false;
}

/**
 * @brief draws a rotating diamond to represent items
 * 
 * @param {int[]} pos 
 * @param {int} size 
 * @param {p5.Color or p5.Image} texture 
 */
function drawRotatingDiamond(pos, size, tex) {
    push();
    if (typeof tex == "string") {
        fill(tex);
    } else {
        texture(tex);
    }
    translate(pos[0], pos[1] + (100 * sin(radians(ctr))), pos[2]);
    rotateY(radians(ctr));
    cone(size, size, 5);
    translate(0, -size, 0);
    cone(size, -size, 5);
    pop();
}
// TODO: make cube disappear if player shows up, add to inventory
// TODO: make win state