Combine a few things:
Where possible, draw things at layers under and above the instance layer the player object is on.
(let's call the player layer "Instance_Player")
Now, certain things might need a bit more adjustment as to when the player appears in front/behind it.
I have a VERY simple trick for you that I'm using all the time,
but it requires your layers to be separated by large numbers in their depth. Just go by the millions (or even a higher power of 10 to be sure):
layer depths in room editor
--------------------------------------
top layer = 0
next layer = 1 000 000
next layer = 2 000 000
Instance_Player layer = 3 000 000
next layer = 4 000 000
and so on.....
Simple enough.
Now you just need objects that have a sprite.
Objects that don't move are static, put this in their create event:
Objects than can move, like the player, are dynamic, put this in their step event:
GML:
depthspecific = 0;
depth = layer_get_depth("Instance_Player") - (((y-depthspecific)*room_width) + x);
the variable depthspecific is just a number you can use to finetune (negative or positive) where depthsorting starts in the vertical pixels in the object's sprite.
This will cleanly sort objects from top to bottom, and left to right, causing perfect overlaps.
Really, no datagrids needed, no "trying to get my tilesets juuuust right", no special long code and data sorting needed.
This basically sorts objects by their y value, followed by their x value, in a single number per object.
It creates a single depth number per object, a quite large number, but in it you'll find the y and x values separated by a bunch of zero's, in a way that
equalizing it to their depth makes it work in a neatly sorted way.
1 042 005 for example
that's 1 number for both the x and y coordinates. The "1" million is needed for the number to remain large,
and 042 005 are basically an x and y value, only separated by their power of 10.
Sorting with that? Well there you go: as easy as it can get.