I have multiple objects like this(16)
function Baboo(x,y,size,speed,target,sps,life){
MobRoot.call(this,x,y,size,speed,target,sps,life);
this.spsCoord = [500*0,500*3,500,500];
this.coolDown = 60;
this.atack = 0;}
Baboo.prototype = Object.create(MobRoot.prototype);
Baboo.prototype.throw = function()
{
projectiles.push(new Arrow(this.x,this.y,10,10,this.angle,150,this.target));
}
Baboo.prototype.update = function(){
if(this.life>0)
{
this.sendBack();
this.draw();
this.checkClick();
this.angle = CalculateSlope(this,this.target);
this.directionUpdate();
this.updateBuffs();
this.x += this.direction.x;
this.y += this.direction.y;
}}
/*etc aditional methods*/
that inherit all from this object
function MobRoot(x,y,size,speed,target,sps,life)
{
this.size = size*WindowScale;
this.x = x;
this.y = y;
this.angle = 0;
this.speed=speed;
this.colided=false;
this.maxlife = life;
this.life = life;
this.buffArray = [];
this.sps = sps;
this.target = target;
this.direction =
{
x:0,
y:0
}
//x,y,w,h
//ex sprite[0][0] = 0,0,500,500;
this.spsCoord = [];
this.isBoss = false;
}
MobRoot.prototype.directionUpdate = function()
{
this.direction.x = this.speed*Math.cos(this.angle)*-1;
this.direction.y = this.speed*Math.sin(this.angle)*-1;
}
/*aditional methods for mobRoot*/
and I want to generate different mobs from an array. At the moment I'm assigning a number for each type (ex: 1-Baboo 2-Spider 3-Whatever) and storing these numbers in arrays, and when I'm generating them i use a switch for each mobIndex from the array like this
switch(wave[i])
{
case 1:
mobs.push(new Baboo(arg1,arg2,...));
break;
case 2:
mobs.push(new Spider(arg1,arg2,...));
break;
/*...*/
case 999:
mobs.push(new Whatever(arg1,arg2,...));
break;
}
Is there a more elegant way of solving this kind of problem?
You can put all the constructors into an array:
var constructors = [Baboo, Spider, Whatever, ...];
if (wave[i] < constructors.length) {
mobs.push(new constructors[wave[i]](arg1, arg2, arg3, ...));
}
Related
I'm trying to create a very basic physics simulation in p5.js and I am using a class to create multiple instances of the shapes (which are currently all circles), I have a function that checks if the user clicks inside the area of a circle and if so allows them to drag it around but I ran into a problem. I need to have the program work out which object it is hovering but I'm not sure how I would do so, below I have the function working for only the first object (obj1). can I do something like {classname}.posX instead?
function whilePressed()
{
if (Math.pow(mouseX-obj1.posX,2)+(Math.pow(mouseY-obj1.posY,2))<=(Math.pow(obj1.size/2,2)) | grabbed == true)
{
grabbed = true;
if (firstGrab == true)
{
difX = (obj1.posX-mouseX);
difY = (obj1.posY-mouseY);
firstGrab = false;
}
obj1.posX = mouseX+difX;
obj1.posY = mouseY+difY;
}
}
below is the class (the draw function has a switch statement in it because I used to have a square as well but decided to get a circle working before implementing a square)
class primitive
{
constructor()
{
this.size = 50;
this.posX = canvasSize/2;
this.posY = canvasSize/2;
this.velX = 0;
this.velY = 0;
this.terminalVel = 15;
}
pos(x,y)
{
this.posX = x;
this.posY = y;
}
draw(shape = 'circle')
{
stroke(168,198,159);
fill(204,226,163);
switch (shape)
{
case 'circle':
circle(this.posX,this.posY,this.size);
break;
}
}
gravity()
{
if (this.velY < this.terminalVel)
{
this.velY = (this.velY+1);
}
else
{
this.velY = 20;
}
this.posY = this.posY+this.velY;
if (this.posY > groundLevel-(this.size/2))
{
this.posY = groundLevel-(this.size/2);
this.velY = 0;
}
}
}
You can create a static method on the primitive class like so:
First, create an array which has all the instances of the class in it.
This is the code:
Remember: I added the parameter name to the constructor. That means when creating an instance do it like so:
var foo = new primitive("foo");
var PRIMITIVES = [];
// ...
constructor(name)
{
this.name = name;
this.size = 50;
this.posX = canvasSize/2;
this.posY = canvasSize/2;
this.velX = 0;
this.velY = 0;
this.terminalVel = 15;
PRIMITIVES.push(name);
}
Now, using the same mouse find principle, you can create a static method that finds and return the right instance.
static findInstance(mouseX, mouseY) {
for (var i = 0; i < PRIMITIVES.length; i++)
{
obj = window[PRIMITIVES[i]];
if (Math.pow(mouseX-obj.posX,2)+(Math.pow(mouseY-obj.posY,2))<=(Math.pow(obj.size/2,2)))
{
return obj;
}
}
}
Then, you can call primitive.findInstance(mouseX, mouseY), and it will return the right instance. If this doesn't work, please comment. I hope this helped you.
Create an array of objects:
let objects = []
objects.push(obj1);
objects.push(obj2);
Implement an algorithm in the mousePressed() callback that detects the clicked object:
let draggedObject;
let dragOffsetX;
let dragOffsetY;
function mousePressed() {
draggedObject = null;
for (let i=0; i < objects.lenght; i++) {
obj = objects[i];
if (Math.pow(mouseX-obj.posX,2) + Math.pow(mouseY-obj.posY,2) <= Math.pow(obj.size/2,2)) {
draggedObject = obj;
dragOffsetX = draggedObject.posX - mouseX;
dragOffsetY = draggedObject.posY - mouseY;
break;
}
}
}
Change the position of the object in the mouseDragged() callback:
function mouseDragged() {
if (dragged_object) {
draggedObject.posX = mouseX + dragOffsetX;
draggedObject.posY = mouseY + dragOffsetY;
}
}
I'm developing a little game of nothing at all more commonly called "Pathfinding"; and I am crashing on a small error that I have never encountered (I am young dev);
I have searched everywhere but I do not understand why this error appears.
I am experiencing this error:
Uncaught TypeError: current.distance is not a function
at search_min_distance (pathfinding.js:127)
at game (pathfinding.js:151)
at HTMLDocument. (pathfinding.js:173)
document.addEventListener("DOMContentLoaded", function() {
const NBR_POINT = 10;
const MIN_SPACING = 100;
const HEIGHT = window.innerHeight;
const WIDTH = window.innerWidth;
class point {
constructor(x, y, name, id) {
this.x = x;
this.y = y;
this.part = document.createElement("div");
this.part.className = name;
this.part.id = id;
}
distance(cmp) {
return Math.sqrt(Math.pow(cmp.x - this.x, 2) + Math.pow(cmp.y - this.y, 2));
}
}
function random(mode) {
if (!mode)
return Math.random() * ((WIDTH - 100) - 100) + 100;
else
return Math.random() * ((HEIGHT - 100) - 100) + 100;
}
function printPoint(stk, crt) {
var block = 0;
do {
block = 0;
var x = random(0);
var y = random(1);
if (stk.length != 0) {
for (let i = 0; i < stk.length; i++) {
if (x < (stk[i].x + MIN_SPACING) && x > (stk[i].x - MIN_SPACING) && y < (stk[i].y + MIN_SPACING) && y > (stk[i].y - MIN_SPACING)) {
block = 1;
break;
}
}
}
} while (block == 1);
var ids = stk.length + 1;
if (crt == "current")
var p = new point(x, y, "point courant", ids);
else
var p = new point(x, y, "point", ids);
p.part.style.bottom = p.y + "px";
p.part.style.left = p.x + "px";
document.body.appendChild(p.part);
return p;
}
function polyPoint() {
var stk = new Array();
for (let i = 0; i < NBR_POINT - 1; i++) {
stk.push(printPoint(stk, null));
}
printPoint(stk, "current");
return stk;
}
function imp_session() {
var dark_status = sessionStorage.getItem('dark_mode');
if (dark_status == 1)
document.body.classList.add("darkmode");
if (dark_status == 0)
document.body.classList.remove("darkmode");
}
function srv_DarkMode() {
document.addEventListener("keypress", function(event) {
let keypress = event.key;
var dark_status = sessionStorage.getItem('dark_mode');
if (keypress == "d") {
if (dark_status == null) {
dark_status = 0;
sessionStorage.setItem('dark_mode', dark_status);
}
if (dark_status == 1) {
this.body.classList.remove("darkmode");
dark_status = 0;
//To be able to delete sessions and not add them afterwards and error duplicate key.
sessionStorage.removeItem('dark_mode');
sessionStorage.setItem('dark_mode', dark_status);
} else {
this.body.classList.add("darkmode");
dark_status = 1;
//To be able to delete sessions and not add them afterwards and error duplicate key.
sessionStorage.removeItem('dark_mode');
sessionStorage.setItem('dark_mode', dark_status);
}
}
});
}
function search_min_distance(stk, current) {
let min = current.distance(stk[0]);
let tmp;
let real = min;
for (let i = 1; i < stk.length; i++) {
if (stk[i].id = current.id)
continue;
tmp = current.distance(stk[i]);
if (tmp < min)
real = i;
}
return real;
}
function game(stk) {
var cp = document.getElementsByClassName('courant');
cp[0].addEventListener("click", function(event) {
alert("txt");
});
var distId = search_min_distance(stk, cp[0]);
var p = document.getElementsByClassName('point');
for (let i = 0; i < p.length; i++) {
if (p[i].id != cp[0].id || p[i].id != distId) {
p[i].addEventListener("click", function(event) {
alert("txt");
});
}
}
}
function init() {
imp_session();
srv_DarkMode();
return polyPoint();
}
game(init());
});
-> current and stk [0] are indeed of "type" point.
All the remaining code does not seem important to me, but if necessary I will provide it.
I thank you in advance ! Be indulgent...
The point you are getting using the getElement function just returns
the js object, it does not contain any functions from the class.
You need to create a new class instance from this obtained point as follows inside the search_min_distance function
const { x, y, name, id } = current;
const currentPoint = new Point(x, y, name, id);
let min = currentPoint.distance(stk[0]);
I suggest you write a util function to calculate the distance between two points, passing the two points as arguments. It will be a cleaner approach.
Bind distance function to this inside constructor
class point {
constructor(x, y, name, id) {
this.x = x;
this.y = y;
this.part = document.createElement("div");
this.part.className = name;
this.part.id = id;
// bind distance method
this.distance = this.distance.bind(this);
}
distance(cmp) {
return Math.sqrt(Math.pow(cmp.x - this.x, 2) + Math.pow(cmp.y - this.y, 2));
}
}
The name of error is "TypeError". And That's not just for fun. Possible problems in search_min_distance:
stk is not defined or its length is 0
stk is not array of points (typeof point[])
point is not defined or it's not point (typeof point)
Sure problems:
point.id is not defined, it's only in point.part
if (stk[i].id = current.id)
to
if (stk[i].part.id = current.part.id)
Your code is only errors.
Finally all this for a simple mistake of inattention; I took dom for my array and put an assignment where there should have been an equal .. thanks everyone.
if (stk[i].id = current.id)
as
if (stk[i].id == current.id)
and
var distId = search_min_distance(stk, cp[0]);
as
var distId = search_min_distance(stk, stk[cp[0].id]);
I have the class Map with a property this.guns = [ ]. I deleted some parts of the code to make it easy to read.
class Maps {
constructor(canvas, sizeX, sizeY, sizeCase) {
this.guns = [];
}
} // end class
Inside this.weapon = [ ] I pushed some objects with its properties. Those objects were instantiated in the Map class.
/* eslint-disable no-console */
class Maps {
constructor(canvas, sizeX, sizeY, sizeCase) {
this.guns = [];
}
setGuns(quantity) {
// this.guns = [];
for (let index = 0; index < quantity; index++) {
const x = App.random(this.size.x);
const y = App.random(this.size.y);
let element;
if (this.mapGame[y][x] === 0) {
switch (index) {
case 0: {
element = new Gun(4, "watermelon", 15, y, x);
this.mapGame[element.y][element.x] = 2;
console.log(element);
this.guns.push(element);
break;
}
case 1: {
element = new Gun(5, "eggplant", 20, y, x);
this.mapGame[element.y][element.x] = 3;
console.log(element);
this.guns.push(element);
break;
}
default:
break;
}
} else {
index -= 1;
}
}
console.log(this.guns);
}
} // end class
I have an other class call Player with a method to move the players. When the player is over a weapon, take the weapon. To do this, I have done the checkWeapon method.
class Player {
constructor(imagePlayer, name, score, positionY, positionX) {
this.imagePlayer = imagePlayer;
this.name = name;
this.score = score;
this.x = positionX;
this.y = positionY;
this.movementAccumulator = 0;
this.limitMovement = 3;
this.weapon = {};
}
/**
* I deleted the not necessary methods, and then the rest of cases on the switch
*/
movement(direction, map, currentPlayer) {
const collision = this.rockCollision(direction, map);
switch (direction) {
case up:
if (this.y > 0) {
if (!collision) {
map.mapGame[this.y][this.x] = 0; // draw grass
this.y -= 1;
this.whoPlayerIsMoving(map, currentPlayer);
this.counterMovement();
this.checkWeapon(map);
}
}
break;
}
checkWeapon(map) {
this.weapon = map.getGun(this.y, this.x);
}
}
Player send throung the checkWeapon method the coordinates x and y, to an other method hosted on the class Map, the getGuns method.
/* eslint-disable no-console */
class Maps {
constructor(canvas, sizeX, sizeY, sizeCase) {
this.guns = [];
}
/**
* The console log are to render(or print) on the console and look if everything goes well.
* The following method is a test, is bad done and it have to be refatoriced
*/
getGun(y, x) {
console.log(`Re Y:${y}` + ` Re X: ${x}`);
console.log(`${this.guns[0].y} ${this.guns[0].x}`);
if (this.guns[0].y == y && this.guns[0].x == x) {
console.log("soy un arma");
}
return gunTaked;
}
} // end class
My main problem is, how to access to the properties of the Gun object. I did the method above, but I got stuck. I have reading about destructuring, but I am not sure if, should I apply it here. Thanks
I have this
class Maps {
constructor(canvas, sizeX, sizeY, sizeCase) {
this.canvas = document.getElementById("canvas");
this.canvas.width = sizeX * sizeCase;
this.canvas.height = sizeY * sizeCase;
this.size = { x: sizeX, y: sizeY, case: sizeCase };
this.ctx = this.canvas.getContext("2d");
this.tileMap = new Image();
this.tileMap.src = "img/tileMap.png";
this.mapGame = [];
this.FPS = 50;
this.weapons = [
{ idWeapon: 2, name: "watermelon", power: 15 },
{ idWeapon: 3, name: "eggplant", power: 20 },
{ idWeapon: 4, name: "pumkin", power: 25 },
{ idWeapon: 5, name: "broccoli", power: 30 }
];
}
}
The object array isn´t inside the code, I pasted like and example to add more information
Don´t worry by the sintaxis, I am breaking my class in small pieces to don´t paste all the code.
What I want to do, is: 1. find de the objecto I need, It is alredy done, checking the idWeapon
My main problem it is, how to extract the object and hosted in a variable and later if I find an other weapon, let the current weapon and take the new one
I'm trying to create a board game and would like to instantiate the class Human based on a number of times provided by the user. Obviously I'm trying to assign a different ID per object and the following loop doesn't work in order to instantiate the number of players:
var question = prompt('how many players');
var numOfPlayers = parseInt(question);
class Human {
constructor (id) {
this.id = id;
this.health = 100;
this.hammer = false
this.knife = false;
this.sword = false;
this.baseballbat = false;
this.damage = 0;
this.location = {
x: Math.floor(Math.random() * 8),
y: Math.floor(Math.random() * 8)
}
}
moveTo(x, y){
this.location.x += x;
this.location.y += y;
}
}
var i;
for (i = 0; i < numOfPlayers; i++) {
const player = new Human(id = i);
}
Firstly, I hope I have understood what you are trying to achieve here. The scope of the "const player" is limited within the loop. If you want to be able to access it outside the loop you need to declare a list/array likewise.
Code may go like this for the same:
var players = [];
for(let i = 0; i < numOfPlayers; i++) {
players.push(new Human(i));
}
Note: If you don't want to use variable 'i' outside the loop you can declare it inside 'for' using 'let' keyword as can be seen in the code above.
class Human {
constructor (id){
this.id = id;
this.health = 100;
this.hammer = false
this.knife = false;
this.sword = false;
this.baseballbat = false;
this.damage = 0;
this.location = {
x:Math.floor(Math.random()*8),
y:Math.floor(Math.random()*8)
}
console.log(`Human created with id of ${id}`); //Remove this just to show you that your class is being instantiated for each 'player'
}
moveTo(x,y){
this.location.x += x;
this.location.y += y;
}
}
let numOfPlayers = prompt('How many players?');
const _init = () => {
if(parseInt(numOfPlayers) > 0) {
for (let i = 0; i < numOfPlayers; i++) {
new Human(i)
}
}
}
_init();
I'm building a tetris-like game, where in stead of removing just one line when you've got a full line I remove all the connected pieces. This has got me stumped on the hard-drop after clearing the pieces.
See this example for a quick and dirty version of what I'm trying to do.
function Board (width, height) {
this.width = width;
this.height = height;
this.board = [];
this.pieces = [];
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
if (!this.board[y]) {
this.board[y] = [];
}
this.board[y][x] = null;
}
}
this.canPlace = function(piece, at) {
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
if ((y+at.y >= this.height) || this.board[y+at.y][x+at.x]) {
return false;
}
}
}
return true;
}
this.hasFullLine = function(line) {
for (var x = 0; x < this.width; x++) {
if (!this.board[line][x]) {
return false;
}
}
return true;
}
this.place = function(piece) {
var position = piece.getPosition();
var shape = piece.getShape();
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
if (shape[y][x]) {
this.board[y+position.y][x+position.x] = piece;
}
}
}
if (this.pieces.indexOf(piece) === -1) {
this.pieces.push(piece);
}
piece.render();
}
this.hardDropPieces = function() {
var pieces = this.pieces.slice();
pieces = pieces.sort(function(a,b) {
var aBottom = a.getPosition().y+a.getHeight();
var bBottom = b.getPosition().y+b.getHeight();
return bBottom-aBottom;
});
for (var i = 0; i < pieces.length; i++) {
this.hardDrop(pieces[i]);
}
}
this.hardDrop = function(piece) {
var position = piece.getPosition();
this.clearArea(piece);
while(this.canPlace(piece, {x: piece.getPosition().x, y: piece.getPosition().y+1})) {
piece.setPosition(piece.getPosition().x, piece.getPosition().y+1);
}
this.place(piece);
}
this.clearArea = function(piece) {
var position = piece.getPosition();
var shape = piece.getShape();
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
if (shape[y][x]) {
this.board[y+position.y][x+position.x] = null;
}
}
}
}
this.remove = function(piece) {
this.clearArea(piece);
this.pieces.splice(this.pieces.indexOf(piece),1);
}
this.clearPiecesOnLine = function(line) {
var piecesToClear = [];
for (var x = 0; x < this.width; x++) {
var piece = this.board[line][x];
if (piecesToClear.indexOf(piece) === -1) {
piecesToClear.push(piece);
}
}
for (var i = 0; i < piecesToClear.length; i++) {
this.remove(piecesToClear[i]);
}
return piecesToClear;
}
this.toString = function() {
var str = "";
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
str += this.board[y][x] ? "1" : "0";
}
str += "\n";
}
return str;
}
}
function Piece (shape, fill, stroke, paper, cellWidth) {
this.shape = shape;
this.fill = fill;
this.stroke = stroke;
this.cellWidth = cellWidth;
this.svgGroup = paper.g().append();
this.position = {x:0, y:0};
this.width = this.shape[0].length;
this.height = this.shape.length;
this.removed = false;
for (var y = 0; y < this.height; y++) {
for (var x = 0; x < this.width; x++) {
if (this.shape[y][x]) {
var rect = paper.rect(x*cellWidth, y*cellWidth, cellWidth, cellWidth);
rect.attr({
fill: this.fill,
stroke: this.stroke
});
rect.appendTo(this.svgGroup);
}
}
}
this.setPosition = function(x, y) {
this.position.x = x;
this.position.y = y;
}
this.getPosition = function() {
return this.position;
}
this.render = function() {
var matrix = new Snap.Matrix();
matrix.translate(this.position.x*cellWidth, this.position.y*cellWidth);
this.svgGroup.attr({
transform: matrix
});
}
this.getWidth = function() {
return this.width;
}
this.getHeight = function() {
return this.height;
}
this.getShape = function() {
return this.shape;
}
this.delete = function() {
this.svgGroup.remove();
}
this.isRemoved = function() {
return this.removed;
}
}
var shapes = [
[
[0,1,0],
[1,1,1]
],
[
[1,1,1,1]
],
[
[1,1,1],
[0,1,0],
[1,1,1]
],
[
[1,1],
[1,1]
],
[
[1,1,1],
[0,1,1],
[0,1,1],
[1,1,1]
],
[
[1,1,1,1],
[1,1,1,1],
[1,1,1,1],
[1,1,1,1]
],
[
[1,0,1],
[1,1,1]
]
];
var width = 10;
var height = 20;
var cellWidth = 20;
var paper = Snap("#svg");
var board = new Board(width, height);
var tick = 500;
paper.attr({
width: cellWidth*width,
height: cellWidth*height
});
for (var x = 0; x < width; x++) {
for (var y = 0; y < height; y++) {
var rect = paper.rect(x*cellWidth, y*cellWidth, cellWidth, cellWidth);
rect.attr({
fill: "#ccc",
stroke: "#ddd"
});
}
}
var piece = new Piece(shapes[0], "red", "white", paper, cellWidth);
piece.setPosition(0, 18);
board.place(piece);
piece = new Piece(shapes[1], "orange", "white", paper, cellWidth);
piece.setPosition(3, 19);
board.place(piece);
piece = new Piece(shapes[2], "yellow", "white", paper, cellWidth);
piece.setPosition(2, 8);
board.place(piece);
piece = new Piece(shapes[3], "green", "white", paper, cellWidth);
piece.setPosition(0, 17);
board.place(piece);
piece = new Piece(shapes[4], "blue", "white", paper, cellWidth);
piece.setPosition(2, 15);
board.place(piece);
piece = new Piece(shapes[5], "indigo", "white", paper, cellWidth);
piece.setPosition(1, 11);
board.place(piece);
piece = new Piece(shapes[6], "violet", "white", paper, cellWidth);
piece.setPosition(7, 17);
piece.render();
function update() {
if (piece.isRemoved()) {
return;
}
var position = piece.getPosition();
if (board.canPlace(piece, {x:position.x,y:position.y+1})) {
piece.setPosition(position.x,position.y+1);
board.place(piece);
for (var y = 0; y < piece.getHeight(); y++) {
if (board.hasFullLine(piece.getPosition().y+y)) {
var removed = board.clearPiecesOnLine(piece.getPosition().y+y);
setTimeout(function() {
for (var i = 0; i < removed.length; i++) {
removed[i].delete();
}
board.hardDropPieces();
},tick);
}
}
}
}
setTimeout(update, tick);
That's pretty much the gist of the Board-logic. Placed pieces kept by reference in an array, after clearing I sort the pieces that are not removed by their lowest point and then drop each one of them as far as they can go.
This works when no pieces are interconnected, but I just can't figure out how to do it when they are, as in this example.
Obviously, the blue piece is the lowest point, but it cannot move downards since the green piece is inside of it. I thought about merging them and dropping them, but that leads to other problems. Like what would happen in this case?
I'm pretty sure I'm just being thick, and there's a relatively easy way of fixing this...? Any help would be much appreciated!
All the pieces are automatically generated, and there's way too many, and more could be added any time, to not make a general solution.
I found two parts that had some missing logic. The first part was where you were performing the drops. You'll need to do it one step at a time for each block, and then keep doing it until you can drop no more. Like this
this.hardDropPieces = function() {
var pieces = this.pieces.slice();
pieces = pieces.sort(function(a,b) {
var aBottom = a.getPosition().y+a.getHeight();
var bBottom = b.getPosition().y+b.getHeight();
return bBottom-aBottom;
});
var canStillDrop = true;
while (canStillDrop) { // Keep going until we can't drop no more
canStillDrop = false;
for (var i = 0; i < pieces.length; i++) {
canStillDrop = this.hardDrop(pieces[i]) ? true : canStillDrop;
}
}
}
this.hardDrop = function(piece) {
var didDrop = false;
var position = piece.getPosition();
this.clearArea(piece);
if(this.canPlace(piece, {x: position.x, y: position.y+1})) {
piece.setPosition(position.x, position.y+1);
didDrop = true; // Oh, I see we have dropped
}
this.place(piece);
return didDrop; // Did we drop a spot? Then we should keep going
}
The second part is that you could use a little recursiveness to check if any of the tiles keeping you from dropping is actually connected to the floor. This one you already recognize:
this.canPlace = function(piece, at) {
// Will it fall below the floor? Then it's a no-go
if (piece.getHeight()+at.y > this.height) {
return false;
}
// Loop through shape
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
// Ignore non-shape positions
if (!piece.shape[y][x]) continue;
// Get piece at current shape position
var pieceAtPos = this.board[y+at.y][x+at.x];
// Is the piece (or any that it's resting on) connected to the floor?
if (pieceAtPos && pieceAtPos!==piece && this.isPieceGrounded(pieceAtPos, [piece]) ){
return false;
}
}
}
return true;
}
But say hello also to isPieceGrounded.
this.isPieceGrounded = function(piece, testedPieces) {
// Check all positions BELOW the piece
var at = { x: piece.getPosition().x, y: piece.getPosition().y+1 };
// Is it connected to the floor?
if (piece.getHeight()+at.y+1 >= this.height) {
return true;
}
// *Sigh* Loop through THIS whole piece
for (var y = 0; y < piece.getHeight(); y++) {
for (var x = 0; x < piece.getWidth(); x++) {
if (!piece.shape[y][x]) continue;
var pieceAtPos = this.board[y+at.y][x+at.x];
if (pieceAtPos && pieceAtPos!==piece && testedPieces.indexOf(pieceAtPos) < 0) {
// Keep a list of all tested pieces so we don't end up in an infinite loop by testing them back and forth
testedPieces.push(pieceAtPos);
// Let's test that one and all its connected ones as well
if (this.isPieceGrounded(pieceAtPos, testedPieces)) {
return true;
};
}
}
}
return false;
}
http://jsfiddle.net/971yvc8r/2/
I'm sure there are lots of different solutions, but I think something like this might be the most efficient.