Wrapper fuer ReceiveMsg()

Da die Wrapper im /std/room thematisch nirgendwo hin passten, wurde
/std/room/comm.c neu erstellt und ReceiveMsg() aus /std/room/items.c
dort hin verlegt.

Die lfun _notify() ist durch SendNotify() ueberfluessig und wurde
ersetzt.

Change-Id: Ia42d131228963cbf8023f6b1563c38f65a38bd63
diff --git a/doc/sphinx/lfun/ReceiveMsg.rst b/doc/sphinx/lfun/ReceiveMsg.rst
index 457235b..1734eb0 100644
--- a/doc/sphinx/lfun/ReceiveMsg.rst
+++ b/doc/sphinx/lfun/ReceiveMsg.rst
@@ -66,7 +66,9 @@
     In diesem Fall ist der Rueckgabe der kleinste von allen gerufenen
     ReceiveMsg() zurueckgelieferte Wert.
 
-    
+  Zur einfacheren Verwendung gibt es einige Wrapper-Funktionen, siehe
+  :doc:`ReceiveMultisense`, :doc:`ReceiveNotify`, :doc:`ReceiveSay` und
+  :doc:`ReceiveTeilemit`.
 
 RUeCKGABEWERT
 -------------
@@ -274,10 +276,10 @@
 ----------
 ::
 
+    Wrapper-Funktionen: :doc:`ReceiveMultisense`, :doc:`ReceiveNotify`, :doc:`ReceiveSay`, :doc:`ReceiveTeilemit`
     Verwandt: send_room(SE)
     Lfuns:    TestIgnore(L)
     Efuns:    tell_object(E), catch_tell(L), catch_msg(L)
               query_verb(E), query_once_interactive(E), break_string(SE)
 
-13.03.2016, Zesstra
-
+Letzte Aenderung: 07.01.2022
diff --git a/doc/sphinx/lfun/ReceiveMultisense.rst b/doc/sphinx/lfun/ReceiveMultisense.rst
new file mode 100644
index 0000000..cafa5bc
--- /dev/null
+++ b/doc/sphinx/lfun/ReceiveMultisense.rst
@@ -0,0 +1,86 @@
+ReceiveMultisense()
+===================
+
+FUNKTION
+--------
+
+  public int* ReceiveMultisense(struct msg_s* msgs,
+    string action = 0, int commontypes = 0)
+  
+DEFINIERT IN
+------------
+
+  /std/living/comm.c
+  /std/room/comm.c
+  /sys/living/comm.h
+
+ARGUMENTE
+---------
+
+  struct msg_s* msgs
+    Die Auszugebenden Nachrichten mit Zusatzinformationen. (S.u.)
+  string action
+    action wird als action an ReceiveMsg() weitergegeben.
+  int commontypes
+    Muss einen oder mehrere Veroderte messagetypes aus
+    :doc:`ReceiveMsg` enthalten. Diese werden bei jedem Aufruf von
+    :doc:`ReceiveMsg` mit dem jeweiligen Messagetyp aus msgs verodert.
+
+BESCHREIBUNG
+------------
+
+  sendet die in msgs angegebenen Nachrichten an den Empfaenger, bis eine
+  zugestellt werden konnte oder das ende von msgs erreicht wurde.
+
+  msgs ist aufgebaut wie folt:
+  Der struct msg_s ist in /std/living/comm_structs.c definiert. Er hat
+  die Eintraege string msg, int type, string prefix.
+  Das Array wird abgearbeitet, bis eine Nachricht zugestellt werden konnte
+  oder das Ende erreicht ist. msg wird vor der Ausgabe an
+  :doc:`../sefun/replace_personal` uebergeben, als erstes Objekt wird das aufrufende
+  Objekt uebergeben, als zweites this_player(), sofern vorhanden. msg wird
+  automatisch auf 78 Zeichen umgebrochen, vorhandene Zeilenumbrueche werden
+  dabei beibehalten. Fuer prefix findet keine Ersetzung statt.
+
+RUECKGABEWERT
+-------------
+
+  Ein Integer-Array mit folgenden Werten:
+
+  0
+    Der Rueckgabewert von :doc:`../lfun/ReceiveMsg`.
+  1
+    Information, welche Meldung zugestellt wurde. -1 steht fuer keine
+    Meldung, ein wert >= 0 fuer die jeweilige Position in msgs.
+
+  Raeume definieren standardmaessig ebenfalls ein ReceiveMultisense(), welches in
+  jedem Objekt im Raum ReceiveMultisense() mit den uebergebenen Argumenten aufruft.
+  In diesem Fall ist der Rueckgabe der kleinste von allen gerufenen
+  ReceiveMultisense() zurueckgelieferte Wert.
+
+BEISPIEL
+--------
+
+  .. code-block:: pike
+
+  inherit "/std/living/comm_structs";
+  this_player().ReceiveMultisense(
+    ({
+      (<msg_s>
+        msg: "Ein Drache landet neben Dir.",
+        type: MT_LOOK),
+      (<msg_s>
+        message: "Du hoerst ein rumpeln, als waere gerade etwas grosses "
+          "neben Dir eingeschlagen.",
+        type: MT_LISTEN),
+      (<msg_s>
+        msg: "Du spuerst einen heftigen Luftzug.",
+        type: MT_FEEL)})
+    MA_MOVE_IN);
+      
+SIEHE AUCH
+----------
+
+  :doc:`ReceiveMsg`, :doc:`../sefun/replace_personal`
+
+Letzte Aenderung: 07.10.2021, Bugfix
diff --git a/doc/sphinx/lfun/ReceiveNotify.rst b/doc/sphinx/lfun/ReceiveNotify.rst
new file mode 100644
index 0000000..d4171f4
--- /dev/null
+++ b/doc/sphinx/lfun/ReceiveNotify.rst
@@ -0,0 +1,56 @@
+ReceiveNotify()
+===============
+
+FUNKTION
+--------
+
+  public int ReceiveNotify(string msg, string action = 0)
+
+DEFINIERT IN
+------------
+
+  /std/living/comm.c
+  /sys/living/comm.h
+
+ARGUMENTE
+---------
+
+  string msg
+    Die auszugebende Meldung. Sie wird automatisch auf 78 Zeichen umgebrochen,
+    vorhandene Zeilenumbrueche werden dabei beibehalten.
+  string action
+    action wird als action an :doc:`ReceiveMsg` weitergegeben.
+
+BESCHREIBUNG
+------------
+
+  Sendet msg via ReceiveMsg() mit MT_NOTIFICATION | MSG_BS_LEAVE_LFS und den
+  uebergebenen bzw. voreingestellten Werten fuer prefix und action an den
+  Empfaenger.
+
+BEMERKUNGEN
+-----------
+
+  Bitte beachten: MT_NOTIFICATION ist fuer Kommandobestaetigungen,
+  Statusmeldungen o.ae., d.h. out-of-character Meldungen vorgesehen. Es soll
+  nicht als Sammeltyp fuer nicht-ignorierbare Meldungen verwendet werden,
+  dafuer gibt es MSG_DONT_IGNORE in Kombination mit den anderen Typen.
+
+RUECKGABEWERT
+-------------
+
+  Der Rueckgabewert von :doc:`../lfun/ReceiveMsg`.
+
+BEISPIEL
+--------
+
+  .. code-block:: pike
+
+  this_player().ReceiveNotify("Dazu reicht dein Level nicht aus.");
+
+SIEHE AUCH
+----------
+
+  :doc:`ReceiveMsg`
+
+Letzte Aenderung: 07.10.2021, Bugfix
diff --git a/doc/sphinx/lfun/ReceiveSay.rst b/doc/sphinx/lfun/ReceiveSay.rst
new file mode 100644
index 0000000..33ceee1
--- /dev/null
+++ b/doc/sphinx/lfun/ReceiveSay.rst
@@ -0,0 +1,92 @@
+ReceiveSay()
+============
+
+FUNKTION
+--------
+
+  public int* ReceiveSay(string msg, string prefix = 0,
+    struct msg_s sense_blocked =
+      ({(<msg_s>
+        msg: "@WER1 bewegt die Lippen, Du hoerst jedoch nichts.",
+        type: MT_LOOK)}))
+
+DEFINIERT IN
+------------
+
+  /std/living/comm.c
+  /std/room/comm.c
+  /sys/living/comm.h
+  
+ARGUMENTE
+---------
+
+  string msg
+    Die auszugebende Meldung. Sie wird vor der Ausgabe an
+    :doc:`../sefun/replace_personal` uebergeben, als erstes Objekt wird das aufrufende
+    Objekt uebergeben, als zweites this_player(), sofern vorhanden. msg wird
+    automatisch auf 78 Zeichen umgebrochen, vorhandene Zeilenumbrueche werden
+    dabei beibehalten.
+  string prefix
+    prefix wird als prefix an :doc:`ReceiveMsg` weitergegeben. Eine
+    Ersetzung mittels :doc:`replace_personal` findet nicht statt.
+    Wird kein Wert uebergeben, wird
+    previous_object().Name(WER, 1) + " sagt: " verwendet. Hierzu muss Name()
+    am aufrufenden Objekt definiert sein und etwas sinnvolles zurueckgeben.
+  struct msg_s sense_blocked
+    Ersatzmeldungen fuer taube Spieler. enthaelt Structs vom Typ msg_s.
+    Dieser ist in /std/living/comm_structs.c definiert und enthaelt die
+    Eintraege string msg, int type, string prefix.
+    Das Array wird von vorne nach hinten
+    abgearbeitet, bis eine Nachricht zugestellt werden konnte oder das Ende
+    erreicht ist. Auch hier findet fuer msg eine Ersetzung mittels
+    :doc:`../sefun/replace_personal` statt, fuer prefix nicht.
+
+BESCHREIBUNG
+------------
+
+  Sendet msg per :doc:`ReceiveMsg` mit den ueblichen Argumenten von
+  "sage" an den Empfaenger. D.h.: MT_LISTEN, MA_SAY, Name(WER, 1) + " sagt: ". Hierzu
+  muss das aufrufende Objekt :doc:`name` definiert haben und dort
+  etwas sinnvolles zurueckgeben. Falls ein anderer prefix gewaehlt wurde,
+  wird dieser statt Name(WER, 1)  " sagt: " verwendet. 
+
+RUECKGABEWERT
+-------------
+
+  Ein Integer-Array mit folgenden Werten:
+
+  0
+    Der Rueckgabewert von :doc:`../lfun/ReceiveMsg`.
+  1
+    Information, welche Meldung zugestellt wurde. -1 steht fuer msg, ein wert
+    >= 0 fuer die jeweilige Position in sense_blocked.
+
+  Raeume definieren standardmaessig ebenfalls ein ReceiveSay(), welches in
+  jedem Objekt im Raum ReceiveSay() mit den uebergebenen Argumenten aufruft.
+  In diesem Fall ist der Rueckgabe der kleinste von allen gerufenen
+  ReceiveSay() zurueckgelieferte Wert.
+
+BEISPIEL
+--------
+
+  .. code-block:: pike
+
+  SetProp(P_NAME, "Questgeber");
+  SetProp(P_GENDER, MALE);
+  this_player().ReceiveSay(
+    "Das hast Du prima gemacht, @WER2!",
+    Name(WER, 1) + " lobt Dich: ",
+    ({
+      (<message_> 
+        msg: "@WER1 nickt Dir laechelnd zu.",
+        type: MT_LOOK),
+      (<msg_s> 
+        msg: "Jemand klopft Dir lobend auf die Schulter.",
+        type: MT_FEEL)}));
+
+SIEHE AUCH
+----------
+
+  :doc:`ReceiveMsg`, :doc:`../sefun/replace_personal`, :doc:`name`
+
+Letzte Aenderung: 07.10.2021, Bugfix
diff --git a/doc/sphinx/lfun/ReceiveTeilemit.rst b/doc/sphinx/lfun/ReceiveTeilemit.rst
new file mode 100644
index 0000000..46648f3
--- /dev/null
+++ b/doc/sphinx/lfun/ReceiveTeilemit.rst
@@ -0,0 +1,54 @@
+ReceiveTeilemit()
+=================
+
+FUNKTION
+--------
+
+  public int ReceiveTeilemit(string msg)
+  
+DEFINIERT IN
+------------
+
+  /std/living/comm.c
+
+ARGUMENTE
+---------
+
+  string msg
+    Die auszugebende Meldung. Sie wird vor der Ausgabe an
+    :doc:`../sefun/replace_personal` uebergeben, als erstes Objekt wird das aufrufende
+    Objekt uebergeben, als zweites this_player(), sofern vorhanden. msg wird
+    automatisch auf 78 Zeichen umgebrochen, vorhandene Zeilenumbrueche werden
+    dabei beibehalten.
+
+BESCHREIBUNG
+------------
+
+  Sendet msg per :doc:`../lfun/ReceiveMsg` mit den ueblichen Argumenten von
+  "teile mit" an pl. D.h.: MT_COMM | MT_FAR, MA_TELL, Name(WER, 1) + " teilt
+  Dir mit: ". Hierzu muss das aufrufende Objekt :doc:`name` definiert
+  haben und dort etwas sinnvolles zurueckgeben. Der einzige Unterschied zum
+  normalen "teile mit" ist, dass zusaetzlich MSG_DONT_STORE uebergeben wird.
+  Da es sich hier ueblicherweise nicht um Kommunikation zwischen Spielern
+  handelt, muss die Nachricht nicht in die tmhist.
+
+RUECKGABEWERT
+-------------
+
+  Der Rueckgabewert von :doc:`../lfun/ReceiveMsg`.
+
+BEISPIEL
+--------
+
+  .. code-block:: pike
+
+  SetProp(P_NAME, "Questgeber");
+  SetProp(P_GENDER, MALE);
+  this_player().ReceiveTeilemit("Das hast Du prima gemacht, @WER2!");
+
+SIEHE AUCH
+----------
+
+  :doc:`ReceiveMsg`, :doc:`../sefun/replace_personal`, :doc:`name`
+
+Letzte Aenderung: 07.10.2021, Bugfix
diff --git a/std/living/comm.c b/std/living/comm.c
index a2ef665..ccfe92b 100644
--- a/std/living/comm.c
+++ b/std/living/comm.c
@@ -8,8 +8,13 @@
 #pragma no_clone
 #pragma range_check
 
+inherit "/std/living/comm_structs";
+
 #include <defines.h>
+#include <thing/language.h>
+#define NEED_PROTOTYPES
 #include <living/comm.h>
+#undef NEED_PROTOTYPES
 
 void create_super()
 {
@@ -135,3 +140,87 @@
   return MT_LOOK;
 }
 
+// Wrapper fuer ReceiveMsg()
+public int* ReceiveSay(string msg, string prefix = 0,
+  struct msg_s* sense_blocked =
+    ({(<msg_s> 
+      msg: "@WER1 bewegt die Lippen, Du hoerst jedoch nichts.",
+      type: MT_LOOK)}))
+{
+  int* res = ({0, -1});
+
+  if(!prefix)
+  {
+    prefix = previous_object().Name(WER, 1) + " sagt: ";
+  }
+
+  res[0] = ReceiveMsg(
+    replace_personal(msg, ({previous_object(), this_player()}) - ({0}), 1),
+    MT_LISTEN | MSG_BS_LEAVE_LFS,
+    MA_SAY,
+    prefix);
+
+  if(res[0] == MSG_SENSE_BLOCK)
+  {
+    // Um eine Nummer zurueckzugeben, ist hier leider eine for-Schleife noetig.
+    for(int i = 0; i < sizeof(sense_blocked); ++i)
+    {
+      res[0] = ReceiveMsg(
+        replace_personal(sense_blocked[i].msg, ({previous_object(),
+                         this_player()}) - ({0}), 1),
+        sense_blocked[i].type | MSG_BS_LEAVE_LFS,
+        MA_SAY,
+        sense_blocked[i].prefix);
+      if(res[0] != MSG_SENSE_BLOCK)
+      {
+        res[1] = i;
+        break;
+      }
+    }
+  }
+  return res;
+}
+
+public int ReceiveNotify(string msg, string action)
+{
+  // Da MT_NOTIFICATION immer zugestellt wird, ist keine Fehlerbehandlung
+  // notwendig.
+  return ReceiveMsg(
+    msg,
+    // MSG_DONT_IGNORE waere wegen MT_NOTIFICATION inhaltlich nicht noetig,
+    // spart aber ein paar Ticks.
+    MT_NOTIFICATION | MSG_BS_LEAVE_LFS | MSG_DONT_IGNORE,
+    action);
+}
+
+public int ReceiveTeilemit(string msg)
+{
+  // Blockierte Sinne sind hier kein Problem, ergo keine Fehlerbehandlung.
+  return ReceiveMsg(
+    replace_personal(msg, ({previous_object(), this_player()}) - ({0}), 1),
+    MT_COMM | MT_FAR | MSG_BS_LEAVE_LFS | MSG_DONT_STORE,
+    MA_TELL,
+    previous_object().Name(WER, 1) + "teilt Dir mit: ");
+}
+
+public int* ReceiveMultisense(struct msg_s* msgs,
+    string action = 0, int commontypes = 0)
+{
+  int* res = ({0, -1});
+
+  // Um eine Nummer zurueckzugeben, ist hier leider eine for-Schleife noetig.
+  for(int i = 0; i < sizeof(msgs); ++i)
+  {
+    res[0] = ReceiveMsg(
+      replace_personal(msgs[i].msg, ({previous_object(), this_player()}), 1),
+      msgs[i].type | MSG_BS_LEAVE_LFS | commontypes,
+      action,
+      msgs[i].prefix);
+    if(res[0] == MSG_DELIVERED)
+    {
+      res[1] = i;
+      break;
+    }
+  }
+  return res;
+}
diff --git a/std/living/comm_structs.c b/std/living/comm_structs.c
new file mode 100644
index 0000000..5105dbd
--- /dev/null
+++ b/std/living/comm_structs.c
@@ -0,0 +1,9 @@
+#pragma strict_types, save_types, pedantic, range_check
+#pragma no_clone
+
+protected struct msg_s
+{
+  string msg;
+  int type;
+  string prefix;
+};
diff --git a/std/player/base.c b/std/player/base.c
index 2b7d6fb..7f616d0 100644
--- a/std/player/base.c
+++ b/std/player/base.c
@@ -1338,7 +1338,7 @@
       if ( input_segments[1] == "" &&
            implode(input_segments[1..],"") == "" )
       {
-        _notify("Du hast hinter dem : nix eingegeben, bitte nochmal "
+        ReceiveNotify("Du hast hinter dem : nix eingegeben, bitte nochmal "
           "versuchen.\n", MA_UNKNOWN);
         // Eine neue Eingabe wird aber nur angefordert, wenn der aktuelle
         // Input ohnehin schon aus einem input_to()-Durchlauf stammte.
@@ -1384,7 +1384,7 @@
     smart_log(error_type, player_input, obj);
   }
   else
-    _notify("Eingabe abgebrochen.\n", MA_UNKNOWN);
+    ReceiveNotify("Eingabe abgebrochen.\n", MA_UNKNOWN);
   return 1;
 }
 
@@ -1438,12 +1438,12 @@
 {
   if (answer != "j" && answer != "ja")
   {
-    _notify("Eingabe abgebrochen.\n", MA_UNKNOWN);
+    ReceiveNotify("Eingabe abgebrochen.\n", MA_UNKNOWN);
     return;
   }
   if (!obj)
   {
-    _notify(sprintf("Leider existiert das Objekt nicht mehr, an dem Du "
+    ReceiveNotify(sprintf("Leider existiert das Objekt nicht mehr, an dem Du "
             "D%s melden wolltest.", desc), MA_UNKNOWN);
     return;
   }
@@ -1451,7 +1451,7 @@
   // ggf. will das Objekte selber loggen, dann wird nicht zentral geloggt.
   if (obj->SmartLog(0, myname, str, strftime("%d. %b %Y")))
   {
-    _notify(sprintf(
+    ReceiveNotify(sprintf(
        "Du hast an %s erfolgreich %s abgesetzt.\n"
        "Hinweis: Das Objekt selber hat die Meldung protokolliert.",
        (obj->IsRoom() ? "diesem Raum" : obj->name(WEM,1)),desc),
@@ -1461,14 +1461,14 @@
   {
     // Eintragung in die Fehler-DB
     string hashkey = ({string})ERRORD->LogReportedError(err);
-    _notify(sprintf(
+    ReceiveNotify(sprintf(
           "Ein kleiner Fehlerteufel hat D%s an %s unter der ID %s "
           "notiert.", desc,
           (obj->IsRoom() ? "diesem Raum" : obj->name(WEM,1)),
           hashkey || "N/A"),
         MA_UNKNOWN);
   }
-  _notify("Vielen Dank fuer die Hilfe.\n", MA_UNKNOWN);
+  ReceiveNotify("Vielen Dank fuer die Hilfe.\n", MA_UNKNOWN);
 }
 
 /** Loggt eine Spielermeldung an Magier.
@@ -1507,7 +1507,7 @@
       err[F_TYPE]=T_REPORTED_SYNTAX;
       break;
   }
-  _notify(sprintf(
+  ReceiveNotify(sprintf(
           "Du hast %s an %s mit der Beschreibung \"%s\" eingegeben. "
           "Wurde das richtige Zielobjekt ausgewaehlt und moechtest Du "
           "speichern?", desc,
diff --git a/std/player/comm.c b/std/player/comm.c
index 135d10e..a04200c 100644
--- a/std/player/comm.c
+++ b/std/player/comm.c
@@ -107,21 +107,13 @@
   report_cache = 0;
 }
 
-// uebermittelt eine MT_NOTIFICATION an this_object(), welche nicht ignoriert
-// werden kann und auch nicht gespeichert wird.
-protected void _notify(string msg, string action) {
-  ReceiveMsg(msg,
-             MT_NOTIFICATION|MSG_DONT_BUFFER|MSG_DONT_IGNORE|MSG_DONT_STORE,
-             action, 0, this_object());
-}
-
 protected void updates_after_restore(int newflag) {
   // Altes Ignoriere loeschen...
   mixed ign = Query(P_IGNORE,F_VALUE);
   if (!mappingp(ign))
   {
     if (pointerp(ign))
-      _notify(break_string(
+      ReceiveNotify(break_string(
         "Deine Ignoriere-Einstellungen wurden soeben geloescht, "
         "weil es eine Aktualisierung der Ignorierefunktion gab, "
         "bei der eine Konversion der Daten leider nicht "
@@ -980,7 +972,7 @@
   }
 
   beep_interval=({int})QueryProp(P_MESSAGE_BEEP);
-  _notify("Ton bei Mitteilungen: "+
+  ReceiveNotify("Ton bei Mitteilungen: "+
         (beep_interval ? "aller "+beep_interval+" Sekunden." : "aus."),
         query_verb());
   return 1;
@@ -996,7 +988,7 @@
     else return 0;
   }
 
-  _notify("Senderwiederholung bei Mitteilungen: "+ 
+  ReceiveNotify("Senderwiederholung bei Mitteilungen: "+ 
           (({int})QueryProp(P_MESSAGE_PREPEND) ?  "aus" : "ein")+".",
           query_verb());
 
@@ -1015,7 +1007,7 @@
   if(stringp(verb) && verb[0] == '\'') str = verb[1..] + " " + str;
   if (str==""||str==" "||!str)
   {
-    _notify("Was willst Du sagen?",MA_SAY);
+    ReceiveNotify("Was willst Du sagen?",MA_SAY);
     return 1;
   }
   msg=permutate(str);
@@ -1045,7 +1037,7 @@
 
   if (!(str=_unparsed_args()))
   {
-    _notify("Was willst Du rufen?",MA_SHOUT);
+    ReceiveNotify("Was willst Du rufen?",MA_SHOUT);
     return 1;
   }
   chars=sizeof(str)/2;
@@ -1073,15 +1065,15 @@
   if(!IS_LEARNER(this_player()))
   {
     if(QueryProp(P_GHOST)) {
-        _notify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
+        ReceiveNotify("So ganz ohne Koerper bekommst Du keinen Ton heraus.",
                 MA_SHOUT);
         return 1;
     }
     if (QueryProp(P_SP) <(chars+20))
     {
-      _notify("Du musst erst wieder magische Kraefte sammeln.",
+      ReceiveNotify("Du musst erst wieder magische Kraefte sammeln.",
               MA_SHOUT);
-      _notify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
+      ReceiveNotify("Tip: Benutz doch mal die Ebenen (Hilfe dazu mit 'hilfe "
               "Ebenen').", MA_SHOUT);
       return 1;
     }
@@ -1112,7 +1104,7 @@
 
   if (extern_call() && this_interactive()!=ME) return 1;
   if (!who || !msg) {
-    _notify("Was willst Du mitteilen?",MA_TELL);
+    ReceiveNotify("Was willst Du mitteilen?",MA_TELL);
     return 1;
   }
 
@@ -1152,7 +1144,7 @@
                                        SENDER:    getuid(ME),
                                        DATA:     msg ]), 1))
       {
-        _notify(ret, MA_TELL);
+        ReceiveNotify(ret, MA_TELL);
       }
       else
       {
@@ -1173,15 +1165,15 @@
     if (!stringp(it))
       switch(it) {
       case -1:
-        _notify("Das war nicht eindeutig!",MA_TELL);
+        ReceiveNotify("Das war nicht eindeutig!",MA_TELL);
         return 1;
       case -2:
-        _notify("Kein solcher Spieler!",MA_TELL);
+        ReceiveNotify("Kein solcher Spieler!",MA_TELL);
         return 1;
       }
     ob = find_player(it) || find_living(it);
     if(!ob) {
-      _notify("Kein solcher Spieler!",MA_TELL);
+      ReceiveNotify("Kein solcher Spieler!",MA_TELL);
       return 1;
     }
   }
@@ -1207,7 +1199,7 @@
     _recv(ob, msg, MSGFLAG_TELL, "Du teilst " + capitalize(it) + " mit: ");
   // oder irgendwas anderes an den Absender ausgeben...
   if (!visflag && interactive(ob))
-      _notify("Kein solcher Spieler!",MA_TELL);
+      ReceiveNotify("Kein solcher Spieler!",MA_TELL);
   else if (away = ({string})ob->QueryProp(P_AWAY))
       ReceiveMsg( break_string( away, 78, capitalize(it)
                            + " ist gerade nicht da: ", BS_INDENT_ONCE ),
@@ -1220,7 +1212,7 @@
       else
         away=time2string("%h %H und %m %M",i);
 
-      _notify(sprintf("%s ist seit %s voellig untaetig.",
+      ReceiveNotify(sprintf("%s ist seit %s voellig untaetig.",
               capitalize(it),away),
               MA_TELL);
     }
@@ -1269,12 +1261,12 @@
   if (!(str=_unparsed_args()) ||
        (sscanf(str, "%s zu %s", who, msg) != 2 &&
         sscanf(str, "%s %s", who, msg) !=2 )) {
-    _notify("Was willst Du wem zufluestern?",MA_SAY);
+    ReceiveNotify("Was willst Du wem zufluestern?",MA_SAY);
     return 1;
   }
   who = lower_case(who);
   if (!(ob = present(who, environment(this_player()))) || !living(ob)) {
-    _notify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
+    ReceiveNotify(capitalize(who)+" ist nicht in diesem Raum.",MA_SAY);
     return 1;
   }
 
@@ -1308,7 +1300,7 @@
   if (!(str=_unparsed_args()) ||
        (sscanf(str, "%s zu %s", who, msg) != 2 &&
         sscanf(str, "%s %s", who, msg) !=2 )) {
-    _notify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
+    ReceiveNotify("Was willst Du wem aus der Ferne zufluestern?",MA_EMOTE);
     return 1;
   }
 
@@ -1318,21 +1310,21 @@
     if (!stringp(it))
       switch(it){
       case -1:
-        _notify("Das war nicht eindeutig!",MA_EMOTE);
+        ReceiveNotify("Das war nicht eindeutig!",MA_EMOTE);
         return 1;
       case -2:
-        _notify("Kein solcher Spieler!",MA_EMOTE);
+        ReceiveNotify("Kein solcher Spieler!",MA_EMOTE);
         return 1;
       }
     ob = find_player(it);
     if(!ob) ob = find_living(it);
     if(!ob){
-      _notify("Kein solcher Spieler!",MA_EMOTE);
+      ReceiveNotify("Kein solcher Spieler!",MA_EMOTE);
       return 1;
     }
   }
   if (environment(ob) == environment()) {
-    _notify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
+    ReceiveNotify("Wenn jemand neben Dir steht, nimm fluester.",MA_EMOTE);
     return 1;
   }
 
@@ -1347,7 +1339,7 @@
 
   // wenn Empfaenger invis und wir kein Magier , ggf. fakefehler ausgeben.
   if (ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())) {
-    _notify("Kein solcher Spieler!",MA_EMOTE);
+    ReceiveNotify("Kein solcher Spieler!",MA_EMOTE);
     return 1;
   }
   // sonst eigene Meldung via _recv() ausgeben.
@@ -1360,7 +1352,7 @@
 
 static int _converse(string arg)
 {
-  _notify("Mit '**' wird das Gespraech beendet.",MA_SAY);
+  ReceiveNotify("Mit '**' wird das Gespraech beendet.",MA_SAY);
   if (stringp(arg) && strstr(arg, "-s") == 0)
     input_to("_converse_more", INPUT_PROMPT, "]", 1);
   else
@@ -1371,7 +1363,7 @@
 static int _converse_more(mixed str, int silent)
 {
   if (str == "**") {
-    _notify("Ok.",MA_SAY);
+    ReceiveNotify("Ok.",MA_SAY);
     return 0;
   }
 
@@ -1390,7 +1382,7 @@
 
   str = _unparsed_args();
   if (!str||!sizeof(str)) {
-    _notify("Was willst Du den Magiern zurufen?",MA_SHOUT);
+    ReceiveNotify("Was willst Du den Magiern zurufen?",MA_SHOUT);
     return 1;
   }
   // Kontrollzeichen rausfiltern.
@@ -1413,7 +1405,7 @@
     return 0;
 
   if (!(str=_unparsed_args())) {
-    _notify("Was moechtest Du 'echoen'?", 0);
+    ReceiveNotify("Was moechtest Du 'echoen'?", 0);
     return 1;
   }
 
diff --git a/std/player/command.c b/std/player/command.c
index d342e90..30d1bf0 100644
--- a/std/player/command.c
+++ b/std/player/command.c
@@ -1101,27 +1101,27 @@
 {
   if (!stringp(cmd))
   {
-    _notify("Mit diesem Befehl kannst Du mithelfen, Syntaxen im MG zu "
+    ReceiveNotify("Mit diesem Befehl kannst Du mithelfen, Syntaxen im MG zu "
             "verbessern. Wenn Du einverstanden bist, speichern wir "
             "anonym (d.h. ohne Deinen Charnamen), welche Deiner Befehle "
             "erfolgreich und nicht erfolgreich waren. Uebliche "
             "Kommunikationsbefehle werden dabei nicht gespeichert.",
             0);
-    _notify("Mit 'syntaxsammlung ja' kannst Du die Speicherung einschalten, "
+    ReceiveNotify("Mit 'syntaxsammlung ja' kannst Du die Speicherung einschalten, "
             "mit 'syntaxsammlung nein' kannst Du sie ausschalten.",0);
-    _notify("Deine Befehle werden zur Zeit"
+    ReceiveNotify("Deine Befehle werden zur Zeit"
             + (QueryProp("_syntaxdb") ? " " : " NICHT ")
             + "gespeichert.", 0);
   }
   else if (cmd == "ja")
   {
     SetProp("_syntaxdb", 1);
-    _notify("Ab jetzt werden Deine Befehle gespeichert. Vielen Dank!", 0);
+    ReceiveNotify("Ab jetzt werden Deine Befehle gespeichert. Vielen Dank!", 0);
   }
   else
   {
     SetProp("_syntaxdb", 0);
-    _notify("Ab jetzt werden Deine Befehle NICHT gespeichert.", 0);
+    ReceiveNotify("Ab jetzt werden Deine Befehle NICHT gespeichert.", 0);
   }
   return 1;
 }
diff --git a/std/room.c b/std/room.c
index 65332ce..93845ba 100644
--- a/std/room.c
+++ b/std/room.c
@@ -21,6 +21,7 @@
 inherit "/std/room/items";
 inherit "/std/container/vitems";
 inherit "/std/room/doors";
+inherit "/std/room/comm";
 
 #include <thing/properties.h>
 #include <config.h>
diff --git a/std/room/comm.c b/std/room/comm.c
new file mode 100644
index 0000000..3f2516b
--- /dev/null
+++ b/std/room/comm.c
@@ -0,0 +1,52 @@
+#pragma strict_types, save_types, pedantic, range_check
+#pragma no_clone
+
+inherit "/std/living/comm_structs";
+
+#include <thing/language.h>
+
+// Per Default nur an alle Items im Inventar weiterleiten.
+public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
+                              string msg_prefix, object origin)
+{
+  int *res = ({int*})all_inventory()->ReceiveMsg(msg, msg_type, msg_action,
+                                         msg_prefix,
+                                         origin || previous_object());
+  if (sizeof(res))
+    return min(res);
+  return 0;
+}
+
+// Sortierfunktion um das Array mit dem kleinsten Wert auszugeben
+
+private int* _minarr(int** arr)
+{
+  if(!sizeof(arr)) return 0;
+  return sort_array(&arr,
+    function int(int* a, int* b)
+    {
+      return a[0] > b[0];
+    })[0];
+}
+
+// Wrapper fuer ReceiveMsg()
+
+public varargs int* ReceiveSay(string msg, string prefix,
+  struct msg_s sense_blocked)
+{
+  // Das muss schon hier passieren, damit previous_object() stimmt
+  if(!prefix)
+  {
+    prefix = ({string})previous_object().Name(WER, 1) + " sagt: ";
+  }
+
+  int** res = ({int**})all_inventory()->ReceiveSay(msg, prefix, sense_blocked);
+  return _minarr(res);
+}
+
+public varargs int* ReceiveMultisense(struct msg_s msgs,
+  string action, int commontypes)
+{
+  int** res = ({int**})all_inventory()->ReceiveMultisense(msgs, action, commontypes);
+  return _minarr(res);
+}
diff --git a/std/room/items.c b/std/room/items.c
index f799570..734ec9c 100644
--- a/std/room/items.c
+++ b/std/room/items.c
@@ -26,16 +26,3 @@
   if ( !sizeof(inh & users()) )
     remove_multiple(3);
 }
-
-// Per Default nur an alle Items im Inventar weiterleiten.
-public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
-                              string msg_prefix, object origin)
-{
-  int *res = all_inventory()->ReceiveMsg(msg, msg_type, msg_action,
-                                         msg_prefix,
-                                         origin || previous_object());
-  if (sizeof(res))
-    return min(res);
-  return 0;
-}
-
diff --git a/sys/living/comm.h b/sys/living/comm.h
index 83811bb..7cb7388 100644
--- a/sys/living/comm.h
+++ b/sys/living/comm.h
@@ -97,6 +97,7 @@
 
 public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
                               string msg_prefix, object origin);
+public int ReceiveNotify(string msg, string action = 0);
 
 #endif // __LIVING_COMM_H_PROTO__