How can I tell if two Minecraft entities are in the same body of water?
Solution 1:
This question is very difficult. Testing if two blocks are somehow connected is already difficult, two entities being in the same body of water is even harder. I have a solution, but it's not a great one. There are probably solutions that work a lot better.
I'd recommend instead looking back the actual problem that you want this solution for; see if there's another way to solve it without testing if two entities are in the same body of water.
This part is to make sure that a search will actually work and won't break your world.
First, we can easily make sure the entities are actually both in water. If either of them is not, then we can skip everything else as they can't both be in the same body of water if one of them isn't even in any water. Put an AND gate with these two commands:
/execute @e[name=Entity1] ~ ~ ~ /testforblock ~ ~ ~ water
/execute @e[name=Entity2] ~ ~ ~ /testforblock ~ ~ ~ water
Next I'd next check that the entities are at least somewhat near eachother. This is kind of a safeguard; they could still be in the same body of water thousands of blocks away, but it's unlikely and you'd crash the game trying to work it out anyway.
/execute @e[name=Entity1] ~ ~ ~ /testfor @e[name=Entity2,r=R] ~ ~ ~
Change R
out with however far you want the search to go for. This solution is laggy, so you may want to keep it small.
If that succeeds (the entities are both in water, and aren't ridiculously far away from each-other) then it's time to do the actual search that'll work out if they're in the same body of water.
This part is the actual search.
The following commands make both Entity1
and Entity2
summon marker
ArmorStands in every adjacent direction around them in which there is water. The -1 data type means that any type of water (flowing, source, etc.) will work:
/execute @e[name=Entity1] ~ ~ ~ detect ~1 ~ ~ water -1 /summon ArmorStand ~1 ~ ~ {CustomName:"Entity1",Marker:1b,NoGravity:1b}
/execute @e[name=Entity1] ~ ~ ~ detect ~-1 ~ ~ water -1 /summon ArmorStand ~-1 ~ ~ {CustomName:"Entity1",Marker:1b,NoGravity:1b}
/execute @e[name=Entity1] ~ ~ ~ detect ~ ~1 ~ water -1 /summon ArmorStand ~ ~1 ~ {CustomName:"Entity1",Marker:1b,NoGravity:1b}
/execute @e[name=Entity1] ~ ~ ~ detect ~ ~-1 ~ water -1 /summon ArmorStand ~ ~-1 ~ {CustomName:"Entity1",Marker:1b,NoGravity:1b}
/execute @e[name=Entity1] ~ ~ ~ detect ~ ~ ~1 water -1 /summon ArmorStand ~ ~ ~1 {CustomName:"Entity1",Marker:1b,NoGravity:1b}
/execute @e[name=Entity1] ~ ~ ~ detect ~ ~ ~-1 water -1 /summon ArmorStand ~ ~ ~-1 {CustomName:"Entity1",Marker:1b,NoGravity:1b}
/execute @e[name=Entity2] ~ ~ ~ detect ~1 ~ ~ water -1 /summon ArmorStand ~1 ~ ~ {CustomName:"Entity2",Marker:1b,NoGravity:1b}
/execute @e[name=Entity2] ~ ~ ~ detect ~-1 ~ ~ water -1 /summon ArmorStand ~-1 ~ ~ {CustomName:"Entity2",Marker:1b,NoGravity:1b}
/execute @e[name=Entity2] ~ ~ ~ detect ~ ~1 ~ water -1 /summon ArmorStand ~ ~1 ~ {CustomName:"Entity2",Marker:1b,NoGravity:1b}
/execute @e[name=Entity2] ~ ~ ~ detect ~ ~-1 ~ water -1 /summon ArmorStand ~ ~-1 ~ {CustomName:"Entity2",Marker:1b,NoGravity:1b}
/execute @e[name=Entity2] ~ ~ ~ detect ~ ~ ~1 water -1 /summon ArmorStand ~ ~ ~1 {CustomName:"Entity2",Marker:1b,NoGravity:1b}
/execute @e[name=Entity2] ~ ~ ~ detect ~ ~ ~-1 water -1 /summon ArmorStand ~ ~ ~-1 {CustomName:"Entity2",Marker:1b,NoGravity:1b}
Note that all of the new ArmorStands being summoned have the name of their summoner. This means that in the next iteration of the search, each of these ArmorStands will also summon more ArmorStands around them in directions that there is water, and so on. The entity that they come from will also be remembered by their name.
Follow all of that up with:
/execute @e[name=Entity1] ~ ~ ~ /setblock ~ ~ ~ red_sandstone
/execute @e[name=Entity2] ~ ~ ~ /setblock ~ ~ ~ red_sandstone
Any solid block will do fine. This is to mark the water blocks as already having a marker ArmorStand on them, so that the future water detects will not spawn another one on that tile.
Repeat all of this search on a clock that terminates when the search succeeds, or the search has gone on for a certain number (I'd recommend R) of iterations (or it'll keep on going forever if they aren't in the same body of water). This search should look like this for each entity:
To test if the search has succeeded, all you need to do is:
/execute @e[name=Entity1] ~ ~ ~ /testfor @e[r=1,Entity2]
Which will make each Entity1 ArmorStand look for adjacent Entity2 ArmorStands, at which point the two entities have been connected by an adjacent route.
To cleanup the all the red_sandstone and marker ArmorStands you just created for this search, all you need to do is:
/execute @e[name=Entity1] ~ ~ ~ /setblock ~ ~ ~ water
/execute @e[name=Entity2] ~ ~ ~ /setbock ~ ~ ~ water
/kill @e[name=Entity1, type=ArmorStand]
/kill @e[name=Entity2, type=ArmorStand]
And that's it done. This is very messy, but works well from what I've tried. It can detect if entities are connected even through mazes of water.