> show canvas only <


/* built with Studio Sketchpad: 
 *   https://sketchpad.cc
 * 
 * observe the evolution of this sketch: 
 *   https://studio.sketchpad.cc/sp/pad/view/ro.KbFetYSpHGL/rev.38348
 * 
 * authors: 
 *   Richard Wen
 *   
 *   

 * license (unless otherwise specified): 
 *   creative commons attribution-share alike 3.0 license.
 *   https://creativecommons.org/licenses/by-sa/3.0/ 
 */ 



// The goal is to produce a top-down survival game
// Please don't copy this; I've been working on it for a long time
// This sketch was created by Richard Wen
/*
    Notes for myself:
    - Manually defined angles will be in radians
        - Use the constand PI to define the angles
        - Angles received from atan() functions will already be in radians, so there is no need to convert them
    - The debug UI can be opened by setting the debug state to true
    - To test functions like in an interpreter, set the first parameter of testing() to true
        - Set the second parameter to true if the program should stop after testing
    - To create a new item:
        - Define a sprite [PImage stickImg]
        - Load the sprite [loadImage(".../stick.png")]
        - Insert the item in findImage [imageOut = stickImg]
        - Put the item in any specialized locations (recipes/other uses)
    - To create a new structure:
        - Create a new Building subclass [class Tent extends Building]
            - Call the super()
            - Define buildingWidth, buildingHeight, and shape
            - Define buildTime (amount of time spent in construction)
            - Define buildCost [buildCost = new BuildCost({}, {})]
        - Create a new ArrayList to hold the structure [ArrayList<Building> tents = new ArrayList<Building>()]
        - Put the stucture in the getBuildingArray lookup [return tents]
        - Define a new structure instance in player.constuction.place() [tents.add(new Tent())]
        - player.build.checkBuildClick()
            - Build the structure if it needs building
            - Use the structure if you can
            - Exit the structure if it is being used
        - Display the structure in draw() [tents.get(i).show()]
*/

/* @pjs preload="/static/uploaded_resources/p.27803/null.png"; */
PImage nullImg;
PImage backpackImg;
PImage hammerImg;
PImage gloveOpenImg;
PImage gloveClosedImg;

PImage stickImg;
PImage rocksImg;

PImage symbolToolLeft;
PImage symbolToolRight;
PImage symbolHead;
PImage symbolHandLeft;
PImage symbolHandRight;
PImage symbolChest;
PImage symbolFootLeft;
PImage symbolFootRight;
PImage symbolLegs;

void loadImages() {
    nullImg = loadImage("/static/uploaded_resources/p.27803/null.png");
    backpackImg = loadImage("/static/uploaded_resources/p.27803/backpack.png");
    hammerImg = loadImage("/static/uploaded_resources/p.27803/hammer.png");
    gloveOpenImg = loadImage("/static/uploaded_resources/p.27803/gloveOpen.png");
    gloveClosedImg = loadImage("/static/uploaded_resources/p.27803/gloveClosed.png");
    
    stickImg = loadImage("/static/uploaded_resources/p.27803/stick.png");
    rocksImg = loadImage("/static/uploaded_resources/p.27803/rocks.png");
}

void testing(boolean isActive, boolean stopProgram) {
    if (isActive) {
        // Insert everything to be tested here
        
        trees.add(new Tree(100, 100, 0));
        
        // Everything to be tested ends here
        
        if (stopProgram){
            exit();
        }
    }
}

float debugFloat1 = 0;
float debugFloat2 = 0;
float debugFloat3 = 0;
String debugString1 = "";
String debugString2 = "";
boolean debugMenu = true; // Set true to enable debug UI; Set false to disable debug UI
int debugState = 0; // The active page of the debug menu (is 0 when the menu is hidden)
void debug(boolean isActive) {
    if (isActive) {
        fill(0);
        textFont(defaultFont);
        textAlign(LEFT, BOTTOM);
        
        switch (debugState) {
        case 0:
            break;
        case 1:
            text("Positional Debug:", 50, 40); // Increase x by 300, increase y by 20
            text("Player Location: " + str(player.x) + ", " + str(player.y), 50, 60);
            text("Player in Cam: " + str(player.xInCam) + ", " + str(player.yInCam), 50, 80);
            text("Camera Location: " + str(width / 2 + player.xInCam - player.x) + ", " + str(height / 2 + player.yInCam - player.y), 50, 100);
            text(str(wDown), 350, 60);
            text(str(aDown), 300, 80);
            text(str(sDown), 350, 80);
            text(str(dDown), 400, 80);
            text(str(ground.tileX) + ", " + str(ground.tileY), 350, 100);
            text(str(mouseX), 300, 120);
            text(str(mouseY), 350, 120);
            break;
        case 2:
            text("Technical Debug:", 50, 40);
            text("gameTime = " + str(gameTime), 50, 60);
            text("isActive = " + str(actionMenu.isActive), 50, 80);
            text("subMenu = " + actionMenu.subMenu, 50, 100);
            text("Energy: " + str(player.energy), 50, 120);
            text("Health: " + str(player.health), 50, 140);
            text("restClock = " + str(player.restClock), 50, 160);
            text("replenishing = " + str(player.replenishing), 50, 180);
            text(str(debugFloat1), 350, 60);
            text(str(debugFloat2), 350, 80);
            text(str(debugFloat3), 350, 100);
            text(debugString1, 350, 120);
            text(debugString2, 350, 140);
            break;
        case 3:
            text("Structure Debug:", 50, 40);
            text(player.construction.building, 50, 60);
            text("isBuilding = " + str(player.build.isBuilding), 50, 80);
            text("buildingType = " + player.build.buildingType, 50, 100);
            text("buildingKey = " + str(player.build.buildingKey), 50, 120);
            text("isUsing = " + str(player.build.isUsing), 250, 80);
            text("usingType = " + player.build.usingType, 250, 100);
            text("usingKey = " + str(player.build.usingKey), 250, 120);
            text("itemLifted = " + str(actionMenu.inventoryMaster.itemLifted), 50, 160);
            text("liftedItem = " + actionMenu.inventoryMaster.liftedItem, 50, 180);
            text("liftedCount = " + str(actionMenu.inventoryMaster.liftedCount), 50, 200);
            break;
        default:
            debugState = 0;
            break;
        }
    }
}



PImage findImage(String name) {
    PImage imageOut;
    switch (name) {
    case "Sticks":
        imageOut = stickImg;
        break;
    case "Rocks":
        imageOut = rocksImg;
        break;
    default:
        imageOut = nullImg;
        break;
    }
    return imageOut;
}

int preventDiv0(float f) { // When there is a div by 0 error possible, add preventDiv0(f) to the denominator, where f is the numerator
    if (f == 0) {
        return 100; // This function only works if the denominator can not be the negative of this value
        // Currently: 0 <= possible denominators
    }
    return 0;
}

// Make a function that passes an integer and returns an array of buildings
int buildingTypeCount = 2; // The number of valid building types
ArrayList<Building> getBuildingArray(String buildingArray) {
    switch (buildingArray) {
    case "0":
    case "Tent":
        return tents;
    case "1":
    case "Campfire":
        return campfires;
    default:
        return new ArrayList<Building>();
    }
}

void drawCursor() {
    if (actionMenu.isActive) {
        if (actionMenu.inventoryMaster.itemLifted || mousePressed) {
            noCursor();
            imageMode(CENTER);
            image(gloveClosedImg, mouseX, mouseY + 2, 30, 30);
        }
        else {
            noCursor();
            imageMode(CENTER);
            image(gloveOpenImg, mouseX, mouseY, -30, 30);
        }
    }
    else {
        for (int i = 0; i < buildingTypeCount; i++) {
            for (int j = 0; j < getBuildingArray(str(i)).size(); j++) {
                if (getBuildingArray(str(i)).get(j).hovered() && getBuildingArray(str(i)).get(j).buildProgress < getBuildingArray(str(i)).get(j).buildTime) {
                    noCursor();
                    imageMode(CENTER);
                    image(hammerImg, mouseX, mouseY, 40, 40);
                    return;
                }
            }
        }
        cursor(CROSS);
    }
}



class Ground {
    // Tiles
    int tileWidth = 50;
    int tileHeight = 50;
    int tileX = 0;
    int tileY = 0;
    
    void show(float centerX, float centerY) {
        rectMode(CORNER);
        for (tileX = int((centerX - width / 2 - tileWidth) / tileWidth); tileX <= (centerX + width / 2) / tileWidth; tileX++) {
            for (tileY = int((centerY - height / 2 - tileHeight) / tileHeight); tileY <= (centerY + height / 2) / tileHeight; tileY++) {
                if (abs(tileX + tileY) % 2 == 0) {
                    fill(255);
                }
                else {
                    fill(100);
                }
                rect(tileX * tileWidth, tileY * tileHeight, tileWidth, tileHeight);
                if (debugMenu) {
                    fill(0);
                    textAlign(CENTER, CENTER);
                    textFont(defaultFont);
                    if (debugState == 1) {
                        text(str(tileX) + ", " + str(tileY), tileX * tileWidth + tileWidth / 2, tileY * tileHeight + tileHeight / 2);
                    }
                    else if (debugState == 2) {
                        text(str(tileX + tileY), tileX * tileWidth + tileWidth / 2, tileY * tileHeight + tileHeight / 2);
                    }
                }
            }
        }
    }
}
Ground ground;



// PLAYER

class Player {
    class Movement { // Controls the player's movement
        boolean isEnabled = true;
        
        float playerMovementX = 0; // 1 if moving right, 0 if no x movement, -1 if moving left
        float playerMovementY = 0;
        float playerVelX = 0; // Function of playerMovementX and the angle of motion
        float playerVelY = 0;
        float playerSpeed = 100;
        
        float xInCamLimit = 0.25; // Value between 0 and 1 representing the fraction of the screen width available for player movement relative to camera center
        float yInCamLimit = 0.25; 
        
        void shift(float xIn, float yIn) { // Move the player and calculate camera movement
            x += xIn;
            y += yIn;
            
            xInCam += xIn;
            yInCam += yIn;
            if (abs(xInCam) > 0.5 * xInCamLimit * width) {
                xInCam *= 0.5 * xInCamLimit * width / abs(xInCam);
            }
            if (abs(yInCam) > 0.5 * yInCamLimit * height) {
                yInCam *= 0.5 * yInCamLimit * height / abs(yInCam);
            }
            
            dir = atan(yIn / xIn);
            if (xIn < 0) {
                dir += PI;
            }
        }
        
        void walk() { // Calculate movement for one frame
            playerMovementX = 0;
            if (aDown) {
                playerMovementX--;
            }
            if (dDown) {
                playerMovementX++;
            }
            playerMovementY = 0;
            if (wDown) {
                playerMovementY--;
            }
            if (sDown) {
                playerMovementY++;
            }
            
            if (shiftDown && !replenishing) { // Running
                playerSpeed = 160;
            }
            else {
                playerSpeed = 100;
                if (energy < 20) {
                    replenishing = true;
                }
            }
            
            playerVelX = playerSpeed * playerMovementX / (pow(pow(playerMovementX, 2) + pow(playerMovementY, 2), 0.5) + preventDiv0(playerMovementX));
            playerVelY = playerSpeed * playerMovementY / (pow(pow(playerMovementX, 2) + pow(playerMovementY, 2), 0.5) + preventDiv0(playerMovementY));
            
            if (playerVelX != 0 || playerVelY != 0) {
                shift(playerVelX / frameRate, playerVelY / frameRate);
            }
        }
    }
    Movement movement = new Movement();
    
    class Construction { // Controls placement of a building
        String building = "null"; // Name of the building being placed (null if no building is being placed)
        float placeX;
        float placeY;
        float placeR;
        float placeAngle;
        float placementRange = 100; // The max range that the building can be placed at
        
        void calcPlacement() { // Calculate the placement location of the building based on the mouse position
            placeX = mouseX - (xInCam + width / 2);
            placeY = mouseY - (yInCam + height / 2);
            placeR = pow(pow(placeX, 2) + pow(placeY, 2), 0.5); // The distance between the mouse and the player
            if (placeR > placementRange) { // Check if the mouse is out of range of the player
                placeX *= placementRange / placeR;
                placeY *= placementRange / placeR;
            }
            
            placeAngle = atan(placeY / placeX) + radians(90);
            if (placeX < 0) {
                placeAngle += PI;
            }
        }
        
        void place() {
            calcPlacement();
            
            switch (building) {
            case "Tent":
                tents.add(new Tent(x + placeX, y + placeY, placeAngle));
                break;
            case "Campfire":
                campfires.add(new Campfire(x + placeX, y + placeY, placeAngle));
                break;
            default:
                break;
            }
            
            building = "null";
        }
        
        void show() {
            calcPlacement();
            
            pushMatrix();
            translate(x + placeX, y + placeY);
            rotate(placeAngle); // Rotate the building to face the player (the -90 is because the orientation of the building should be perpendicular to the line between it and the player)
            scale(-abs(placeX) / placeX, 1); // Flip the image if atan(placeY / placeX) was in the 1st or 2nd quadrant (this is because of the symmetrical properties of arctangents)
            
            fill(0, 0, 0, 75);
            noStroke();
            rectMode(CENTER);
            switch (building) {
            case "null":
                break;
            case "Tent":
                rect(0, 0, 100, 80);
                break;
            case "Campfire":
                ellipse(0, 0, 60, 60);
                break;
            default:
                break;
            }
            stroke(0);
            popMatrix();
        }
    }
    Construction construction = new Construction();
    
    class Build { // Controls using and building a structure
        boolean isUsing = false;
        String usingType = "null";
        int usingKey = 0;
        
        boolean isBuilding = false;
        String buildingType = "null";
        int buildingKey = 0;
        
        float buildingRange = 100;
        float xOfBuilding; // Just to tell the draw() function where to place the range circle
        float yOfBuilding;
        boolean inBuildingRange = false;
        Build() {
        }
        
        void checkBuildClick() { // Called when the mouse is pressed; Checks if the player clicked on a structure; Initiates using or building a structure
            if (!isUsing) {
                for (int i = 0; i < tents.size(); i++) {
                    if (tents.get(i).hovered()) { // If the player clicked on the structure
                        if (tents.get(i).buildProgress < tents.get(i).buildTime) { // If the structure needs building
                            isBuilding = true;
                            buildingType = "Tent";
                            buildingKey = i;
                            return;
                        }
                        else { // If the structure can be used
                            if (pow(pow(tents.get(i).x - x, 2) + pow(tents.get(i).y - y, 2), 0.5) <= buildingRange) {
                                isUsing = true;
                                usingType = "Tent";
                                usingKey = i;
                                
                                movement.shift(tents.get(usingKey).x - x, tents.get(usingKey).y - y);
                                movement.isEnabled = false;
                                isVisible = false;
                                return;
                            }
                        }
                    }
                }
                for (int i = 0; i < campfires.size(); i++) {
                    if (campfires.get(i).hovered()) { // If the player clicked on the structure
                        if (campfires.get(i).buildProgress < campfires.get(i).buildTime) { // If the structure needs building
                            isBuilding = true;
                            buildingType = "Campfire";
                            buildingKey = i;
                            return;
                        }
                        else { // If the structure can be used
                            // campfires.get(i).use();
                        }
                    }
                }
            }
            else {
                isUsing = false;
                switch (usingType) {
                case "Tent":
                    movement.isEnabled = true;
                    isVisible = true;
                    movement.shift(tents.get(usingKey).x + (tents.get(usingKey).buildingWidth / 2 + 30) * cos(tents.get(usingKey).angle) - x, tents.get(usingKey).y + (tents.get(usingKey).buildingWidth / 2 + 30) * sin(tents.get(usingKey).angle) - y);
                    break;
                default:
                    break;
                }
            }
        }
        
        void checkBuildHold() { // Called every frame; Checks if the player is currently building a structure
            xOfBuilding = getBuildingArray(buildingType).get(buildingKey).x;
            yOfBuilding = getBuildingArray(buildingType).get(buildingKey).y;
            buildingRange = 100; // At this point, all buildings have the same building range; If this changes, add a switch statement here that checks BuildingType
            if (getBuildingArray(buildingType).get(buildingKey).buildProgress < getBuildingArray(buildingType).get(buildingKey).buildTime) { // If the structure still needs building
                if (pow(pow(getBuildingArray(buildingType).get(buildingKey).x - x, 2) + pow(getBuildingArray(buildingType).get(buildingKey).y - y, 2), 0.5) <= buildingRange) { // If the structure is within range
                    inBuildingRange = true;
                    if (getBuildingArray(buildingType).get(buildingKey).hovered()) {
                        getBuildingArray(buildingType).get(buildingKey).buildProgress += 1 / frameRate; // Make building progress
                    }
                }
            }
            else {
                getBuildingArray(buildingType).get(buildingKey).buildProgress = getBuildingArray(buildingType).get(buildingKey).buildTime;
                isBuilding = false;
            }
        }
    }
    Build build = new Build();
    
    class Harvest { // Controls harvesting naturals
        boolean isHarvesting = false;
        String naturalType = "null";
        int naturalKey = 0;
        
        float harvestingRange = 100;
        float xOfNatural;
        float yOfNatural;
        boolean inHarvestingRange = false;
        
        void checkHarvestClick() {
            
        }
    }
    
    float x;
    float y;
    float dir; // Angle of player's viewing direction from the +x axis
    float xInCam; // x distance from center of camera
    float yInCam; // y distance from center of camera
    boolean isVisible = true;
    Player() {
        x = 0;
        y = 0;
        dir = 0;
        xInCam = 0;
        yInCam = 0;
    }
    
    float energy = 100;
    float health = 100;
    boolean replenishing = false; // When true, energy-using actions are no longer possible; becomes set to false when reaching sufficient energy
    float restClock = 0; // Counts how long the player has been resting (the longer the player has been resting, the more energy they gain per tick)
    
    void tick() {
        if (movement.isEnabled) {
            movement.walk();
        }
        
        energy += 3 / frameRate;
        health += 0.75 / frameRate;
        
        if (build.isUsing) {
            switch (build.usingType) {
            case "Tent":
                health += 2.5 / frameRate;
                energy += 6 / frameRate;
                break;
            default:
                break;
            }
        }
        
        if (!(movement.playerMovementX == 0 && movement.playerMovementY == 0) && player.movement.isEnabled) { // If walking
            energy -= 2.5 / frameRate;
            energy += (1.5 - 15 / (restClock + 10)) / frameRate; // After 5 seconds, energy regen increased by 0.5 en/sec; approach +1.5 en/sec; total +2 en/sec
            restClock += 0.75 / frameRate;
            if (shiftDown && !replenishing) { // If running
                restClock = 0;
                energy -= 5.5 / frameRate;
            }
        }
        else {
            energy += (3 - 12 / (restClock + 4)) / frameRate; // After 2 seconds, energy regen increased by 1 en/sec; approach +3 en/sec; total +6 en/sec
            restClock += 1 / frameRate;
            if (energy < 20) {
                replenishing = true;
            }
        }
        if (replenishing) {
            energy -= 0.5 / frameRate; // Decreased energy regen if fatigued (under 20% energy)
        }
        
        if (energy < 0) {
            energy = 0;
            replenishing = true;
        }
        else if (energy > 100) {
            energy = 100;
        }
        else if (energy > 20) {
            replenishing = false;
        }
        if (health < 0) {
            health = 0;
            // death
        }
        else if (health > 100) {
            health = 100;
        }
    }
    
    void show() {
        if (isVisible) {
            pushMatrix();
            translate(x, y);
            rotate(dir);
            fill(0);
            ellipse(0, 0, 40, 40);
            fill(255, 0, 0);
            ellipse(20, 0, 5, 5);
            popMatrix();
        }
    }
    
    void showStatusBars() {
        rectMode(CORNER);
        fill(215, 225, 225);
        rect(75, 35, 200, 20);
        rect(75, 55, 200, 10);
        fill(220, 30, 100);
        rect(75, 35, 2 * health, 20); // Health bar
        fill(70, 230, 240);
        rect(75, 55, 2 * energy, 10); // Energy bar
        fill(70, 180, 240);
        if (energy > 20) { // Emergency energy bar
            rect(75, 55, 40, 10);
        }
        else {
            rect(75, 55, 2 * energy, 10);
        }
        fill(215, 200, 190);
        ellipse(50, 50, 60, 60);
    }
}
Player player;



// BUILDINGS

class Building { // Superclass for all structures
    float buildingWidth;
    float buildingHeight;
    float x;
    float y;
    float angle;
    float sinAngle;
    float cosAngle;
    String shape; // rect, ellipse
    float buildProgress = 0; // How long the player has been building the structure
    float buildTime; // How long the player needs to build the structure for it to be complete
    String[] buildCostItems;
    int[] buildCostCounts;
    boolean inUse = false;
    Building(float xIn, float yIn, float angleIn) { // Do not construct a building; Construct the subtype
        x = xIn;
        y = yIn;
        angle = angleIn;
        sinAngle = sin(angle);
        cosAngle = cos(angle);
    }
    
    boolean hovered() {
        float xToMouse = camOriginX + mouseX - x;
        float yToMouse = camOriginY + mouseY - y;
        float u = xToMouse * cosAngle + yToMouse * sinAngle;
        float v = yToMouse * cosAngle - xToMouse * sinAngle;
        
        switch (shape) {
        case "rect":
            if (abs(u) <= buildingWidth / 2 && abs(v) <= buildingHeight / 2) {
                return true;
            }
            else {
                return false;
            }
        case "ellipse":
            if (pow(pow(u / (buildingWidth / 2), 2) + pow(v / (buildingHeight / 2), 2), 0.5) <= 1) {
                return true;
            }
            else {
                return false;
            }
        default:
            return false;
        }
    }
    
    void appearance() {}; // By default has nothing; Redefine in the subclass
    
    void show() {
        pushMatrix();
        translate(x, y);
        rotate(angle);
        
        appearance();
        
        popMatrix();
        
        if (buildProgress < buildTime) {
            rectMode(CORNER);
            fill(240, 240, 240, 80);
            rect(x - 40, y - 10, 80, 20);
            rectMode(CORNER);
            fill(210, 255, 255);
            rect(x - 40, y - 10, 80 * buildProgress / buildTime, 20);
        }
        
        /* fill(240, 240, 240, 80);
        ellipse(x, y, 50, 50);
        fill(210, 255, 255);
        arc(x, y, 50, 50, radians(-90), radians(-90) + radians(360) * buildProgress / buildTime, PIE); */ // Unfortunately, Studio Sketchpad does not support the arc() function
        
        if (debugMenu && debugState == 3) {
            fill(0);
            textAlign(CENTER, CENTER);
            text(str(int(buildProgress * 100) / 100) + "/" + str(buildTime), x, y);
        }
    }
}

class Tent extends Building {
    Tent(float xIn, float yIn, float angleIn) {
        super(xIn, yIn, angleIn);
        buildingWidth = 100;
        buildingHeight = 80;
        shape = "rect";
        buildTime = 12;
        String[] buildCostItems = {"Sticks", "Rocks", "leather", "rope"};
        int[] buildCostCounts = {6, 4, 3, 2};
    }
    
    void appearance() {
        if (player.build.isUsing && player.build.usingType == "Tent" && player.build.usingKey == tents.indexOf(this)) {
            inUse = true;
        }
        else {
            inUse = false;
        }
        
        noStroke();
        if (inUse) {
            fill(255, 200 + 15 * sin(gameTime / 100), 0, 5);
            for (int i = 2; i < buildingHeight; i += 2) {
                ellipse(buildingWidth / 2, 0, i, i);
            }
        }
        else {
            fill(0, 0, 0, 80);
            triangle(buildingWidth / 2, buildingHeight / 2, buildingWidth / 2, -buildingHeight / 2, buildingWidth / 2 - 2, 0);
        }
        
        stroke(0);
        fill(80, 120, 70);
        if (buildProgress >= buildTime) {
            quad(-buildingWidth / 2, buildingHeight / 2, buildingWidth / 2, buildingHeight / 2, buildingWidth / 2 - 2, 0, -buildingWidth / 2 - 2, 0);
            quad(-buildingWidth / 2, -buildingHeight / 2, buildingWidth / 2, -buildingHeight / 2, buildingWidth / 2 - 2, 0, -buildingWidth / 2 - 2, 0);
            noStroke();
            fill(0, 0, 0, 80);
            triangle(buildingWidth / 2 - 2, 0, buildingWidth / 2 - 22, buildingHeight / 2 - 12, buildingWidth / 2, buildingHeight / 2);
            triangle(buildingWidth / 2 - 2, 0, buildingWidth / 2 - 22, -buildingHeight / 2 + 12, buildingWidth / 2, -buildingHeight / 2);
            stroke(0);
            fill(80, 120, 70);
            triangle(buildingWidth / 2 - 2, 0, buildingWidth / 2 - 20, buildingHeight / 2 - 10, buildingWidth / 2, buildingHeight / 2);
            triangle(buildingWidth / 2 - 2, 0, buildingWidth / 2 - 20, -buildingHeight / 2 + 10, buildingWidth / 2, -buildingHeight / 2);
        }
        else {
            quad(-buildingWidth / 2, buildingHeight / 2, buildingWidth / 2, buildingHeight / 2, buildingWidth / 2 + 2, 0, -buildingWidth / 2 - 2, 0);
            quad(-buildingWidth / 2, -buildingHeight / 2, buildingWidth / 2, -buildingHeight / 2, buildingWidth / 2 + 2, 0, -buildingWidth / 2 - 2, 0);
        }
    }
}
ArrayList<Building> tents = new ArrayList<Building>();

class Campfire extends Building {
    class Flame {
        float x;
        float y;
        color c;
        float size;
        float maxSize;
        float time;
        float maxTime;
        Flame() {
            regen();
        }
        
        void regen() {
            x = random(-buildingWidth / 5, buildingWidth / 5);
            y = random(-buildingHeight / 5, buildingHeight / 5);
            
            maxSize = random(buildingWidth / 6, buildingWidth / 2.5);
            maxTime = random(0.5, 2);
            
            time = 0;
            size = 0;
            
            c = color(random(230, 255), random(100, 220), random(10, 50));
        }
        
        void tick() {
            time += 1 / frameRate;
            size = maxSize - 2 * maxSize / maxTime * abs(time - maxTime / 2);
            
            if (size < 0) {
                regen();
            }
        }
        
        void show() {
            fill(c);
            ellipse(x, y, size, size);
        }
    }
    Flame[] flames = new Flame[12];
    
    Campfire(float xIn, float yIn, float angleIn) {
        super(xIn, yIn, angleIn);
        buildingWidth = 60;
        buildingHeight = 60;
        shape = "ellipse";
        buildTime = 8;
        String[] buildCostItems = {"Sticks", "Rocks"};
        int[] buildCostCounts = {8, 12};
        
        for (int i = 0; i < flames.length; i++) {
            flames[i] = new Flame();
        }
    }
    
    void appearance() {
        pushMatrix();
        rotate(radians(10));
        fill(190, 190, 200);
        for (int i = 0; i < 18; i++) {
            ellipse(0, buildingWidth / 2 - 4, 8, 8);
            rotate(radians(20));
        }
        popMatrix();
        
        pushMatrix();
        rectMode(CENTER);
        fill(110, 80, 50);
        for (int i = 0; i < 3; i++) {
            rect(0, 0, buildingWidth + 10, buildingWidth / 6);
            rotate(radians(120));
        }
        popMatrix();
        
        if (buildProgress >= buildTime) {
            noStroke();
            fill(255, 180, 0, 4);
            for (int i = 2; i < 2 * buildingWidth; i += 2) {
                ellipse(0, 0, i, i);
            }
            
            for (int i = 0; i < flames.length; i++) {
                flames[i].tick();
                flames[i].show();
            }
            stroke(0);
        }
    }
}
ArrayList<Building> campfires = new ArrayList<Building>();



// ENVIRONMENT

int naturalTypeCount = 1;
ArrayList<Natural> getNaturalArray(String naturalArray) {
    
}

class Natural { // Superclass for all natural structures
    float naturalWidth;
    float naturalHeight;
    float x;
    float y;
    float angle;
    float sinAngle;
    float cosAngle;
    String shape; // rect, ellipse
    float harvestProgress = 0; // How long the player has been building the structure
    float harvestTime; // How long the player needs to build the structure for it to be complete
    boolean isHarvestable = true;
    Natural(float xIn, float yIn, float angleIn) { // Do not construct a building; Construct the subtype
        x = xIn;
        y = yIn;
        angle = angleIn;
        sinAngle = sin(angle);
        cosAngle = cos(angle);
    }
    
    boolean hovered() {
        float xToMouse = camOriginX + mouseX - x;
        float yToMouse = camOriginY + mouseY - y;
        float u = xToMouse * cosAngle + yToMouse * sinAngle;
        float v = yToMouse * cosAngle - xToMouse * sinAngle;
        
        switch (shape) {
        case "rect":
            if (abs(u) <= naturalWidth / 2 && abs(v) <= naturalHeight / 2) {
                return true;
            }
            else {
                return false;
            }
        case "ellipse":
            if (pow(pow(u / (naturalWidth / 2), 2) + pow(v / (naturalHeight / 2), 2), 0.5) <= 1) {
                return true;
            }
            else {
                return false;
            }
        default:
            return false;
        }
    }
    
    void appearance() {}; // By default has nothing; Redefine in the subclass
    
    void show() {
        pushMatrix();
        translate(x, y);
        rotate(angle);
        
        appearance();
        
        popMatrix();
        
        if (harvestProgress > 0) {
            rectMode(CORNER);
            fill(240, 240, 240, 80);
            rect(x - 40, y - 10, 80, 20);
            rectMode(CORNER);
            fill(210, 255, 255);
            rect(x - 40, y - 10, 80 * harvestProgress / harvestTime, 20);
        }
        
        /* fill(240, 240, 240, 80);
        ellipse(x, y, 50, 50);
        fill(210, 255, 255);
        arc(x, y, 50, 50, radians(-90), radians(-90) + radians(360) * harvestProgress / harvestTime, PIE); */ // Unfortunately, Studio Sketchpad does not support the arc() function
        
        if (debugMenu && debugState == 3) {
            fill(0);
            textAlign(CENTER, CENTER);
            text(str(int(harvestProgress * 100) / 100) + "/" + str(harvestTime), x, y);
        }
    }
}

class Tree extends Natural {
    Tree(float xIn, float yIn, float angleIn) {
        super(xIn, yIn, angleIn);
        naturalWidth = 80;
        naturalHeight = 80;
        shape = "ellipse";
        harvestTime = 1;
    }
    
    void appearance() {
        fill(100, 255, 100);
        ellipse(0, 0, naturalWidth, naturalHeight);
    }
}
ArrayList<Natural> trees = new ArrayList<Natural>();



// ENTITIES

class DroppedItem {
    float x;
    float y;
    String itemName;
    int itemCount;
    DroppedItem(float xIn, float yIn, String itemNameIn, int itemCountIn) {
        x = xIn;
        y = yIn;
        itemName = itemNameIn;
        itemCount = itemCountIn;
        
        timeOff = random(2 * PI);
    }
    
    boolean collecting = false; // Set to true when the player steps over the item
    float collectionClock = 0;
    float collectionTime = 0.15; // The total collection time
    float startX;
    float startY;
    void collectTick() { // Activate if the item is being collected
        if (collecting) {
            collectionClock += 1 / frameRate;
            if (collectionClock > collectionTime) {
                if (actionMenu.inventoryMaster.canCollect(itemName)) {
                    actionMenu.inventoryMaster.collectItem(itemName, itemCount);
                    droppedItems.remove(droppedItems.indexOf(this));
                }
            }
            else {
                x = startX + (player.x - startX) * pow(collectionClock / collectionTime, 2);
                y = startY + (player.y - startY) * pow(collectionClock / collectionTime, 2);
            }
        }
        else if (pow(pow(player.x - x, 2) + pow(player.y - y, 2), 0.5) < 40) { // The collection distance is alterable
            collecting = true;
            collectionClock = 0;
            startX = x;
            startY = y;
        }
    }
    
    float timeOff; // Used to create a hovering effect
    void show() {
        imageMode(CENTER);
        image(findImage(itemName), x, y, 40 * (1 - collectionClock / collectionTime), 40 * (1 - collectionClock / collectionTime));
        // image(findImage(itemName), x + 2 * cos(gameTime / 50 + timeOff), y + 4 * sin(gameTime / 50 + timeOff), 40, 40); // Creates a hovering effect, but is slower
        
        if (debugMenu && debugState == 2) {
            textFont(defaultFont);
            textAlign(CENTER, CENTER);
            fill(0);
            text(itemName, x, y - 6);
            text(str(itemCount), x, y + 6);
        }
    }
}
ArrayList<DroppedItem> droppedItems = new ArrayList<DroppedItem>();



// UI
class Button {
    PFont buttonFont;
    float x;
    float y;
    float buttonWidth;
    float buttonHeight;
    String buttonText;
    boolean buttonPressed = false;
    Button(float xIn, float yIn, float buttonWidthIn, float buttonHeightIn, String buttonTextIn) {
        x = xIn;
        y = yIn;
        buttonWidth = buttonWidthIn;
        buttonHeight = buttonHeightIn;
        buttonText = buttonTextIn;
        
        buttonFont = createFont("Georgia", buttonHeight * 0.75);
        textFont(buttonFont);
        if (textWidth(buttonText) > buttonWidth * 0.9) {
            buttonFont = createFont("Georgia", buttonHeight * 0.75 * buttonWidth / textWidth(buttonText) * 0.9);
        }
    }
    
    boolean hovered() { // Return true if the cursor is on the button; return false otherwise
        if (mouseX > x && mouseX < x + buttonWidth && mouseY > y && mouseY < y + buttonHeight) {
            return true;
        }
        else {
            return false;
        }
    }
    
    void show() {
        if (buttonPressed) {
            fill(175);
        }
        else {
            fill(220);
        }
        rectMode(CORNER);
        rect(x, y, buttonWidth, buttonHeight);
        fill(0);
        textFont(buttonFont);
        textAlign(CENTER, CENTER);
        text(buttonText, x + buttonWidth / 2, y + buttonHeight / 2 - 3);
    }
}

class ItemSlot {
    PFont slotFont;
    float x;
    float y;
    float slotWidth;
    float slotHeight;
    String itemName = "null";
    int itemCount = 0;
    ItemSlot(float xIn, float yIn, float slotWidthIn, float slotHeightIn) {
        x = xIn;
        y = yIn;
        slotWidth = slotWidthIn;
        slotHeight = slotHeightIn;
        
        slotFont = createFont("Georgia", 15);
    }
    
    boolean hovered() { // Return true if the cursor is on the slot; return false otherwise
        if (mouseX > x && mouseX < x + slotWidth && mouseY > y && mouseY < y + slotHeight) {
            return true;
        }
        else {
            return false;
        }
    }
    
    void show() {
        rectMode(CORNER);
        fill(175);
        rect(x, y, slotWidth, slotHeight);
        if (hovered()) {
            fill(230, 230, 230, 95);
            rect(x, y, slotWidth, slotHeight);
        }
        
        float drawImageX = x + slotWidth / 2;
        float drawImageY = y + slotHeight / 2;
        if (itemCount != 0) {
            imageMode(CENTER);
            image(findImage(itemName), drawImageX, drawImageY - 2, slotWidth, slotHeight);
            
            textFont(slotFont);
            textAlign(RIGHT, BOTTOM);
            fill(0);
            text(str(itemCount), drawImageX + slotWidth / 2, drawImageY + slotHeight / 2);
        }
        
        if (debugMenu && debugState == 2) {
            textFont(defaultFont);
            textAlign(CENTER, CENTER);
            fill(0);
            text(itemName, drawImageX, drawImageY - 6);
            text(str(itemCount), drawImageX, drawImageY + 6);
        }
    }
}

class ActionMenu {
    boolean isActive = false;
    String subMenu = "items"; // items, craft, build,
    
    ItemSlot[] inventory = new ItemSlot[32];
    ItemSlot[] belt = new ItemSlot[4];
    ItemSlot[] equipment = new ItemSlot[9];
    
    class ButtonMaster { // Used to manage and navigate buttons in the submenus
        Button itemsTab = new Button(80, 300, 95, 40, "Items");
        Button craftTab = new Button(195, 300, 95, 40, "Craft");
        Button buildTab = new Button(310, 300, 95, 40, "Build");
        Button equipTab = new Button(425, 300, 95, 40, "Equip");
        
        Button[] itemsButtons = {
        };
        
        Button[] craftButtons = {
        };
        Button[] buildButtons = {
            new Button(80, 60, 100, 40, "Tent"),
            new Button(310, 60, 100, 40, "Campfire")
        };
        Button[] equipButtons = {
        };
        ButtonMaster() {
        }
        
        void checkClicksPress() { // See if the player clicked on any of the buttons
            if (itemsTab.hovered()) {
                subMenu = "items";
            }
            else if (craftTab.hovered()) {
                subMenu = "craft";
            }
            else if (buildTab.hovered()) {
                subMenu = "build";
            }
            else if (equipTab.hovered()) {
                subMenu = "equip";
            }
            else if (inventoryMaster.itemLifted) {
                switch (subMenu) {
                case "items":
                    for (int i = 0; i < inventory.length; i++) {
                        if (inventory[i].hovered()) {
                            inventoryMaster.placeItem(inventoryMaster.itemSlotArray("inventory"), i);
                            break;
                        }
                    }
                    break;
                case "craft":
                    break;
                case "build":
                    break;
                case "equip":
                    for (int i = 0; i < equipment.length; i++) {
                        if (equipment[i].hovered()) {
                            inventoryMaster.placeItem(inventoryMaster.itemSlotArray("equipment"), i);
                            break;
                        }
                    }
                    for (int i = 0; i < belt.length; i++) {
                        if (belt[i].hovered()) {
                            inventoryMaster.placeItem(inventoryMaster.itemSlotArray("belt"), i);
                            break;
                        }
                    }
                    break;
                default:
                    break;
                }
            }
            else {
                switch (subMenu) {
                case "items":
                    for (int i = 0; i < inventory.length; i++) { // Check if an item slot with an item in it was clicked
                        if (inventory[i].hovered()) {
                            inventoryMaster.liftItem(inventoryMaster.itemSlotArray("inventory"), i);
                            break;
                        }
                    }
                    break;
                case "craft":
                    break;
                case "build":
                    for (int i = 0; i < buildButtons.length; i++) { // Check if a button was clicked
                        if (buildButtons[i].hovered()) {
                            buildButtons[i].buttonPressed = true;
                        }
                    }
                    break;
                case "equip":
                    for (int i = 0; i < equipment.length; i++) { // Check if an item slot with an item in it was clicked
                        if (equipment[i].hovered()) {
                            inventoryMaster.liftItem(inventoryMaster.itemSlotArray("equipment"), i);
                            break;
                        }
                    }
                    for (int i = 0; i < belt.length; i++) { // Check if an item slot with an item in it was clicked
                        if (belt[i].hovered()) {
                            inventoryMaster.liftItem(inventoryMaster.itemSlotArray("belt"), i);
                            break;
                        }
                    }
                    break;
                default:
                    break;
                }
            }
        }
        
        void checkClicksRelease() {
            switch (subMenu) {
            case "items":
                break;
            case "craft":
                break;
            case "build":
                for (int i = 0; i < buildButtons.length; i++) {
                    if (buildButtons[i].hovered() && buildButtons[i].buttonPressed) {
                        player.construction.building = buildButtons[i].buttonText;
                        actionMenu.isActive = false;
                        break;
                    }
                }
                break;
            case "equip":
                break;
            default:
                break;
            }
        }
        
        void unclick() {
            switch (subMenu) {
            case "items":
                for (int i = 0; i < itemsButtons.length; i++) {
                    itemsButtons[i].buttonPressed = false;
                }
                break;
            case "craft":
                for (int i = 0; i < craftButtons.length; i++) {
                    craftButtons[i].buttonPressed = false;
                }
                break;
            case "build":
                for (int i = 0; i < buildButtons.length; i++) {
                    buildButtons[i].buttonPressed = false;
                }
                break;
            case "equip":
                for (int i = 0; i < equipButtons.length; i++) {
                    equipButtons[i].buttonPressed = false;
                }
                break;
            default:
                break;
            }
        }
        
        void show() {
            itemsTab.buttonPressed = false; // Unclick all of the tabs, then later reclick the active tab
            craftTab.buttonPressed = false;
            buildTab.buttonPressed = false;
            equipTab.buttonPressed = false;
            switch (subMenu) {
            case "items":
                for (int i = 0; i < itemsButtons.length; i++) {
                    itemsButtons[i].show();
                }
                for (int i = 0; i < inventory.length; i++) {
                    inventory[i].show();
                }
                itemsTab.buttonPressed = true;
                break;
            case "craft":
                for (int i = 0; i < craftButtons.length; i++) {
                    craftButtons[i].show();
                }
                craftTab.buttonPressed = true;
                break;
            case "build":
                for (int i = 0; i < buildButtons.length; i++) {
                    buildButtons[i].show();
                }
                buildTab.buttonPressed = true;
                break;
            case "equip":
                for (int i = 0; i < equipButtons.length; i++) {
                    equipButtons[i].show();
                }
                for (int i = 0; i < equipment.length; i++) {
                    equipment[i].show();
                    if (equipment[i].itemCount == 0) {
                        switch (i) { // Draw the equipment symbol
                        case 0:
                            break;
                        default:
                            break;
                        }
                    }
                }
                for (int i = 0; i < belt.length; i++) {
                    belt[i].show();
                }
                equipTab.buttonPressed = true;
                break;
            default:
                break;
            }
            itemsTab.show();
            craftTab.show();
            buildTab.show();
            equipTab.show();
            if (inventoryMaster.itemLifted) { // Show the item being moved
                imageMode(CENTER);
                image(findImage(inventoryMaster.liftedItem), mouseX, mouseY, inventory[0].slotWidth, inventory[0].slotHeight);
                
                textAlign(RIGHT, BOTTOM);
                textFont(inventory[0].slotFont);
                fill(0);
                text(str(inventoryMaster.liftedCount), mouseX + inventory[0].slotWidth / 2, mouseY + inventory[0].slotHeight / 2);
            }
        }
    }
    ButtonMaster buttonMaster;
    
    class InventoryMaster {
        String[] itemSlotArrays = {"inventory", "equipment", "belt"};
        
        boolean itemLifted = false; // Set to true when the player clicks an item in their inventory, moving it
        String liftedItem = "null"; // Array of the lifted item
        int liftedCount = 0; // Key of the lifted item
        InventoryMaster() {
            inventory[0].itemName = "Sticks";
            inventory[0].itemCount = 3;
            inventory[1].itemName = "Rocks";
            inventory[1].itemCount = 3;
            debugString1 = inventory[0].itemName;
            debugFloat1 = inventory[0].itemCount;
        }
        
        ItemSlot[] itemSlotArray(String arrayName) {
            switch (arrayName) {
            case "inventory":
                return inventory;
            case "equipment":
                return equipment;
            case "belt":
                return belt;
            default:
                return new ItemSlot[0];
            }
        }
        
        void liftItem(ItemSlot[] fromArray, int fromKey) {
            if (fromArray[fromKey].itemCount == 0) {
                return;
            }
            
            if (mouseButton == LEFT) {
                itemLifted = true;
                liftedItem = fromArray[fromKey].itemName;
                liftedCount = fromArray[fromKey].itemCount;
                
                fromArray[fromKey].itemName = "null";
                fromArray[fromKey].itemCount = 0;
            }
            else if (mouseButton == RIGHT) {
                int originalCount = fromArray[fromKey].itemCount;
                
                itemLifted = true;
                fromArray[fromKey].itemCount = int(fromArray[fromKey].itemCount / 2);
                
                liftedItem = fromArray[fromKey].itemName;
                liftedCount = originalCount - fromArray[fromKey].itemCount;
                
                if (fromArray[fromKey].itemCount == 0) {
                    fromArray[fromKey].itemName = "null";
                }
            }
        }
        
        void placeItem(ItemSlot[] toArray, int toKey) {
            if (mouseButton == LEFT) {
                if (toArray[toKey].itemName == liftedItem || toArray[toKey].itemCount == 0) {
                    toArray[toKey].itemName = liftedItem;
                    toArray[toKey].itemCount += liftedCount;
                    
                    itemLifted = false;
                }
                else {
                    String movingItem = liftedItem;
                    int movingCount = liftedCount;
                    
                    liftedItem = toArray[toKey].itemName;
                    liftedCount = toArray[toKey].itemCount;
                    toArray[toKey].itemName = movingItem;
                    toArray[toKey].itemCount = movingCount;
                }
            }
            else if (mouseButton == RIGHT) {
                if (toArray[toKey].itemName == liftedItem || toArray[toKey].itemCount == 0) {
                    toArray[toKey].itemName = liftedItem;
                    liftedCount--;
                    toArray[toKey].itemCount++;
                    
                    if (liftedCount == 0) {
                        itemLifted = false;
                    }
                }
            }
        }
        
        void dropItem(int dropCount) {
            liftedCount -= dropCount;
            droppedItems.add(new DroppedItem(player.x + 50 * cos(player.dir) + random(-5, 5), player.y + 50 * sin(player.dir) + random(-5, 5), liftedItem, dropCount));
            
            if (liftedCount == 0) {
                itemLifted = false;
            }
        }
        
        boolean canCollect(String collectingItem) {
            for (int i = 0; i < inventory.length; i++) {
                if (inventory[i].itemCount == 0 || inventory[i].itemName == collectingItem) {
                    return true;
                }
            }
            return false;
        }
        
        void collectItem(String collectingName, int collectingCount) {
            for (int i = 0; i < inventory.length; i++) {
                if (inventory[i].itemName == collectingName) {
                    inventory[i].itemCount += collectingCount;
                    return;
                }
            }
            for (int i = 0; i < inventory.length; i++) {
                if (inventory[i].itemCount == 0) {
                    inventory[i].itemName = collectingName;
                    inventory[i].itemCount = collectingCount;
                    return;
                }
            }
        }
    }
    InventoryMaster inventoryMaster;
    
    ActionMenu() {
        for (int i = 0; i < inventory.length; i++) {
            inventory[i] = new ItemSlot(57.5 * (i % 8) + 80, 60 * int(i / 8) + 60, 37.5, 37.5);
        }
        for (int i = 0; i < belt.length; i++) {
            belt[i] = new ItemSlot(57.5 * i + 80, 240, 37.5, 37.5);
        }
        for (int i = 0; i < equipment.length; i++) {
            equipment[i] = new ItemSlot(57.5 * (i % 3) + 108.75, 60 * int(i / 3) + 60, 37.5, 37.5);
        }
        
        buttonMaster = new ButtonMaster();
        inventoryMaster = new InventoryMaster();
    }
    
    void show() {
        fill(0, 0, 0, 60);
        rectMode(CORNER);
        rect(0, 0, width, height);
        
        fill(240);
        rectMode(CENTER);
        rect(width / 2, height / 2, width * 0.8, height * 0.8);
        
        buttonMaster.show();
    }
}
ActionMenu actionMenu;



PFont defaultFont;
void setup() {
    size(600, 400);
    frameRate(60);
    cursor(CROSS);
    defaultFont = createFont("Lucida Sans", 12);
    
    loadImages();
    
    ground = new Ground();
    player = new Player();
    actionMenu = new ActionMenu();
    
    testing(true, false);
}

int gameTime = 0; // Begins at 0, increases by 1 every frame
float camOriginX = 0;
float camOriginY = 0;
void draw() {
    gameTime++;
    background(200);
    
    // CALCULATIONS
    player.tick();
    camOriginX = player.x - width / 2 - player.xInCam; // For top-left corner of camera
    camOriginY = player.y - height / 2 - player.yInCam;
    if (player.build.isBuilding) {
        player.build.checkBuildHold();
    }
    for (int i = droppedItems.size() - 1; i >= 0; i--) {
        droppedItems.get(i).collectTick();
    }
    
    // GRAPHICS
    pushMatrix(); // Move the origin opposite to the player so the player stays in frame
    translate(-camOriginX, -camOriginY);
    
    ground.show(camOriginX + width / 2, camOriginY + height / 2); // Create a tile pattern to help see movement
    
    if (player.build.isBuilding) {
        fill(0, 0, 0, 30);
        ellipse(player.build.xOfBuilding, player.build.yOfBuilding, 2 * player.build.buildingRange, 2 * player.build.buildingRange);
    }
    
    for (int i = 0; i < tents.size(); i++) {
        tents.get(i).show();
    }
    for (int i = 0; i < campfires.size(); i++) {
        campfires.get(i).show();
    }
    
    player.show(); // Draw the player
    player.construction.show(); // Draw the building box
    
    for (int i = 0; i < trees.size(); i++) {
        trees.get(i).show();
    }
    
    for (int i = 0; i < droppedItems.size(); i++) {
        droppedItems.get(i).show();
    }
    
    popMatrix(); // Return the origin to (0, 0) to prepare for the next frame
    
    player.showStatusBars();
    
    if (actionMenu.isActive) {
        actionMenu.show();
    }
    
    imageMode(CORNER);
    image(backpackImg, width - 80, height - 95, 75, 90); // Draw inventory icon
    
    drawCursor();
    
    debug(debugMenu);
}

boolean wDown = false; // For keys that must be held down while other keys are being pressed
boolean aDown = false;
boolean sDown = false;
boolean dDown = false;
boolean shiftDown = false;
void keyPressed() {
    if (key == 'w' || key == 'W') {
        wDown = true;
    }
    if (key == 'a' || key == 'A') {
        aDown = true;
    }
    if (key == 's' || key == 'S') {
        sDown = true;
    }
    if (key == 'd' || key == 'D') {
        dDown = true;
    }
    if (keyCode == SHIFT) {
        shiftDown = true;
    }
    if (key == 'e' || key == 'E') {
        actionMenu.isActive = !actionMenu.isActive;
        player.construction.building = "null";
        if (actionMenu.inventoryMaster.itemLifted) {
            actionMenu.inventoryMaster.dropItem(actionMenu.inventoryMaster.liftedCount);
        }
    }
    if ((key == 'l' || key == 'L') && debugMenu) {
        debugState++;
    }
    if (key == ESC) {
        key = 0;
        if (actionMenu.isActive) {
            actionMenu.isActive = false;
        }
        else if (player.construction.building != "null") {
            player.construction.building = "null";
        }
        else {
            // pause
        }
    }
}

void keyReleased() {
    if (key == 'w' || key == 'W') {
        wDown = false;
    }
    if (key == 'a' || key == 'A') {
        aDown = false;
    }
    if (key == 's' || key == 'S') {
        sDown = false;
    }
    if (key == 'd' || key == 'D') {
        dDown = false;
    }
    if (keyCode == SHIFT) {
        shiftDown = false;
    }
}

void mousePressed() {
    if ((mouseX > width - 68 && mouseX < width - 18 && mouseY > height - 88 && mouseY < height - 8) || (mouseX > width - 77 && mouseX < width - 9 && mouseY > height - 42 && mouseY < height - 14)) {
        actionMenu.isActive = !actionMenu.isActive; // Check if the player clicked on the backpack icon
        player.construction.building = "null";
        if (actionMenu.inventoryMaster.itemLifted) {
            actionMenu.inventoryMaster.dropItem(actionMenu.inventoryMaster.liftedCount);
        }
    }
    else if (!actionMenu.isActive) {
        if (player.construction.building != "null") {
            if (mouseButton == LEFT) {
                player.construction.place();
            }
            else if (mouseButton == RIGHT) {
                player.construction.building = "null";
            }
        }
        else {
            if (mouseButton == LEFT) {
                player.build.checkBuildClick();
                player.harvest.checkHarvestClick();
            }
        }
    }
    else {
        if ((mouseX < width * 0.1 || mouseX > width * 0.9) || (mouseY < height * 0.1 || mouseY > height * 0.9)) {
            if (actionMenu.inventoryMaster.itemLifted) {
                if (mouseButton == LEFT) {
                    actionMenu.inventoryMaster.dropItem(actionMenu.inventoryMaster.liftedCount);
                }
                else if (mouseButton == RIGHT) {
                    actionMenu.inventoryMaster.dropItem(1);
                }
            }
            else {
                actionMenu.isActive = false;
            }
        }
        else {
            actionMenu.buttonMaster.checkClicksPress();
        }
    }
}

void mouseReleased() {
    if (mouseButton == LEFT) {
        player.build.isBuilding = false;
        if (actionMenu.isActive) {
            actionMenu.buttonMaster.checkClicksRelease();
            actionMenu.buttonMaster.unclick();
        }
    }
}