function Game() { // Current Sprite Selection from User -- might not be needed let currUserSprite; // Four numbers that define the transform, i hat and j hat const i_x = 1; const i_y = 0.5; const j_x = -1; const j_y = 0.5; // Symmetric 2D array to store instances of TileClass const tile_arr_size = 12; let tileArr = new Array(tile_arr_size).fill(null).map(() => new Array(tile_arr_size).fill(null)); let playerSprite; let playerSprite_curTgc; // the current tgc coordinates that the player is at let playerSprite_curTileType; // The tile type that the player is currently sitting on // Sprite size const w = 64; const h = 64; // what the current cursor calculation is based off let maxWinWidth = 1920; let maxWinHeight = 1080; // TODO: replace let tools = ["Fix Sidewalk", "Add Curb Ramp"]; let currentToolIndex = 0; // keyPressed() has logic to make sure that index doens't go outisde of tools array bounds /* 0 --> grass block (use for where houses go for now) 1 --> road tile 2 --> sidewalk tile 3 --> sidewalk rotated 180 degrees 4 --> broken sidewalk 5 --> broken sidewalk rotated 180 degrees 6 --> sidewalk corner topLeft (no curb ramp) 7 --> sidewalk corner botLeft (no curb ramp) 8 --> sidewalk corner botRight (no curb ramp) 9 --> sidewalk corner topRight (no curb ramp) */ // Hardcoding tile placement for the first level // x and y here are just the grid coordinates const startingTileIndex = { x: 3, y: 0 }; const endingTileIndex = { x: 3, y: 11 }; const levelOne = [ [0, 0, 0, 2, 1, 1, 1, 1, 2, 0, 0, 0], [0, 0, 0, 2, 1, 1, 1, 1, 2, 0, 0, 0], [0, 0, 0, 2, 1, 1, 1, 1, 2, 0, 0, 0], [3, 3, 5, 6, 1, 1, 1, 1, 9, 3, 5, 3], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1], [3, 5, 3, 7, 1, 1, 1, 1, 8, 3, 3, 3], [0, 0, 0, 2, 1, 1, 1, 1, 4, 0, 0, 0], [0, 0, 0, 2, 1, 1, 1, 1, 4, 0, 0, 0], [0, 0, 0, 2, 1, 1, 1, 1, 2, 0, 0, 0], ]; // Currently 10 by 10 this.setup = function() { // createCanvas(maxWinWidth, maxWinHeight); new Canvas(maxWinWidth, maxWinHeight); imageMode(CENTER); // Create our player sprite playerSprite = new Sprite(); playerSprite.width = w; playerSprite.height = h; playerSprite.image = 'assets/tile_pngs/grass.png'; playerSprite_curTgc = startingTileIndex; // An object: {x: _, y: _} playerSprite.x = toScreenCoords(playerSprite_curTgc).x; playerSprite.y = toScreenCoords(playerSprite_curTgc).y; // Now loop through our tile map and draw // each tile accoridng to our levelOne matrix // mapping for (let i = 0; i < tile_arr_size; i++) { for (let j = 0; j < tile_arr_size; j++) { let tile_type = levelOne[i][j]; let tsc = toScreenCoords({ x: i, y: j }); switch (tile_type) { case 0: // grass block (use for where houses go for now) // grid[i][j] = new GridTile(j * gridSize, i * gridSize, gridSize, "city", "none"); tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, grassTile, "decoration", "normal"); tileArr[i][j].display(tileArr[i][j].sprite); break; case 1: // road tile // grid[i][j] = new GridTile(j * gridSize, i * gridSize, gridSize, "road", "vertical"); tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, roadTile, "road", "normal"); tileArr[i][j].display(tileArr[i][j].sprite); break; case 2: // sidewalk tile // grid[i][j] = new GridTile(j * gridSize, i * gridSize, gridSize, "road", "horizontal"); tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, sidewalk, "sidewalk", "normal"); tileArr[i][j].display(tileArr[i][j].sprite); break; case 3: // sidewalk rotated 180 degrees // grid[i][j] = new GridTile(j * gridSize, i * gridSize, gridSize, "road", "4way"); tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, sidewalkRotated180, "sidewalk", "rotated180"); tileArr[i][j].display(tileArr[i][j].sprite); break; case 4: // broken sidewalk // grid[i][j] = new GridTile(j * gridSize, i * gridSize, gridSize, "road", "4way"); tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, sidewalkBroken, "broken_sidewalk", "normal"); tileArr[i][j].display(tileArr[i][j].sprite); break; case 5: // broken sidewalk rotated 180 degrees // grid[i][j] = new GridTile(j * gridSize, i * gridSize, gridSize, "road", "4way"); tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, sidewalkBrokenRotated180, "broken_sidewalk", "rotated180"); tileArr[i][j].display(tileArr[i][j].sprite); break; case 6: // sidewalk corner topLeft (no curb ramp) // grid[i][j] = new GridTile(j * gridSize, i * gridSize, gridSize, "road", "4way"); tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, sidewalkCornerTopLeft, "sidewalk_corner", "normal"); tileArr[i][j].display(tileArr[i][j].sprite); break; case 7: // sidewalk corner botLeft (no curb ramp) // grid[i][j] = new GridTile(j * gridSize, i * gridSize, gridSize, "road", "4way"); tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, sidewalkCornerBotLeft, "sidewalk_corner", "rotated90"); // TODO: Replace with rotated versions tileArr[i][j].display(tileArr[i][j].sprite); break; case 8: // sidewalk corner botRight (no curb ramp) // grid[i][j] = new GridTile(j * gridSize, i * gridSize, gridSize, "road", "4way"); tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, sidewalkCornerBotRight, "sidewalk_corner", "rotated180"); // TODO: Replace with rotated versions tileArr[i][j].display(tileArr[i][j].sprite); break; case 9: // sidewalk corner topRight (no curb ramp) // grid[i][j] = new GridTile(j * gridSize, i * gridSize, gridSize, "road", "4way"); tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, sidewalkCornerTopRight, "sidewalk_corner", "rotated270"); // TODO: Replace with rotated versions tileArr[i][j].display(tileArr[i][j].sprite); break; default: // Otherwise, default to road // grid[i][j] = new GridTile(j * gridSize, i * gridSize, gridSize, "city", "none"); tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, roadTile, "road", "normal"); tileArr[i][j].display(tileArr[i][j].sprite); } // tileArr[i][j] = new TileClass(i, j, tsc.x, tsc.y, roadTile); // tileArr[i][j].display(tileArr[i][j].sprite); } } } this.draw = function() { currUserSprite = sidewalk; background('#588dbe'); translate(width / 2, height / 3); // For now, use simple text to show which // tool you have selected // Top-left corner // textAlign(LEFT, TOP); textSize(16); // fill(255); // stroke(0); // strokeWeight(4); text(`Current Tool: ${tools[currentToolIndex]}`, -300, -10); // Draw the player in their current pos let curPlayerScreenCords = toScreenCoords(playerSprite_curTgc); playerSprite.x = curPlayerScreenCords.x; playerSprite.y = curPlayerScreenCords.y; let tgc = screenToGridCoords({ x: mouseX, y: mouseY }); for (let i = 0; i < tile_arr_size; i++) { for (let j = 0; j < tile_arr_size; j++) { tileArr[i][j].display(tileArr[i][j].sprite); // hover highlighting if (tgc.x == i && tgc.y == j && tileArr[i][j].sprite == roadTile) { tileArr[i][j].display(roadHighlightedTile); } else { tileArr[i][j].display(tileArr[i][j].sprite); } } } } this.mouseClicked = function() { let { x: tgc_x, y: tgc_y } = screenToGridCoords({ x: mouseX, y: mouseY }); if (!isValidGridCoords(tgc_x, tgc_y)) { return; } /* Type can either be - decoration (grass, house, etc) - road - sidewalk - broken_sidewalk - sidewalk_corner - sidewalk_corner_curbRamp */ /* Orientation can either be - normal (rotated 0 degrees) - rotated90 - rotated180 - rotated270 */ // TODO: Make a fixed_sidewalk sprite tile_type = tileArr[tgc_x][tgc_y].type; tile_orientation = tileArr[tgc_x][tgc_y].orientation; // Depending on what type of tile you clicked on, // update the sprite accordingly if (tile_type === "broken_sidewalk" && tools[currentToolIndex] === "Fix Sidewalk") { // If broken sidewalk is clicked, then // repair to make fixed sidewalk updateTileType(tgc_x, tgc_y, "sidewalk"); // Check to see this tile's orientation to make sure we // place down the right sprite if (tile_orientation === "normal") { updateTileSprite(tgc_x, tgc_y, sidewalk); } else if (tile_orientation === "rotated180") { updateTileSprite(tgc_x, tgc_y, sidewalkRotated180); } } else if (tile_type === "sidewalk_corner" && tools[currentToolIndex] === "Add Curb Ramp") { // If sidewalk corner is clicked, then // replace with curb ramp updateTileType(tgc_x, tgc_y, "sidewalk_corner_curbRamp"); // Check to see this tile's orientation to make sure we // place down the right sprite if (tile_orientation === "normal") { updateTileSprite(tgc_x, tgc_y, sidewalkCurbRampTopLeft); } else if (tile_orientation === "rotated90") { updateTileSprite(tgc_x, tgc_y, sidewalkCurbRampBotLeft); } else if (tile_orientation === "rotated90") { updateTileSprite(tgc_x, tgc_y, sidewalkCurbRampBotRight); } else { updateTileSprite(tgc_x, tgc_y, sidewalkCurbRampTopRight); } } } this.keyPressed = function() { if (key === 'r') { tileArr = rotateMatrixClockwise(tileArr); } if (key === 't') { // Cycle to the next tool in our tools array (and wrap back around if // we have reached the end of our tools array) currentToolIndex = (currentToolIndex + 1) % tools.length; // TODO: Change the cursor to an image that matches the current tool // type can either be predefined image or path to image // cursor(type, x, y) } } this.keyReleased = function() { // why is the player not showing up? // it seems to be a reference issue let playerSprite_newTcgCords_x = playerSprite_curTgc.x; let playerSprite_newTcgCords_y = playerSprite_curTgc.y; if (key === 'w') { playerSprite_newTcgCords_y -= 1; } else if (key === 's') { playerSprite_newTcgCords_y += 1; } else if (key === 'a') { playerSprite_newTcgCords_x -= 1; } else if (key === 'd') { playerSprite_newTcgCords_x += 1; } // Check to make sure that the new coords are valid, if not, then don't // allow the player to move to that tile let tileType_playerIsOn = tileArr[playerSprite_curTgc.x][playerSprite_curTgc.y].type; let tileType_nextTile = tileArr[playerSprite_newTcgCords_x][playerSprite_newTcgCords_y].type; // console.log(`\nCurrent tile: ${tileType_playerIsOn}`); // console.log(`Next tile: ${tileType_nextTile}`); // console.log(`tileType_nextTile !== "broken_sidewalk": ${tileType_nextTile !== "broken_sidewalk"}`); // console.log(`tileType_nextTile !== "grass": ${tileType_nextTile !== "grass"}`); // console.log(`(tileType_nextTile !== "road" && tileType_playerIsOn !== "sidewalk_corner") : ${(tileType_nextTile !== "road" && tileType_playerIsOn !== "sidewalk_corner") }`); // console.log(`(tileType_nextTile !== "sidewalk_corner" && tileType_playerIsOn !== "road"): ${(tileType_nextTile !== "sidewalk_corner" && tileType_playerIsOn !== "road")}`); // console.log(`(tileType_nextTile !== "sidewalk" && tileType_playerIsOn !== "road"): ${(tileType_nextTile !== "sidewalk" && tileType_playerIsOn !== "road")}`); // check redundancy later here if (tileType_nextTile == "road" && tileType_playerIsOn == "sidewalk_corner_curbRamp") { playerSprite_curTgc.x = playerSprite_newTcgCords_x; playerSprite_curTgc.y = playerSprite_newTcgCords_y; return; } if (tileType_nextTile == "sidewalk_corner_curbRamp" && tileType_playerIsOn == "road") { playerSprite_curTgc.x = playerSprite_newTcgCords_x; playerSprite_curTgc.y = playerSprite_newTcgCords_y; return; } if (tileType_nextTile == "road" && tileType_playerIsOn == "road") { playerSprite_curTgc.x = playerSprite_newTcgCords_x; playerSprite_curTgc.y = playerSprite_newTcgCords_y; return; } if (tileType_nextTile == "sidewalk_corner_curbRamp") { playerSprite_curTgc.x = playerSprite_newTcgCords_x; playerSprite_curTgc.y = playerSprite_newTcgCords_y; return; } // I think we only want to walk on sidwalk no? if (tileType_playerIsOn != "road" && (tileType_nextTile == "sidewalk" || tileType_nextTile == "sidewalk_corner")) { console.log("The movement has been allowed..."); playerSprite_curTgc.x = playerSprite_newTcgCords_x; playerSprite_curTgc.y = playerSprite_newTcgCords_y; } } function rotateMatrixClockwise(matrix) { let rotatedMatrix = new Array(tile_arr_size).fill(null).map(() => new Array(tile_arr_size).fill(null)); for (let i = 0; i < tile_arr_size; i++) { for (let j = 0; j < tile_arr_size; j++) { let newX = j; let newY = tile_arr_size - 1 - i; let tile = matrix[i][j]; let screenCoord = toScreenCoords({ x: newX, y: newY }); rotatedMatrix[newX][newY] = new TileClass(newX, newY, screenCoord.x, screenCoord.y, tile.sprite); } } return rotatedMatrix; } // Helper function to check if grid coordinates are within bounds function isValidGridCoords(tgc_x, tgc_y) { return tgc_x >= 0 && tgc_x < tile_arr_size && tgc_y >= 0 && tgc_y < tile_arr_size; } // Helper function to update tile sprite function updateTileSprite(tgc_x, tgc_y, sprite) { tileArr[tgc_x][tgc_y].sprite = sprite; } // Helper function to update tile type function updateTileType(tgc_x, tgc_y, type) { tileArr[tgc_x][tgc_y].type = type; } function invertMatrix(a, b, c, d) { /* Invert a 2d matrix, with a, b, c, and d being [ a b ] [ c d ] */ const det = (1 / (a * d - b * c)); return { a: det * d, b: det * -b, c: det * -c, d: det * a, } } function toGridCoords(screen) { const a = i_x * 0.5 * w; const b = j_x * 0.5 * w; const c = i_y * 0.5 * h; const d = j_y * 0.5 * h; const inv = invertMatrix(a, b, c, d); return { // offset mouse cursor to align with block x: screen.x * inv.a + screen.y * inv.b - 25.75, y: screen.x * inv.c + screen.y * inv.d + 4.15, } } function toScreenCoords(tile) { return { x: tile.x * i_x * 0.5 * w + tile.y * j_x * 0.5 * w, y: tile.x * i_y * 0.5 * h + tile.y * j_y * 0.5 * h, } } // Helper function to convert screen coordinates to grid coordinates function screenToGridCoords(screen) { let tgc = toGridCoords(screen); return { x: Math.floor(tgc.x), y: Math.floor(tgc.y) }; } }