Talking to moving NPCs in Minecraft 1.13

Context:

In order to trigger a chat message "from" NPCs - say villagers - when approaching them, I am using an always-active repeat-command block (A), a redstone-signal-sensitive impulse-command block (B) and a redstone comparator between them, pointing from A to B.

enter image description here

Since I want to let the NPCs run free, I can't use absolute coordinates, so what I'm currently doing is:

A: execute at @e[name=Gary] if entity @p[distance=..2]
B: execute at @e[name=Gary] run tellraw @a[distance=..2] "Hey!"

This results in the chat message "Hey!" to appear once for every player with distance 2 blocks or less to an entity named Gary as soon as a player is detected in that space.


Question:

Since it is odd that Gary talks to people in front of him as well as to people behind him, is it possible to alter the commands in a way that the center of the area in which players are detected (and in which players have to be to get the message) is not at exactly Gary's position, but 1 block in front of him?

enter image description here


Deliberations:

  • If Gary always stood in the same spot, it would be easy by using absolute coordinates like this:

    A: execute if entity @p[x=x_,y=y_,z=z_,distance=..2]
    B: tellraw @a[x=x_,y=y_,z=z_,distance=..2] "Hey!"
    

    x_, y_ and z_ being the desired coordinates 1 block in front of Gary.

  • Since version 1.13 it is possible to use caret notation for coordinates in some commands making them relative coordinates considering the command's executor's viewing direction, but it seems that target selector arguments only support absolute coordinates in Java edition.


Solution 1:

You can use the caret notation (^) in execute positioned, which removes the need for the x, y and z target selectors entirely (except in some really niche cases). The command you need is this one:

execute at @e[type=villager] positioned ^ ^ ^2 run tellraw @a[distance=..2] "Hey!"

As a bonus, you can get rid of the comparator (and also prevent that players why enter the radius multiple times get the message multiple times) by putting this command after the first:

execute at @e[type=villager] positioned ^ ^ ^2 run tag @a[distance=..2] add heardVillagerTalk

and modifying the first command to:

execute at @e[type=villager] positioned ^ ^ ^2 run tellraw @a[distance=..2,tag=!heardVillagerTalk] "Hey!"

This will prevent the issue the comparator had that players could walk in and out of the area and get the message every time.


If you're using a state scoreboard in your map, you can of course do the same with that. Let's say that state 0 is before having "heard" the "villager" "talk" and state 1 is afterwards. Make this command the second command in the row:

execute at @e[type=villager] positioned ^ ^ ^2 if entity @a[distance=..2] run scoreboard players set $game state 1

And make both only activate in state 0. This assumes that your state scoreboard is called "state" and the holder "$game".