ID:1308389
 
(See the best response by Koriami.)
Problem description: I'm at a complete loss with this, especially working out which objects to move. The idea is that blocks move down into empty spaces. Also, when a column is empty, all the blocks to the right of said column shift left to fill it up (leaving the empty columns on the right).

I'd prefer to move them pixel by pixel to give a better "falling and sliding" effect.

Some images to help illustrate what I'm trying to do:


Which then becomes this:


I currently have my blocks falling by calling this proc every time blocks are removed from play.
obj/step_size = 4

proc/Slide()
for(var/obj/Block/B in world)
walk(B,SOUTH)
Do you want them to fall in both directions at the same time? Or move left and then down?
I want them to fall down first, and only move left to occupy the empty columns. What I've tried so far keeps moving the blocks left until they hit another block.

The images should give a good idea of what I'm trying to do.
Best response
Why not make it so that when an object is deleted it checks if there are any other objects in that column, and then moves everything that is to the right 1 square to the left and then down?
obj/Block
Del()
var/Found = 0
for(var/obj/Block/B in world)
if(B.step_x == step_x)//Assuming that they will always have the same value if they are in the same column
Found = 1
break
if(!Found)
for(var/obj/Block/B in world)
if(B.step_x > step_x)
spawn(10) B.StepLeft(1) //Wait to move the blocks until this one deletes.
..()
proc
StepLeft(var/Squares)
Steps = (Squares*32)/4 //Number of times to step for a smooth glide
while(Steps)
step(src, WEST)
sleep(1)

This is just off the top of my head, but the basic concept is there even if this code doesn't function properly. After that you can cause them to move south.
That's not at all what I'm looking for. I think my 'falling' movement is interfering with my attempts to accomplish this so far.
obj/Block
density=1
New()
..()
spawn(10)
walk(src,SOUTH)
//Now my blocks are always falling down

I was using Bump() to stop my blocks from walking when they hit another, however that caused problems with them hitting the block below before it had the chance to fall.

My latest method of moving the columns of blocks to the left once one column is empty leaves a few stragglers behind.
proc/CheckCols()
var/list/cols = list()

for(var/obj/Block/B in world)
cols |= B.x //This has been a big help, thanks Kaiochao
for(var/i=1,i<=10,i++)
if(i in cols) continue
for(var/obj/Block/Bl in world)
if(Bl.x>i)
var/steps=7
while(steps)
step(Bl,WEST)
steps--
Oh nevermind.


I see the problem, you should just loop to check to see if there is null at any point along the y = 1 level, and move everything above that x to the left one.

I think that's what you want to accomplish anyway.
That's precisely what I am doing, however it's just not working properly.
proc/CheckCols()
//A List to hold all columns with blocks in them
var/list/cols = list()
//Loop through all the blocks in the world (100 at most)
for(var/obj/Block/B in world)
//Add the Blocks X Value to the list if it isn't already there
cols |= B.x
for(var/i=1,i<=10,i++)
//Check if var/i is a column with blocks in it
if(i in cols) continue
//if not, another for loop
for(var/obj/Block/Bl in world)
//If the Blocks x value is greater than var/i
//i.e It is to the right of the empty column
if(Bl.x>i)
//7 steps make up one tile movement
var/steps=7
while(steps)
//move left
step(Bl,WEST)
steps--
Alright, I've made much more progress. I have my columns moving over just fine now, and I'm left only with one issue. I need a better way of having my blocks 'fall'. I then need to call CheckCols() only once all the blocks have fallen down.
proc/Slide()
for(var/obj/Block/B in world)
walk(B,SOUTH) //this is no good
//need to ensure all blocks have fallen before calling
//CheckCols(), spawn leaves too much delay in a fast paced game.
spawn(20)
CheckCols()


proc/CheckCols()
for(var/i=1,i<10,i++)
var/list/cols = list()
for(var/obj/Block/B in world)
cols |= B.x
if(i in cols) continue
for(var/obj/Block/Bl in world)
if(Bl.x==i+1)
var/steps=7
while(steps)
step(Bl,WEST)
steps--
Try a loop?

As you create new blocks, add them to a list- have the loop constantly running and moving an blocks with in that before mentioned list to move down- upon landing remove them from the list.
I think I see what you're saying, but this is working out to be very 'laggy'. Perhaps I'm doing something wrong?
var/list/fall = list()

obj/Block
density=1
Bump()
fall -= src

proc/Destroy()
//do stuff
Slide()

proc/Slide()
for(var/obj/Block/B in world)
fall += B
while(fall.len>=1)
for(var/obj/Block/Bl in fall)
step(Bl,SOUTH)
sleep(world.tick_lag)
That sleep should be one indent to the left, otherwise you're pausing for every block?
Ah yeah, that helps a lot. Now for some reason, the list 'fall' is never completely emptied, so it is constantly running and stacking up.
Ok, I've just about got this sorted. The only issue now is that my bottom row of blocks are not acutally Bump()'ing into anything but the map edge, so they are not removed from my list.

Is there any way to detect a Bump against the map edge or should I add an invisible layer of blocks to the bottom?
I'd say make an invisible layer of blocks to the bottom.
You could put under for(var/obj/Block/Bl in fall) if(B1.y==0) fall -= src.