
function Board(container, fillDecoration, wallDecoration) {
    this.container = container;
    this.tokens = [];
    this.player = null;
    this.actorsZOrder = [Token.LIGHT_TYPE, Token.BUMPER_TYPE, Token.DOOR_TYPE, Token.TASER_TYPE, Token.REGULAR_BOMB_TYPE, Token.TIME_BOMB_TYPE, Token.PLASMA_BULLET_TYPE, Token.SHOOTER_TYPE, Token.FLYING_ENEMY_TYPE, Token.STAR_TYPE, Token.BALLOON_TYPE, Token.BREAKABLE_TYPE, Token.PLAYER_TYPE, Token.HOOK_TYPE, Token.SPIKE_TYPE]; //Order from bottom to top z order
    this.zOrderFiltered = [];
    this.animatedDeco = [];

    this.resurrectCol = 0;
    this.resurrectRow = 0;

    this.parse(container, fillDecoration, wallDecoration);
    this.playerStop(this.player);

    this.playerDelay = 1;

    this.resurrectDirection = MoveableToken.NONE;
    globalsignal.add(this.onGlobalSignal.bind(this));
}

Board.prototype.parse = function (container, fillDecoration, wallDecoration) {
    var walls = this.parseWalls(container);
    var fill = this.parseFill(container);

    var wallTokens = [];
    var spikes = this.parseSpikes(container);

    for (var i = container.layers.length - 1; i > -1; i--) {
        var tile = container.layers[i];
        var col = Math.floor(tile.x / game.config.TILE_SIZE);
        var row = Math.floor(tile.y / game.config.TILE_SIZE);
        var rot = this.getQuarter(-tile.skew.x);

        var token = null;

        if (tile.classname.indexOf(Token.ROBIN_ID) !== -1) {
            token = new Player(col, row);
            this.resurrectCol = col;
            this.resurrectRow = row;
            this.player = token;
        } else if (tile.classname.indexOf(Token.WALL_ID) !== -1 || tile.classname.indexOf(Token.WALL_NO_SHADOW_ID) !== -1) {
            var adjacentWalls = [
                this.getTileFrom(col - 1, row - 1, walls),
                this.getTileFrom(col, row - 1, walls),
                this.getTileFrom(col + 1, row - 1, walls),
                this.getTileFrom(col - 1, row, walls),
                this.getTileFrom(col, row, walls),
                this.getTileFrom(col + 1, row, walls),
                this.getTileFrom(col - 1, row + 1, walls),
                this.getTileFrom(col, row + 1, walls),
                this.getTileFrom(col + 1, row + 1, walls)];
			if (tile.classname.indexOf(Token.WALL_NO_SHADOW_ID) !== -1){
				token = new Wall(col, row, adjacentWalls, false);
			} else {
				token = new Wall(col, row, adjacentWalls);
			}
            wallTokens.push(token);
        } else if (tile.classname.indexOf(Token.WALL_EMPTY_ID) !== -1) {
            token = new WallEmpty(col, row);
        }else if (tile.classname.indexOf(Token.BREAKABLE_ID) !== -1) {
            token = new Breakable(col, row);
        } else if (tile.classname.indexOf(Token.BALLOON_ID) !== -1) {
            token = new Balloon(col, row);
        } else if (tile.classname.indexOf(Token.EXIT_ID) !== -1) {
            token = new Exit(col, row);
        } else if (tile.classname.indexOf(Token.SPAWN_ID) !== -1) {
            token = new Spawn(col, row);
        } else if (tile.classname.indexOf(Token.DOOR_ID) !== -1) {
            token = new Door(col, row, rot);
        } else if (tile.classname.indexOf(Token.LIGHT_ID) !== -1) {
            token = new Light(col, row, rot);
        } else if (tile.classname.indexOf(Token.STAR_ID) !== -1) {
            token = new Star(col, row);
        } else if (tile.classname.indexOf(Token.SPIKE_ID) !== -1) {
            var adjacentSpikes = [
                this.getTileFrom(col - 1, row, spikes),
                this.getTileFrom(col + 1, row, spikes),
                this.getTileFrom(col, row - 1, spikes),
                this.getTileFrom(col, row + 1, spikes)];
            token = new Spike(col, row, rot, adjacentSpikes);
        } else if (tile.classname.indexOf(Token.BUMPER_ID) !== -1) {
            token = new Bumper(col, row, rot);
        } else if (tile.classname.indexOf(Token.TASER_ID) !== -1) {
            token = new Taser(col, row, rot);
        } else if (tile.classname.indexOf(Token.DIRECTION_CHANGER_ID) !== -1) {
            token = new DirectionChanger(col, row, rot);
        } else if (tile.classname.indexOf(Token.SHOOTER_ID) !== -1) {
            token = new Shooter(col, row, rot, this.getTileTime(tile.classname));
        } else if (tile.classname.indexOf(Token.TIME_BOMB_ID) !== -1) {
            token = new TimeBomb(col, row, this.getTileTime(tile.classname));
        } else if (tile.classname.indexOf(Token.REGULAR_BOMB_ID) !== -1) {
            token = new RegularBomb(col, row);
        } else if (tile.classname.indexOf(Token.HOR_FLYING_ENEMY_ID) !== -1) {
            token = new FlyingEnemy(col, row, MoveableToken.RIGHT);
        } else if (tile.classname.indexOf(Token.VER_FLYING_ENEMY_ID) !== -1) {
            token = new FlyingEnemy(col, row, MoveableToken.UP);
        } else if (tile.classname.indexOf(Token.BULLET_TIME_ID) !== -1) {
            token = new BulletTimeTile(col, row);
        }

        if (token) {
            this.addToken(token);
        } else {
            
        }
        container.removeChild(tile); //Removing useless logic parsed tiles
    }

    this.sortBackground(fill, fillDecoration, wallTokens, wallDecoration);
};

Board.prototype.getTileTime = function (classname) {
    var arr = classname.split("_");
    return arr[arr.length - 1];
}


Board.prototype.parseFill = function (container) {
    var collidableTiles = [],
        fill = [],
        map2D = [],
        walls = this.getWallsFloodFill(container),
        i = 0,
        j = 0,
        col = 0,
        row = 0,
        tile,
        minRow,
        maxRow,
        minCol,
        maxCol,
        startCol = 0,
        startRow = 0;
    for (col in walls) {
        var colData = walls[col],
            rowArray = [],
            parsedCol = Number(col);
        if (minCol === undefined || (minCol > parsedCol)) {
            minCol = parsedCol;
        }
        if (maxCol === undefined || (maxCol < parsedCol)) {
            maxCol = parsedCol;
        }
        for (row in colData) {
            rowArray.push(Number(row));
        }

        rowArray.sort(function (a, b) {
            return a - b;
        });
        if (minRow === undefined || minRow > rowArray[0]) {
            minRow = rowArray[0];
        }
        if (maxRow === undefined || rowArray[rowArray.length - 1] > maxRow) {
            maxRow = rowArray[rowArray.length - 1];
        }
        collidableTiles.push([Number(col), rowArray]);
    }
    collidableTiles.sort(function (a, b) {
        return a[0] - b[0];
    });
    
    
    
    
    
    maxRow = (maxRow - minRow) + 1;
    maxCol = (maxCol - minCol) + 1;
    for (i = 0; i < maxRow; i++) {
        map2D.push([]);
        for (j = 0; j < maxCol; j++) {
            map2D[i].push(0);
        }
    }

    for (col = 0; col < maxCol; col++) {
        for (row = 0; row < maxRow; row++) {
            var colData = collidableTiles[col];
            var rowData = colData[1][row];
            if (rowData !== undefined) {
                var localRow = (rowData - minRow);
                if (map2D[localRow]) {
                    map2D[localRow][col] = 1;
                } else {
                    
                    
                }
            }
        }
    }
    

    for (i = 0; i < container.layers.length; i++) {
        tile = container.layers[i];
        col = Math.floor(tile.x / game.config.TILE_SIZE);
        row = Math.floor(tile.y / game.config.TILE_SIZE);

        if (tile.classname.indexOf(Token.ROBIN_ID) !== -1) {
            startCol = (col - minCol);
            startRow = (row - minRow);
        }
    }
    
    WONBATS.fill(map2D, startRow, startCol, 8);
    for (row = 0; row < map2D.length; row++) {
        for (col = 0; col < map2D[row].length; col++) {
            if (map2D[row][col] === 8) {
                var x = Math.floor((col + minCol) * game.config.TILE_SIZE);
                var y = Math.floor((row + minRow) * game.config.TILE_SIZE);
                var texture = PIXI.Texture.fromImage("assets/wall_bg_x.png");
                var divisions = 2;
                texture = new PIXI.Texture(texture.baseTexture, new PIXI.Rectangle(game.config.TILE_SIZE * (col % divisions), game.config.TILE_SIZE * (row % divisions), game.config.TILE_SIZE, game.config.TILE_SIZE));
                tile = new PIXI.Sprite(texture);
                tile.x = x;
                tile.y = y;
                container.addChild(tile);
                fill.push(tile);
            }
        }
    }
    return fill;
};

Board.prototype.getWallsFloodFill = function (container) {
    var walls = {};

    for (var i = 0; i < container.layers.length; i++) {
        var tile = container.layers[i];
        var col = Math.floor(tile.x / game.config.TILE_SIZE);
        var row = Math.floor(tile.y / game.config.TILE_SIZE);

        if (tile.classname.indexOf(Token.WALL_ID) !== -1 || tile.classname.indexOf(Token.WALL_NO_SHADOW_ID) !== -1) {
            if (!walls[col]) {
                walls[col] = {};
            }
            walls[col][row] = 1;
        }
    }

    return walls;
};

Board.prototype.parseWalls = function (container) {
    var walls = {};

    for (var i = 0; i < container.layers.length; i++) {
        var tile = container.layers[i];
        var col = Math.floor(tile.x / game.config.TILE_SIZE);
        var row = Math.floor(tile.y / game.config.TILE_SIZE);

        if (tile.classname.indexOf(Token.WALL_ID) !== -1 || tile.classname.indexOf(Token.WALL_EMPTY_ID) !== -1 || tile.classname.indexOf(Token.WALL_NO_SHADOW_ID) !== -1) {
            if (!walls[col]) {
                walls[col] = {};
            }
            walls[col][row] = 1;
        }
    }

    return walls;
};

Board.prototype.sortBackground = function (fill, fillDecoration, walls, wallDecoration) {
    var i = 0,
        indexPointer = 0;

    for (i = 0; i < fill.length; i++) {
        this.container.setChildIndex(fill[i], i);
    }
    indexPointer += fill.length;
    indexPointer = this.parseAndAddDecoration(indexPointer, fillDecoration);
    walls.sort(function (a, b) {
        return b.row - a.row;
    }); //Sorting tiles by row to avoid shadow and tile clipping
    for (i = 0; i < walls.length; i++) {
        this.container.setChildIndex(walls[i].container, indexPointer);
    }
    indexPointer += walls.length;
    indexPointer = this.parseAndAddDecoration(indexPointer, wallDecoration);
};

Board.prototype.parseAndAddDecoration = function (indexPointer, decoration) {
    if (!decoration){
        return indexPointer;
    }
    var temp = [];
    for (var i = decoration.children.length - 1; i > -1; i--) {
        var decoElement = decoration.children[i];
        var localPos = this.container.toLocal(new PIXI.Point(decoElement.x, decoElement.y));
        temp.push(decoElement);
        decoElement.x = localPos.x;
        decoElement.y = localPos.y;
        if (decoElement.classname.indexOf("_x") === -1) {
            this.animatedDeco.push(decoElement);
        }
        
    }
    for (var j=0; j<temp.length; j++){
        this.container.addChildAt(temp[j], indexPointer);
    }
    indexPointer += temp.length;
    decoration.parent.removeChild(decoration); //remove empty decoration layer
    
    return indexPointer;
};

Board.prototype.parseSpikes = function (container) {
    var spikes = {};

    for (var i = 0; i < container.layers.length; i++) {
        var tile = container.layers[i];
        var col = Math.floor(tile.x / game.config.TILE_SIZE);
        var row = Math.floor(tile.y / game.config.TILE_SIZE);

        if (tile.classname.indexOf(Token.SPIKE_ID) !== -1) {
            if (!spikes[col]) {
                spikes[col] = {};
            }

            spikes[col][row] = 1;
        }
    }

    return spikes;
};

Board.prototype.getTileFrom = function (col, row, walls) {
    if (walls[col] && walls[col][row]) {
        return true;
    }

    return false;
}

Board.prototype.getSpikeTiles = function (col, row, spikes) {
    if (spikes[col] && spikes[col][row]) {
        return true;
    }

    return false;
}

Board.prototype.getQuarter = function (rad) {
    if (rad === 1.571) return Token.DOWN_ORIENTATION;
    if (rad === -3.142) return Token.LEFT_ORIENTATION;
    if (rad === -1.571) return Token.UP_ORIENTATION;
    return Token.RIGHT_ORIENTATION;
};

Board.prototype.addToken = function (token, zorder) {
    this.tokens.push(token);

    if (zorder !== undefined) {
        this.container.addChildAt(token.container, zorder);
    } else {
        this.container.addChild(token.container);
    }
};

Board.prototype.removeToken = function (token) {
    token.dispose();
};

Board.prototype.onGlobalSignal = function (globalEvent, data) {
    switch (globalEvent) {
        case ge.SPAWN_PLASMA_BULLET:
            var bullet = new PlasmaBullet(data.col, data.row);
            bullet.setDirection(data.direction);
            this.addToken(bullet);
            bullet.update(0); //HACK to fix bullet spawn in (0,0) and kill the player for no reason
            break;
        case ge.THROW_HOOK:
            var maxDistance = this.getDistanceToNearestType([Token.WALL_TYPE, Token.SHOOTER_TYPE, Token.SPIKE_TYPE, Token.TIME_BOMB_TYPE, Token.REGULAR_BOMB_TYPE, Token.DOOR_TYPE, Token.BREAKABLE_TYPE], data.col, data.row, data.direction);
            var hookData = {
                col: data.col + maxDistance.col,
                row: data.row + maxDistance.row,
                direction: data.direction
            };

            globalsignal.emit(ge.HOOK_PARTICLE, {
                x: Math.floor(hookData.col * game.config.TILE_SIZE) + (game.config.TILE_SIZE / 2) + ((game.config.TILE_SIZE / 2) * data.direction[0]),
                y: Math.floor(hookData.row * game.config.TILE_SIZE) + (game.config.TILE_SIZE / 2) + ((game.config.TILE_SIZE / 2) * data.direction[1]),
                direction: data.direction
            });
            var hook = new Hook(hookData.col, hookData.row);
            hook.setDirection(hookData.direction);
            hook.update(0);
            hook.stop();
            this.addToken(hook);
            this.player.setHook(hook, this.getToken(hook.col + data.direction[0], hook.row + data.direction[1]));
            this.player.setNextDirection(data.direction);
            break;
        case ge.TILE_EXPLODE:
            this.blowUpEverthingAround(data.col, data.row);
            break;
        case ge.TOKEN_DESTROYED:
            break;
    }
};

Board.prototype.getToken = function (col, row) {
    for (var i = 0; i < this.tokens.length; i++) {
        var token = this.tokens[i];
        if (token.col === col && token.row === row) {
            return token;
        }
    }
    return;
};

Board.prototype.getDistanceToNearestType = function (types, col, row, direction) {
    var distance = {
        col: 0,
        row: 0
    };

    if (direction[0] !== 0) {
        var colReached = false;
        while (!colReached) {
            distance.col += direction[0];
            for (var i = 0; i < this.tokens.length; i++) {
                var token = this.tokens[i];
                if (token.row === row && token.col === (col + distance.col) && this.tokenIsType(token, types) && !token.isSensor) {
                    colReached = true;
                    distance.col -= direction[0];
                    break;
                }
            }
        }
    }

    if (direction[1] !== 0) {
        var rowReached = false;
        while (!rowReached) {
            distance.row += direction[1];
            for (var i = 0; i < this.tokens.length; i++) {
                var token = this.tokens[i];
                if (token.col === col && token.row === (row + distance.row) && this.tokenIsType(token, types) && !token.isSensor) {
                    rowReached = true;
                    distance.row -= direction[1];
                    break;
                }
            }
        }
    }

    return distance;
}

Board.prototype.tokenIsType = function (token, types) {
    for (var i = 0; i < types.length; i++) {
        if (token.type === types[i]) {
            return true;
        }
    }

    return false;
};

Board.prototype.update = function (dt) {
    var interactionPairs = [];
    this.zOrderFiltered.splice(0, this.zOrderFiltered.length);

    this.playerDelay -= dt;
    for (var i = 0; i < this.tokens.length; i++) {
        var tokenA = this.tokens[i];
        tokenA.update(dt);
        var zIndex = this.actorsZOrder.indexOf(tokenA.type);
        if (zIndex != -1 && tokenA.container.visible) {
            this.zOrderFiltered.push({
                index: zIndex,
                token: tokenA
            });
        }
        if (tokenA.isDynamic) {
            for (var j = 0; j < this.tokens.length; j++) {
                var tokenB = this.tokens[j];
                if (tokenA !== tokenB) {
                    if (tokenB.isSensor) {
                        if (tokenA.overlap(tokenB)) {
                            interactionPairs.push([tokenA, tokenB]);
                        }
                    } else if (tokenA.collide(tokenB)) {
                        interactionPairs.push([tokenA, tokenB]);
                    }
                }
            }

            if (tokenA.type === Token.BREAKABLE_TYPE) {
                if (this.isEmpty(tokenA.col, tokenA.row + 1) && tokenA.direction === MoveableToken.NONE) {
                    tokenA.setDirection(MoveableToken.DOWN);
                }
            } else if (tokenA.type === Token.BALLOON_TYPE) {
                if (this.isEmpty(tokenA.col, tokenA.row - 1) && tokenA.direction === MoveableToken.NONE) {
                    tokenA.setDirection(MoveableToken.UP);
                }
            }
        }
    }

    this.sortByZOrderPriority();

    interactionPairs.sort(this.sortResolveByPriority.bind(this));
    for (var k = 0; k < interactionPairs.length; k++) {
        this.resolveInteractionPair(interactionPairs[k][0], interactionPairs[k][1]);
    }
    for (var l = 0; l < interactionPairs.length; l++) {
        interactionPairs[l][0].canCollide = true;
    }
    for (var m = this.tokens.length - 1; m > -1; m--) {
        if (!this.tokens[m].alive) {
            this.removeToken(this.tokens.splice(m, 1)[0]);
        }
    }

    for (var f = 0; f < this.animatedDeco.length; f++) {
        this.animatedDeco[f].update(dt);
    }
};

Board.prototype.isEmpty = function (col, row) {
    for (var i = 0; i < this.tokens.length; i++) {
        var token = this.tokens[i];
        if (!token.isSensor) {
            if (token.col === col &&
                token.row === row &&
                token.alive) {
                if (token.isDynamic) {
                    if (token.direction === MoveableToken.NONE) {
                        return false;
                    }
                } else {
                    return false;
                }
            }
        }
    }

    return true;
};

Board.prototype.sortByZOrderPriority = function () {
    var startingIndex = this.container.children.length - this.zOrderFiltered.length,
        mc,
        currentIndex;
    this.zOrderFiltered.sort(function (a, b) { //z-order by index and row
        if (a.index < b.index) return -1;
        if (a.index > b.index) return 1;
        if (a.index === b.index) {
            if (a.token.row > b.token.row) {
                return -1;
            } else {
                return 1;
            }
        }
    });
    for (var i = 0; i < this.zOrderFiltered.length; i++) {
        mc = this.zOrderFiltered[i].token.container;
        currentIndex = this.container.children.indexOf(mc);
        if (currentIndex !== startingIndex + i) {
            this.container.addChildAt(this.zOrderFiltered[i].token.container, startingIndex + i);
        }
    }
};

Board.prototype.sortResolveByPriority = function (elementA, elementB) {
    if (elementA[1].isSensor) {
        return -1;
    } else if (elementB[1].isSensor) {
        return 1;
    } else {
        return 0;
    }
};

Board.prototype.resolveInteractionPair = function (tokenA, tokenB) {
    if (tokenA.type === Token.PLAYER_TYPE && tokenA.canCollide && this.playerDelay < 0) {
        if (tokenB.type === Token.SPAWN_TYPE) {
            tokenB.alive = false;
            this.resurrectCol = tokenB.col;
            this.resurrectRow = tokenB.row;
            this.resurrectDirection = tokenA.direction;
        } else if (tokenB.type === Token.WALL_TYPE) {
            this.playerStop(tokenA);
        } else if (tokenB.type === Token.DOOR_TYPE) {
            if (tokenB.isSensor) {
                tokenB.close();
            } else {
                this.playerStop(tokenA);
            }
        } else if (tokenB.type === Token.LIGHT_TYPE) {
            if (tokenB.isOn) {
                tokenB.turnOff();
            }
        } else if (tokenB.type === Token.TASER_TYPE) {
            if (tokenB.isActive) {
                this.playerDie(tokenA, true);
            }
        } else if (tokenB.type === Token.SHOOTER_TYPE) {
            this.playerStop(tokenA);
        } else if (tokenB.type === Token.TIME_BOMB_TYPE) {
            this.playerStop(tokenA);
            tokenB.ignite();
        } else if (tokenB.type === Token.REGULAR_BOMB_TYPE) {
            this.playerStop(tokenA);
        } else if (tokenB.type === Token.HOOK_TYPE) {
            tokenB.destroy();
        } else if (tokenB.type === Token.PLASMA_BULLET_TYPE) {
            this.playerDie(tokenA, true);
            tokenB.destroy();
            tokenB.stop();
        } else if (tokenB.type === Token.STAR_TYPE) {
            tokenB.destroy();
            globalsignal.emit(ge.STAR_GRABBED);
        } else if (tokenB.type === Token.EXIT_TYPE) {
            tokenA.stop(false, false, false, false);
            tokenA.canCollide = false;
            tokenB.destroy();
            globalsignal.emit(ge.LEVEL_END);
        } else if (tokenB.type === Token.SPIKE_TYPE) {
            this.playerDie(tokenA, false);
        } else if (tokenB.type === Token.FLYING_ENEMY_TYPE) {
            this.playerDie(tokenA, true);
            tokenB.discharge();
        } else if (tokenB.type === Token.BUMPER_TYPE) {
            tokenB.push(tokenA);
            if (tokenA.direction === MoveableToken.RIGHT) {
                if (tokenB.rot === Token.UP_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.UP);
                } else if (tokenB.rot === Token.LEFT_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.DOWN);
                }
            } else if (tokenA.direction === MoveableToken.LEFT) {
                if (tokenB.rot === Token.RIGHT_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.UP);
                } else if (tokenB.rot === Token.DOWN_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.DOWN);
                }
            } else if (tokenA.direction === MoveableToken.DOWN) {
                if (tokenB.rot === Token.RIGHT_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.RIGHT);
                } else if (tokenB.rot === Token.UP_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.LEFT);
                }
            } else if (tokenA.direction === MoveableToken.UP) {
                if (tokenB.rot === Token.DOWN_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.RIGHT);
                } else if (tokenB.rot === Token.LEFT_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.LEFT);
                }
            }
            tokenA.trail.start();
        } else if (tokenB.type === Token.DIRECTION_CHANGER_TYPE) {
            if (tokenB.rot === Token.UP_ORIENTATION) {
                tokenA.forceDirection(MoveableToken.UP);
            } else if (tokenB.rot === Token.RIGHT_ORIENTATION) {
                tokenA.forceDirection(MoveableToken.RIGHT);
            } else if (tokenB.rot === Token.DOWN_ORIENTATION) {
                tokenA.forceDirection(MoveableToken.DOWN);
            } else if (tokenB.rot === Token.LEFT_ORIENTATION) {
                tokenA.forceDirection(MoveableToken.LEFT);
            }
        } else if (tokenB.type === Token.BULLET_TIME_TYPE) {
            globalsignal.emit(ge.BULLET_TIME);
            tokenB.destroy();
        } else if (tokenB.type === Token.BREAKABLE_TYPE) {
            if (tokenA.direction !== MoveableToken.NONE) {
                tokenA.forceDirection(tokenA.direction);
            }
            tokenB.destroy();
        } else if (tokenB.type === Token.BALLOON_TYPE) {
            tokenB.destroy();
        }
    }
    else if (tokenA.type === Token.BALLOON_TYPE && tokenA.canCollide) {
        if (tokenB.type === Token.WALL_TYPE ||
            tokenB.type === Token.SHOOTER_TYPE ||
            tokenB.type === Token.BREAKABLE_TYPE ||
            tokenB.type === Token.BALLOON_TYPE ||
            tokenB.type === Token.STAR_TYPE ||
            tokenB.type === Token.TIME_BOMB_TYPE ||
            tokenB.type === Token.REGULAR_BOMB_TYPE) {
            tokenA.stop();
        } else if (tokenB.type === Token.TASER_TYPE && tokenB.isActive) {
            tokenA.destroy();
            tokenA.stop();
        } else if (tokenB.type === Token.PLASMA_BULLET_TYPE) {
            tokenA.destroy();
            tokenA.stop();
        }
    }
    else if (tokenA.type === Token.BREAKABLE_TYPE && tokenA.canCollide) {
        if (tokenB.type === Token.WALL_TYPE ||
            tokenB.type === Token.SHOOTER_TYPE ||
            tokenB.type === Token.BREAKABLE_TYPE ||
            tokenB.type === Token.BALLOON_TYPE ||
            tokenB.type === Token.TIME_BOMB_TYPE ||
            tokenB.type === Token.REGULAR_BOMB_TYPE) {
            tokenA.stop();
        } else if (tokenB.type === Token.TASER_TYPE && tokenB.isActive) {
            tokenA.destroy();
            tokenA.stop();
        }
    }
    else if (tokenA.type === Token.PLASMA_BULLET_TYPE && tokenA.canCollide) {
        if (tokenB.type === Token.WALL_TYPE ||
            tokenB.type === Token.SHOOTER_TYPE ||
            tokenB.type === Token.BREAKABLE_TYPE ||
            tokenB.type === Token.BALLOON_TYPE ||
            tokenB.type === Token.PLASMA_BULLET_TYPE ||
            tokenB.type === Token.TIME_BOMB_TYPE ||
            tokenB.type === Token.REGULAR_BOMB_TYPE) {
            tokenA.destroy();
            tokenA.stop();
        }

        if (tokenB.type === Token.PLASMA_BULLET_TYPE ||
            tokenB.type === Token.BREAKABLE_TYPE ||
            tokenB.type === Token.BALLOON_TYPE) {
            tokenB.destroy();
            tokenB.stop();
        }

        if (tokenB.type === Token.TIME_BOMB_TYPE) {
            tokenB.ignite();
        }
    }
    else if (tokenA.type === Token.FLYING_ENEMY_TYPE && tokenA.canCollide) {
        if (tokenB.type === Token.WALL_TYPE || tokenB.type === Token.SHOOTER_TYPE || tokenB.type === Token.SPIKE_TYPE || tokenB.type === Token.REGULAR_BOMB_TYPE) {
            tokenA.bounce();
        } else if (tokenB.type === Token.BUMPER_TYPE) {
            tokenB.push(tokenA);
            if (tokenA.direction === MoveableToken.RIGHT) {
                if (tokenB.rot === Token.UP_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.UP);
                } else if (tokenB.rot === Token.LEFT_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.DOWN);
                }
            } else if (tokenA.direction === MoveableToken.LEFT) {
                if (tokenB.rot === Token.RIGHT_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.UP);
                } else if (tokenB.rot === Token.DOWN_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.DOWN);
                }
            } else if (tokenA.direction === MoveableToken.DOWN) {
                if (tokenB.rot === Token.RIGHT_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.RIGHT);
                } else if (tokenB.rot === Token.UP_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.LEFT);
                }
            } else if (tokenA.direction === MoveableToken.UP) {
                if (tokenB.rot === Token.DOWN_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.RIGHT);
                } else if (tokenB.rot === Token.LEFT_ORIENTATION) {
                    tokenA.forceDirection(MoveableToken.LEFT);
                }
            }
        } else if (tokenB.type === Token.TASER_TYPE) {
            if (tokenB.isActive) {
                tokenA.discharge();
            }
        } else if (tokenB.type === Token.FLYING_ENEMY_TYPE) {
            tokenA.bounce(true);
            tokenB.bounce(true);
        } else if (tokenB.type === Token.TIME_BOMB_TYPE) {
            if (!tokenB.touched) {
                tokenA.discharge();
            } else {
                tokenA.bounce();
            }
            tokenB.ignite();
        } else if (tokenB.type === Token.REGULAR_BOMB_TYPE) {
            tokenA.bounce();
        } else if (tokenB.type === Token.BREAKABLE_TYPE) {
            tokenA.discharge();
            tokenB.destroy();
        } else if (tokenB.type === Token.BALLOON_TYPE) {
            tokenB.destroy();
        } else if (tokenB.type === Token.PLASMA_BULLET_TYPE) {
            tokenA.discharge();
            tokenB.destroy();
            tokenB.stop();
        } else if (tokenB.type === Token.DOOR_TYPE) {
            if (!tokenB.isSensor) {
                tokenA.bounce();
            }
        }
    }
    else if (tokenA.type === Token.HOOK_TYPE) {
        if (tokenB.type === Token.SPIKE_TYPE) {
            tokenA.view.gotoAndStop("laser");
        }
    }
};

Board.prototype.blowUpEverthingAround = function (col, row) {
    var area = [[col, row - 1], [col - 1, row], [col + 1, row], [col, row + 1]];

    for (var a = 0; a < area.length; a++) {
        var expandedCol = area[a][0];
        var expandedRow = area[a][1];

        for (var i = 0; i < this.tokens.length; i++) {
            var token = this.tokens[i];
            if (token.col === expandedCol && token.row === expandedRow && token.alive) {
                if (token.type === Token.TIME_BOMB_TYPE) {
                    token.ignite(0.1);
                } else if (token.type === Token.REGULAR_BOMB_TYPE) {
                    token.ignite();
                } else if (token.type === Token.PLAYER_TYPE) {
                    this.playerDie(token, false);
                } else if (token.type === Token.BREAKABLE_TYPE) {
                    token.destroy();
                } else if (token.type === Token.BALLOON_TYPE) {
                    token.destroy();
                }
            }
        }
    }
};

Board.prototype.playerStop = function (player) {
    var up = this.getDistanceToNearestType([Token.WALL_TYPE, Token.SHOOTER_TYPE, Token.SPIKE_TYPE, Token.TIME_BOMB_TYPE, Token.REGULAR_BOMB_TYPE, Token.DOOR_TYPE], player.col, player.row, MoveableToken.UP).row !== 0;
    var right = this.getDistanceToNearestType([Token.WALL_TYPE, Token.SHOOTER_TYPE, Token.SPIKE_TYPE, Token.TIME_BOMB_TYPE, Token.REGULAR_BOMB_TYPE, Token.DOOR_TYPE], player.col, player.row, MoveableToken.RIGHT).col !== 0;
    var down = this.getDistanceToNearestType([Token.WALL_TYPE, Token.SHOOTER_TYPE, Token.SPIKE_TYPE, Token.TIME_BOMB_TYPE, Token.REGULAR_BOMB_TYPE, Token.DOOR_TYPE], player.col, player.row, MoveableToken.DOWN).row !== 0;
    var left = this.getDistanceToNearestType([Token.WALL_TYPE, Token.SHOOTER_TYPE, Token.SPIKE_TYPE, Token.TIME_BOMB_TYPE, Token.REGULAR_BOMB_TYPE, Token.DOOR_TYPE], player.col, player.row, MoveableToken.LEFT).col !== 0;
    player.stop(up, right, down, left);
}

Board.prototype.playerDie = function (player, isElectrocuted) {
    player.destroy(isElectrocuted);
    player.stop(false, false, false, false);
    globalsignal.emit(ge.LEVEL_LOSE);
};

Board.prototype.resurrectPlayer = function () {
    this.player.alive = false;
    this.player = new Player(this.resurrectCol, this.resurrectRow);
    this.addToken(this.player);
    this.player.update(0);
    this.player.setDirection(this.resurrectDirection);
}

Board.prototype.dispose = function () {
    this.animatedDeco.splice(0, this.animatedDeco.length);
    this.zOrderFiltered.splice(0, this.zOrderFiltered.length);
    this.actorsZOrder.splice(0, this.actorsZOrder.length);
    this.tokens.splice(0, this.tokens.length);
};
