How do I cut off the edge of an image in a 2D JavaScript Array?

OK, I am trying to make a game that is a little bit like SimTower but utilizing HTML5 (particularly the canvas element) and JavaScript with a little CSS.

I have ran into a couple of little problems that have caused me to pause coding since summer and read and reread JavaScript books. My main problem right now is that I don't know how to get the lobbies to work properly. Currently, I have divided the lobby images into the left-most portion (which does not repeat) and the right portion which does (but this portion needs to be able to be cut off at arbitrary grid locations. I loaded them into my webpage but the bottom floors (in this case 5 since it is a 3-story grand lobby and a 2-story mezzanine) need to be completely filled in with lobby and mezzanine from the left-most side to the right-most side of the tower (because one cannot build wider than the lobby). The 32 right-most tiles are supposed to repeat (the 7-leftmost tiles are supposed to stay on the leftmost side of the tower) but the repeating portion of the lobby can only repeat whole copies of the image when I need it to be able to be cut off at arbitrary grid tiles (multiples of 16 pixel widths). I was also wondering if it was good form to have 0's in the array where the images/tiles extend (so no images overlap) but I presume it is.

In short, I would like to know how to make the lobby images render correctly (with part of each staying on the left-most side and the rest repeating all the way to the right-most side). My skylobbies look OK because I just have rooms without transparency overlap and cover portions of the skylobby but this is not ideal and it might make programming path-finding harder once it is implemented (and I need the ground lobby to work correctly). I previously posted a similar question that really asked two questions (one on optimizing performance) but I was able to figure that out and fix it thanks to my friends and pen pals.

By the way, my web page has 21 lines of code, and supporting JavaScript pages for the functions and a 110 line array (that is 320 columns wide). I could post a portion of my code if requested though I don't know how helpful it would be to post the entirety. Also, if it matters, each tile in my game is 16 pixels wide and 72 pixels tall (48 pixels for the rooms, 24 pixels for the space between stories).

I am going to assume that a link to the project would probably be best so here it is: https://github.com/AzemOcram/OcramTower/tree/gh-pages

Here is the portion of the code for loading the array and displaying it in the web page:

// Depends on global mapArray, defined in map.js
function drawRooms(ctx, rect) {
var FLOOR_HEIGHT = 72;
var SECTION_WIDTH = 16;
var MAX_ROOM_HEIGHT = FLOOR_HEIGHT * 5;
for (var floor = 0; floor < mapArray.length; floor++) {
var y = 108 + FLOOR_HEIGHT * floor;

// Bail out early to avoid drawing the whole tower every time
if (y > rect.h + rect.y) continue;
if (y + MAX_ROOM_HEIGHT < rect.y) continue;

var floorSections = mapArray[floor];
for (var section = 0; section < floorSections.length; section++) {
var x = 1000 + SECTION_WIDTH * section;
if (x > rect.w + rect.x) continue;

var roomIndex = mapArray[floor][section];
var img = roomImages[roomIndex];
var w = img.naturalWidth;
var h = img.naturalHeight;
if (x + w < rect.x) continue;
if (y + h < rect.y) continue;

ctx.drawImage(img, x, y, w, h);
}
}
}
function draw(ctx, rect) {
drawBackground(ctx, rect);
drawRooms(ctx, rect);
}

var ctx;
function drawFrame() {
var x = -drawOffset.x;
var y = -drawOffset.y;
var w = ctx.canvas.width;
var h = ctx.canvas.height;
ctx.save();
ctx.fillStyle = 'black';
ctx.translate(-x, -y);
ctx.fillRect(x, y, w, h);
draw(ctx, {x: x, y: y, w: w, h: h});
ctx.restore();

console.log("drawing...");

requestAnimationFrame(drawFrame);
}

var canvas = document.getElementById("gameCanvas");

var dragStart = {x: 0, y: 0, started: false};

window.onmousedown = function (e) {
dragStart.x = e.clientX;
dragStart.y = e.clientY;
window.onmousemove = drag;
return false;
};

window.onmouseup = function (e) {
window.onmousemove = null;
return false;
};

function drag(e) {
drawOffset.x += e.clientX - dragStart.x;
drawOffset.y += e.clientY - dragStart.y;
dragStart.x = e.clientX;
dragStart.y = e.clientY;

return false;
};

window.onresize = fillWindow;
function fillWindow() {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
}

fillWindow();

skyLine.src = 'UI/OT_skyline.png';
waitFor(skyLine);

for (var i = 0; i < ROOM_IMAGE_MAP.length; i++) {
var roomImage = new Image();
roomImage.src = ROOM_IMAGE_MAP[i];
waitFor(roomImage);
roomImages.push(roomImage);
}

ctx = canvas.getContext('2d');
waitForAll();
}

waitFor.queue = [];
function waitFor(img) {
waitFor.queue.push(img);
}
function waitForAll() {
for (var i = 0; i < waitFor.queue.length; i++) {
if (waitFor.loaded == waitFor.queue.length - 1) {
requestAnimationFrame(drawFrame);
}


EDIT: Here are some pictures demonstrating the problem:

The left side (which doesn't have the problem visible)

I hope these pictures demonstrate the problem (I need the lobby to extend all the way to the right) and I hope I can get help on this. If I add another copy of the lobby image, it extends beyond the confines of the building area. Ideally, I want to be able to build lobbies that can be extended 1 tile (16 pixels) at a time.

• Could you make a jsfiddle? Your explanation was long, but I did not understand it :P – Trader Nov 18 '14 at 10:22

Use the overload of the drawImage method of 2DContext that allows you to draw part of an image. MDN calls it slicing:

drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)
Given an image, this function takes the area of the source image specified by the rectangle whose top-left corner is (sx, sy) and whose width and height are sWidth and sHeight and draws it into the canvas, placing it on the canvas at (dx, dy) and scaling it to the size specified by dWidth and dHeight.

In your case, you will have to check each tile of the image that you draw, and when you get to the rightmost (or whatever) tile that needs to be cut off, figure out how much width you really need. For the other tiles, you can either draw the full width with this method, or use one of the other overloads that draws the full image.