This is actually not too difficult to implement by using scoreboard magic.
First, set up some scoreboard commands
scoreboard objectives add airborne dummy
scoreboard objectives add fallen dummy
Next, run the following commands on a fast clock (I suggest using a fill clock), in this order.
scoreboard players set @a airborne 0
execute @a ~ ~ ~ detect ~ ~-1 ~ minecraft:air -1 scoreboard players set @p airborne 1
scoreboard players set @a[score_airborne=0] fallen 0
scoreboard players add @a[score_airborne_min=1] fallen 1
The first two will set airborne
to 1 for every player who is not standing on something, i.e. the block beneath him is air
. The third command will reset the fallen
score to 0 for everyone who is not airborne
, the fourth increments the score 20 times a second instead. This means that fallen
is 0 as long as you were standing on something since your last fall.
Now you can select the falling players for teleportation using @a[score_fallen_min=XYZ]
. Replace XYZ
with however many seconds a player has to be above air to be considered "falling", multiplied by 20.
There is an issue with this however: Standing on the edge of a block will make you be considered falling, and I can't think of an elegant solution to this, short of chaining execute detect
commands to check a 3×3 area below the player for blocks.
/execute @a ~ ~ ~ detect ~ ~-1 ~ minecraft:stonebrick 1 /tp @p @e{type=Villager]
When ANY player steps on mossy stonebrick (minecraft:stonebirck 1) it teleports the nearest player to the command block to the villager regardless if he step on the mossy cobble.
The /execute
command modifies the sender of the command to that of its target. Any subsequent selectors in commands being run are going to use the executor as the sender.
This comes into play with sender bias, which forces a target selector to target the entity that ran the command. The command you've presented already uses it. The executing player will be the one to be teleported, so long as they are alive.
The @a
selector is the only target selector capable of targeting dead players. If a player dies while on top of stonebrick, they will teleport the nearest living player to them rather than themselves, because @p
cannot target dead players.
The fix for that in particular is to use the @a
selector while reducing the count (via c
parameter) to 1, which also enforces a sender bias. The command itself is still functional as you need it without having to have changed anything else.
/execute @a ~ ~ ~ detect ~ ~-1 ~ minecraft:stonebrick 1 /tp @a[c=1] @e[type=Villager,c=1]
/execute
is used to change the sender and origin of commands to run. /tp
is being run through the player, not by the command block. @p
will be in reference to the executing player's location, and due to sender bias, @p
will always target that player so long as that player is alive. Your command was originally working fine apart from the dead targeting flaw.
Best Answer
Good question. I have spent a few hours playing with the mechanics of target selectors. I have reread the wiki a few times. I came up with a solution but it does not involve radius.
The problem is the initial target selector:
@a[y=166,r=59]
This will select any player within a 59 block radius of y=166 directly above or below the command block. (If x and z are not specified it defaults to the command execution position) This radius means above and below y=166. When x and z of player are same coordinates as command block this would select anyone from y=107 to y=225.
If you use a volume argument to specify y, it only selects players inside the specified zone that also happens to be within the radius. Which without x and z seems to be only a few cubes directly above/below command block. Also doesn't work.
Also, I don't recommend using the execute command. Technically your command is finding a player based on the above criteria and then teleporting the nearest player(
@p
) to that position. Just use the tp command directly while targeting the player you want to with the target selectorOne solution would be to specify a zone to target. This would use volume arguments instead of the radius argument. It requires a bit more.
X and Z specify one corner of the zone tested.
DX and DZ specify the distance from X and Z respectively. (they are a distance, not a coordinate)
Note: Relative coordinates can not be used in the target selector.
Example:
This will select and teleport any player between (-25,y,-25) and (25,y,25). This could sort of replace a radius of 50. It is not perfect, it seems to select any player above 164 and below 167.
In your case of a player falling off of a structure it will teleport them once they hit y=166.
Also, the command block has to be located in a loaded chunk. You could use a command block per zone of your map to have small targeted zones or you could put the command block in the spawn chunks and use one large zone that covers your entire map area.
Example of the latter:
This would select and teleport any players between (-5000,y,-5000) and (5000,y,5000) at the correct y level.