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

Leave a Reply