There are two other possibilities I can think of right away:

1. Give the entity action a while loop and check the distance to the player inside of it. If the player is in "communication-range" check if the "communicate-button" is pressed (with key_X not on_X .. X being any key, like w, a, and so on). If that is the case, show your dialogue.
example code (quick 'n dirty)
Code:
action my_npc
{
  while(!player) { wait(1); } // wait for the player pointer to become valid
  
  while(me) // as long as the entities is alive
  {
    if(vec_dist(my.x,player.x) < 200)
    {
      if(key_w)
      {
        while(key_w) { wait(1); } //wait for the key to be released (fail safe)
        show_dialog();
      }
    }
 
    wait(1);
  }
}



2. The second approach uses c_scan and the event function of the entities and might reduce the processing overkill by checking the distance to the player each frame. This will come in handy if you have a huge number of entities that can be talked to by the player.
This would function like:
- the player approaches a character he wants to talk to and faces him
- if the player presses a key, a scan (c_scan) will be performed in a small circle segment to the front of the player, so hopefully the character he wants to talk to is the closest entity return by the scan function
- now the entity has an event function which is triggered by the scan, if all checks are ok (scanning entity is the player etc.) than start the dialogue