Ueberarbeitung des Ebenensystems

- Ersatz von mixed durch die korrekten Typen
- diverse Fehler beseitigt
- Code aufgeraeumt und modernisiert

Nach Review:
- kaputten Merge gefixt
- Loeschen von Ebenen: Funktion umbenannt
- Review-Anmerkungen umgesetzt
- Zugriffskontrolle: Funktion check() umbenannt
- Kommentare ergaenzt und aktualisiert
- Zugriffsfunktion access() ueberarbeitet
- write() durch tell_object() ersetzt

Change-Id: Ifb5431954b2e71f5e99fd8ed16cb17660a8aa7da
diff --git a/std/player/channel.c b/std/player/channel.c
index 0078ce9..9167e6a 100644
--- a/std/player/channel.c
+++ b/std/player/channel.c
@@ -24,12 +24,11 @@
 #include <sys_debug.h>
 #include <regexp.h>
 
-#define P_SWAP_CHANNELS  "swap_channels"
 #define P_CHANNEL_SHORT  "short_channels"
 
 #define CHANNELCMDS      "[#@%$&()<>a-zA-Z0-9\\-]"
 
-#define DEFAULT_CHANNELS ({"Abenteuer", "Anfaenger","Grats","Tod", "ZT"})
+#define DEFAULT_CHANNELS ({"Abenteuer", "Anfaenger", "Grats", "Tod", "ZT"})
 #define DEFAULT_SHORTCUTS \
 ([                     \
  "b":"Abenteuer",      \
@@ -51,7 +50,9 @@
  "m":"Magier",         \
 ])
 
-
+// TODO: <shortcut> wird eigentlich nur in getChannel() sinnvoll verwendet
+// Koennte man an sich komplett einsparen und dort wie ueberall sonst auch
+// einfach nur mit P_CHANNEL_SHORT arbeiten.
 private nosave mapping shortcut;
 private nosave int c_status;
 
@@ -64,7 +65,7 @@
   Set(P_STD_CHANNEL, SAVE, F_MODE);
   Set(P_CHANNEL_SHORT, SAVE, F_MODE);
   Set(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
-      + (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
+      + (IS_LEARNER(ME) ? WIZARD_SHORTCUTS : ([])));
 }
 
 static <int|string>** _query_localcmds()
@@ -89,7 +90,7 @@
   string* err;
   if (closurep(cl))
   {
-    err = filter(QueryProp(P_CHANNELS), cl, this_object());
+    err = filter(QueryProp(P_CHANNELS), cl, ME);
   }
   if (QueryProp(P_LEVEL) < 5)
     return err;
@@ -97,7 +98,7 @@
   // CHMASTER->new() gibt bei Erfolg 0 zurueck, d.h. es bleiben
   // alle Elemente erhalten, deren Channel nicht erstellt werden konnten.
   // Die werden an die Aufrufer zurueckgegeben.
-  return filter(err, CHMASTER, "new", this_object());
+  return filter(err, "new", CHMASTER, ME);
 }
 
 string* RemoveChannels()
@@ -116,22 +117,38 @@
   closure cl = symbol_function("leave", CHMASTER);
   if (closurep(cl))
   {
-    err = filter(QueryProp(P_CHANNELS), cl, this_object());
+    err = filter(QueryProp(P_CHANNELS), cl, ME);
     SetProp(P_CHANNELS, QueryProp(P_CHANNELS) - err);
   }
   return err;
 }
 
-varargs private string getName(string|object|closure x, int fall) {
-  string|object o = closurep(x) ? query_closure_object(x) : x;
-  if (stringp(o) && sizeof(o) && (x = find_object(o)))
-    o = x;
+varargs private string getName(string|object|closure who, int fall) {
+  // Objekt zur Closure raussuchen. <o> ist danach entweder jenes Objekt,
+  // oder aber <who> selbst, das Objekt oder String sein kann.
+  //
+  // query_closure_object() KANN auch 0 oder -1 zurueckgeben. In beiden
+  // Faellen fuehrt das hier zu einem Laufzeit-Typfehler, weil ein Integer
+  // bzw. die urspruengliche Closure zugewiesen wird. Diesen Fall abzufangen
+  // und separat zu behandeln, hilft nicht, weil es auch keine alternative
+  // Moeglichkeit gibt, <who> in verwendbare Daten umzuwandeln, denn das ist
+  // ja offenbar eine kaputte Closure. Dieser Fall ist ausserdem anscheinend
+  // so exotisch, dass wir vorerst auf Absicherungen mittels catch()
+  // verzichten.
+  string|object o = closurep(who) ? query_closure_object(who) : who;
+
+  // Ist es ein String, pruefen wir, ob es vielleicht ein Objektname ist,
+  // indem wir es in ein Objekt umzuwandeln versuchen. Schlaegt das fehl,
+  // schreiben wir <who> wieder zurueck, so dass <o> wieder der urspruengliche
+  // String ist.
+  if (stringp(o) && sizeof(o))
+    o = find_object(o) || who;
 
   // Objekte
   if (objectp(o))
   {
     // Magier sehen unsichtbare nicht nur als "Jemand"
-    if (o->QueryProp(P_INVIS) && IS_LEARNING(this_object()))
+    if (o->QueryProp(P_INVIS) && IS_LEARNING(ME))
       return "("+capitalize(getuid(o))+")";
     // Froesche mit Namen versorgen.
     if (o->QueryProp(P_FROG))
@@ -149,7 +166,7 @@
       if (p != -1)
       {
         // Magier im Magiermodus kriegen den Realnamen, andere nicht.
-        if (IS_LEARNING(this_object()))
+        if (IS_LEARNING(ME))
           return o[1..p-1];
         else
           return o[p+1..];
@@ -204,7 +221,7 @@
       break;
     case MSG_SAY:
     default:
-      string presay=sprintf("[%s:%s] ", channel, sender_name);
+      string presay = sprintf("[%s:%s] ", channel, sender_name);
       channel_message = break_string(message, max(78,sizeof(presay)+10),
                           presay, prepend_indent_flag);
       break;
@@ -230,51 +247,142 @@
   return 0;
 }
 
-private void createList(string n, mixed a, mixed m, mixed l)
+// string n: Channelname
+// <object*|closure|string|object>* a: Channeldaten
+// string* m: P_CHANNELS
+// string* l: Ausgabeliste.
+private void OLDcreateList(string n, <object*|closure|string|object>* a,
+             string* m, string* l)
 {
-  int pos = member(map(m_values(shortcut), #'lower_case/*'*/), n);
+  // Wir suchen das vom Spieler festgelegte Kuerzel fuer die aktuell
+  // bearbeitete Ebene, falls vorhanden.
   string sh = "";
-  if (pos != -1)
-    sh = m_indices(shortcut)[pos];
+  foreach(string sc, string chan : shortcut) {
+    if ( lower_case(chan) == n ) {
+      sh = sc;
+      break;
+    }
+  }
 
-  string* mem=map(a[I_MEMBER],#'getName/*'*/, WER);
-  mem -= ({"<MasteR>"});
   l += ({ sprintf("%-12.12'.'s %c[%-1.1s] %|12.12' 's (%-|3' 'd) %-42.42s\n",
-          a[I_NAME], (member(m, n) != -1 ? '*' : ' '), sh,
-          a[I_MASTER] ?
-          getName(a[I_MASTER]) : getName(a[I_ACCESS]),
-          sizeof(mem),
-          (closurep(a[I_INFO]) && objectp(query_closure_object(a[I_INFO]))) ?
-              funcall(a[I_INFO]) || "- Keine Beschreibung -" :
-              (stringp(a[I_INFO]) ? a[I_INFO] : "- Keine Beschreibung -")
-          ) });
+          a[I_NAME],
+          (member(m, n) != -1
+              ? '*'
+              : ' '),
+          sh,
+          a[I_MASTER]
+              ? getName(a[I_MASTER])
+              : getName(a[I_ACCESS]),
+          sizeof(a[I_MEMBER] - ({ find_object(CHMASTER) })),
+          (closurep(a[I_INFO]) && objectp(query_closure_object(a[I_INFO])))
+              ? funcall(a[I_INFO]) || "- Keine Beschreibung -"
+              : (stringp(a[I_INFO])
+                    ? a[I_INFO]
+                    : "- Keine Beschreibung -")) });
 }
 
-private mixed getChannel(string ch)
+private void createList(mapping ch_list, string* p_channels,
+                           int show_only_subscribed) {
+  // Kopfzeile der Tabelle erstellen.
+  string listhead =
+            sprintf("%-12.12' 's  [A] %|12' 's (%-3' 's) %-42.42s\n",
+                    "Name", "Eigner", "Sp", "Beschreibung");
+  // Linie drunter.
+  listhead += ("-"*78 + "\n");
+
+  // Rest der Daten erstmal in einem Array zusammenstellen, um es
+  // anschliessend sortieren zu koennen.
+  string* entries = ({});
+
+  object* ch_members;
+  string|object ch_master;
+  string ch_real_name, ch_description;
+  closure|string ch_access;
+  closure|string ch_info;
+  string sh;
+
+  foreach(string chname, <object*|closure|string|object>* chdata : ch_list)
+  {
+    // Wenn nur abonnierte Ebenen aufgelistet werden sollen, dann alle
+    // ueberspringen, die nicht in P_CHANNELS stehen.
+    int is_subscribed = (member(p_channels, chname) > -1);
+    if (show_only_subscribed && !is_subscribed)
+      continue;
+
+    ch_members =   chdata[I_MEMBER];
+    ch_master =    chdata[I_MASTER];
+    ch_access =    chdata[I_ACCESS];
+    ch_real_name = chdata[I_NAME];
+    ch_info =      chdata[I_INFO];
+    sh = "";
+
+    // Ist eine Closure als I_INFO eingetragen, zu der es auch ein Objekt
+    // gibt, tragen wir deren Rueckgabewert als Beschreibung ein.
+    if (closurep(ch_info) && objectp(query_closure_object(ch_info))) {
+      ch_description = funcall(ch_info);
+    }
+    // Ist es ein String, wird er unveraendert uebernommen.
+    else if (stringp(ch_info)) {
+      ch_description = ch_info;
+    }
+    // Sollte nirgends etwas eingetragen sein, oder die Closure 0 zurueck-
+    // gegeben haben, gibt es eine Defaultbeschreibung. Man haette die
+    // Variable auch schon mit dem Default initialisieren koennen, der kann
+    // aber bei Rueckgabe von 0 aus der Closure wieder ueberschrieben werden.
+    // Daher passiert das erst hier.
+    ch_description ||= "- Keine Beschreibung -";
+
+    // Wir brauchen noch das vom Spieler festgelegte Kuerzel fuer die aktuell
+    // bearbeitete Ebene, falls vorhanden, um es in die Liste eintragen
+    // zu koennen.
+    foreach(string _sh_cut, string _chan_name : shortcut) {
+      if ( lower_case(_chan_name) == chname ) {
+        sh = _sh_cut;
+        break;
+      }
+    }
+
+    // Jetzt haben wir endlich alle Infos beisammen und koennen die
+    // Eintraege zusammenbauen.
+    entries += ({
+      sprintf("%-12.12'.'s %c[%-1.1s] %|12.12' 's (%-|3' 'd) %-42.42s\n",
+          ch_real_name,
+          is_subscribed ? '*' : ' ',
+          sh,
+          ch_master ? getName(ch_master) : getName(ch_access),
+          sizeof(ch_members - ({ find_object(CHMASTER) })),
+          ch_description
+      ) });
+  }
+
+   // alphabetisch sortieren
+  entries = sort_array(entries, #'>);
+
+  More( listhead +             // Listenkopf
+        implode(entries, "") + // Eintraege
+        "-"*78+"\n" );         // Linie drunter
+}
+
+private string|string* getChannel(string ch)
 {
   if (!sizeof(ch))
     ch = QueryProp(P_STD_CHANNEL);
   if (shortcut && shortcut[ch])
     ch = shortcut[ch];
-  return CHMASTER->find(ch, this_object());
+  return CHMASTER->find(ch, ME);
 }
 
-#ifndef DEBUG
-#define DEBUG(x) if (funcall(symbol_function('find_player),"zesstra"))\
-          tell_object(funcall(symbol_function('find_player),"zesstra"),\
-                  "MDBG: "+x+"\n")
-#endif
-
 int ChannelParser(string args)
 {
-  mixed ch;
+  string|string* ch;
   int pos, type, err;
-  string txt, tmp;
-  string|string* cmd = query_verb();
-  args = _unparsed_args();
+  string tmp;
   notify_fail("Benutzung: -<Ebene>[ ]['|:|;]<Text>\n"
               "           -<Ebene>[+|-|?|!|*]\n"
               "           -?\n");
+
+  args = _unparsed_args(1);
+  string|string* cmd = query_verb();
   if (!cmd && !args)
     return 0;
 
@@ -282,7 +390,7 @@
     args = "";
 
   cmd = cmd[1..];
-  cmd = regexplode(cmd, "^" CHANNELCMDS "*" "([+-]|\\!|\\?|\\*)*")
+  cmd = regexplode(cmd, "^" CHANNELCMDS "*" "([+-]|\\!|\\?|\\*)*");
   if (sizeof(cmd) > 1)
   {
     //z.B. cmd= ({"","allgemein",":testet"})
@@ -297,8 +405,8 @@
       if (pointerp(ch))
       {
         notify_fail("Diese Angabe war nicht eindeutig! "
-                    "Folgende Ebenen passen:\n"
-                    +implode(ch, ", ")+"\n");
+                    "Folgende Ebenen passen:\n"+
+                    implode(ch, ", ")+"\n");
         return 0;
       }
       else if (!ch)
@@ -311,7 +419,7 @@
     if (sizeof(cmd[1])) {
       switch (cmd[1][<1]) {
         case '+':
-          switch (CHMASTER->join(ch, this_object()))
+          switch (CHMASTER->join(ch, ME))
           {
             case E_ACCESS_DENIED:
               notify_fail("Du darfst an die Ebene '"+ch+"' nicht heran.\n");
@@ -322,23 +430,24 @@
             default:
               break;
           }
-          write("Du betrittst die Ebene '"+ch+"'.\n");
+          tell_object(ME,"Du betrittst die Ebene '"+ch+"'.\n");
           if (member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
             SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
           return 1;
 
         case '-':
-          switch (CHMASTER->leave(ch, this_object()))
+          switch (CHMASTER->leave(ch, ME))
           {
             case E_ACCESS_DENIED:
-              write("Du kannst die Ebene '"+ch+"' nicht verlassen.\n");
+              tell_object(ME,"Du kannst die Ebene '"+ch+"' nicht "
+                "verlassen.\n");
               break;
             case E_NOT_MEMBER:
-              write("Wie willst Du eine Ebene verlassen, welche Du nicht "
-                    "betreten hast?\n");
+              tell_object(ME,"Wie willst Du eine Ebene verlassen, welche Du "
+                "nicht betreten hast?\n");
               break;
             default:
-              write("Du verlaesst die Ebene '"+ch+"'.\n");
+              tell_object(ME,"Du verlaesst die Ebene '"+ch+"'.\n");
               SetProp(P_CHANNELS,
                       QueryProp(P_CHANNELS) - ({ lower_case(ch), ch }));
               break;
@@ -347,66 +456,60 @@
 
         case '!':
         case '?':
-          mapping l;
-          if (mappingp(l = CHMASTER->list(this_object())))
+          mapping l = CHMASTER->list(ME);
+          if (mappingp(l))
           {
+            // Es wurde ein Channel angegeben, dessen Infos gefragt sind.
             if (stringp(ch) && sizeof(ch) && pointerp(l[ch = lower_case(ch)]))
             {
-              int c; object o; string n; string *m;
-              m = sort_array(
-                    map(l[ch][I_MEMBER],#'getName/*'*/, WER),
-                  #'>/*'*/);
-              m-=({"<MasteR>"});
-              write(l[ch][I_NAME]+", "+funcall(l[ch][I_INFO])+".\n");
-              write("Du siehst "+((c = sizeof(m)) > 0
-                      ? (c == 1 ? "ein Gesicht" : c+" Gesichter")
-                         : "niemanden")+" auf der Ebene '"
-                    +l[ch][I_NAME]+"':\n");
-              write(break_string(implode(m,", "), 78));
-              write((l[ch][I_MASTER] ?
-                  getName(l[ch][I_MASTER]) : getName(l[ch][I_ACCESS], WER))
-                  +" hat das Sagen auf dieser Ebene.\n");
+              <object*|closure|string|object>* chdata = l[ch];
+              string* m =
+                sort_array(map(chdata[I_MEMBER], #'getName, WER), #'>) -
+                ({ CMNAME });
+
+              string wen;
+              switch(sizeof(m)) {
+                case 1:  wen = "ein Gesicht"; break;
+                case 0:  wen = "niemanden";   break;
+                // TODO: mittels Zahlwoerter-Objekt die Zahl als Text
+                // ausgeben?
+                default: wen = sprintf("%d Gesichter", sizeof(m)); break;
+              }
+
+              tell_object(ME,
+                chdata[I_NAME]+", "+funcall(chdata[I_INFO])+".\n" +
+                "Du siehst "+wen+" auf der Ebene '"+chdata[I_NAME]+"':\n"+
+                break_string(CountUp(m), 78) +
+                (chdata[I_MASTER] ? getName(chdata[I_MASTER])
+                                  : getName(chdata[I_ACCESS], WER))+
+                 " hat das Sagen auf dieser Ebene.\n");
             }
+            // kein Channel angegeben, dann Gesamtliste erzeugen
             else
             {
-              string* list = ({});
-              if (cmd[1][<1] == '!')
-                l -= mkmapping(m_indices(l) - QueryProp(P_CHANNELS));
-              walk_mapping(l, #'createList/*'*/, QueryProp(P_CHANNELS), &list);
-              list = sort_array(list, #'>/*'*/);
-              txt = sprintf("%-12.12' 's  [A] %|12' 's (%-3' 's) %-42.42s\n",
-                            "Name", "Eigner", "Sp", "Beschreibung")
-                  + "-------------------------------------------------------"
-                  + "-----------------------\n"
-                  + implode(list, "");
-              More(txt);
+              // Wenn nur die abonnierten angezeigt werden sollen
+              // (Kommando -!), wird die Bedingung im 3. Argument == 1, so
+              // dass createList() die reduzierte Tabelle erzeugt.
+              createList(l, QueryProp(P_CHANNELS), (cmd[1][<1] == '!'));
             }
           }
           return 1;
 
         case '*':
-          mixed hist = CHMASTER->history(ch, this_object());
+          int|<int|string>** hist = CHMASTER->history(ch, ME);
           if (!pointerp(hist) || !sizeof(hist))
           {
-            write("Es ist keine Geschichte fuer '"+ch+"' verfuegbar.\n");
+            tell_object(ME,"Es ist keine Geschichte fuer '"+ch+
+              "' verfuegbar.\n");
             return 1;
           }
 
-          //(Zesstra) cmd hat offenbar immer 3 Elemente...
-          //bei -all* ({"","all*",""})
-          //bei -all*10 ({"","all*,"10"})
-          //also ist bei -all* amount immer == 0 und es funktioniert eher
-          //zufaellig.
-          /*if(sizeof(cmd) > 2)
-            amount = to_int(cmd[2]);
-          else
-            amount=sizeof(hist);*/
           int amount = to_int(cmd[2]);
           if (amount <= 0 || amount >= sizeof(hist))
             amount = sizeof(hist);
 
-          txt = "Folgendes ist auf '"+ch+"' passiert:\n"
-              + implode(map(hist[<amount..], #'ChannelMessage/*'*/, 1), "");
+          string txt = "Folgendes ist auf '"+ch+"' passiert:\n" +
+                       implode(map(hist[<amount..], #'ChannelMessage, 1), "");
           More(txt);
           return 1;
 
@@ -416,17 +519,20 @@
     }
   }
 
-  if (sizeof(cmd = implode(cmd[2..], "")))
+  cmd = implode(cmd[2..], "");
+  if (sizeof(cmd))
     args = cmd + (sizeof(args) ? " " : "") + args;
 
   // KOntrollchars ausfiltern.
-  args = regreplace(args,"[[:cntrl:]]","",RE_PCRE|RE_GLOBAL);
+  args = regreplace(args, "[[:cntrl:]]", "", RE_PCRE|RE_GLOBAL);
   if (!sizeof(args))
     return 0;
 
   //Wenn cmd leer ist: MSG_SAY
   if (!sizeof(cmd))
+  {
     type = MSG_SAY;
+  }
   else
   {
     switch (cmd[0])
@@ -451,15 +557,16 @@
   if (!ch || !sizeof(ch))
     ch = QueryProp(P_STD_CHANNEL);
 
-  err = CHMASTER->send(ch, this_object(), args, type);
+  err = CHMASTER->send(ch, ME, args, type);
   if (err < 0)
   {
-    err = CHMASTER->join(ch, this_object());
+    err = CHMASTER->join(ch, ME);
     if (!err)
     {
-      if (member(QueryProp(P_CHANNELS), ch = lower_case(ch)) == -1)
+      ch = lower_case(ch);
+      if (member(QueryProp(P_CHANNELS), ch) == -1)
         SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ ch }));
-      err = CHMASTER->send(ch, this_object(), args, type);
+      err = CHMASTER->send(ch, ME, args, type);
     }
   }
 
@@ -479,8 +586,8 @@
 {
   args = _unparsed_args();
 
-  string n, descr, sh, cn;
-  mixed tmp;
+  string target_channel, descr, _sh_cut;
+  string|string* chans;
   notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"
               "           ebene <Abkuerzung>=\n"
               "           ebene abkuerzungen [standard]\n"
@@ -489,123 +596,157 @@
               +(QueryProp(P_LEVEL) >= 5 ?
                 "           ebene neu <Name> <Bezeichnung>\n"
                 "           ebene beschreibung <Name> <Beschreibung>\n" : "")
-              +(IS_ARCH(this_object()) ?
+              +(IS_ARCH(ME) ?
                 "           ebene kill <Name>\n"
                 "           ebene clear <Name>\n": ""));
 
   if (!args || !sizeof(args))
     return 0;
 
-  if (sscanf(args, "kill %s", n) && IS_ARCH(this_object()))
+  if (sscanf(args, "kill %s", target_channel) && IS_ARCH(ME))
   {
-    cn = CHMASTER->find(n, this_object());
-    if (!cn)
-      cn = n;
+    chans = CHMASTER->find(target_channel, ME);
 
-    if (CHMASTER->remove(cn, this_object()) == E_ACCESS_DENIED) {
-      notify_fail("Die Ebene '"+cn+"' liess sich nicht entfernen!\n");
+    if (!chans)
+    {
+      notify_fail("Ebene '"+target_channel+"' nicht gefunden!\n");
       return 0;
     }
-
-    write("Du entfernst die Ebene '"+cn+"'.\n");
-    return 1;
-  }
-
-  if (sscanf(args, "clear %s", n) && IS_ARCH(this_object()))
-  {
-    cn = CHMASTER->find(n, this_object());
-    if (!cn)
-      cn = n;
-
-    if (CHMASTER->clear_history(cn, this_object()) == E_ACCESS_DENIED) {
-      notify_fail("Der Verlauf zur Ebene '"+cn+"' liess sich nicht "
+    else if (pointerp(chans))
+    {
+      notify_fail(
+        "Das war keine eindeutige Angabe! Folgende Ebenen passen:\n"+
+        break_string(CountUp(chans), 78));
+      return 0;
+    }
+    else if (CHMASTER->remove_channel(target_channel, ME))
+    {
+      notify_fail("Die Ebene '"+target_channel+"' lies sich nicht "
         "entfernen!\n");
       return 0;
     }
 
-    write("Du entfernst den Verlauf zur Ebene '"+cn+"'.\n");
+    tell_object(ME,"Du entfernst die Ebene '"+target_channel+"'.\n");
     return 1;
   }
 
-  if (sscanf(args, "neu %s %s", n, descr) == 2)
+  if (sscanf(args, "clear %s", target_channel) && IS_ARCH(ME))
   {
+    chans = CHMASTER->find(target_channel, ME);
+
+    if (!chans)
+    {
+      notify_fail("Ebene '"+target_channel+"' nicht gefunden!\n");
+      return 0;
+    }
+    else if (pointerp(chans))
+    {
+      notify_fail(
+        "Das war keine eindeutige Angabe! Folgende Ebenen passen:\n"+
+        break_string(CountUp(chans), 78));
+      return 0;
+    }
+    else if (CHMASTER->clear_history(target_channel, ME) == E_ACCESS_DENIED)
+    {
+      notify_fail("Der Verlauf zur Ebene '"+target_channel+"' liess sich "
+                  "nicht entfernen!\n");
+      return 0;
+    }
+
+    tell_object(ME,"Du entfernst den Verlauf zur Ebene '"+target_channel+
+      "'.\n");
+    return 1;
+  }
+
+  if (sscanf(args, "neu %s %s", target_channel, descr) == 2)
+  {
+    notify_fail(break_string("Neue Ebenen kannst Du erst erstellen, wenn "
+      "Du Spielerstufe 5 erreicht hast.", 78));
     if (QueryProp(P_LEVEL) < 5)
-    {
-      notify_fail("Neue Ebenen zu erstellen, ist Dir verwehrt.\n");
       return 0;
-    }
 
-    if (!sizeof(regexp(({ n }), "^" CHANNELCMDS CHANNELCMDS "*")))
-    {
-      notify_fail("Der Name '"+n+"' ist nicht konform!\n");
+    notify_fail("Der Name '"+target_channel+"' ist nicht konform!\n");
+    if (!sizeof(regexp(({target_channel}), "^" CHANNELCMDS CHANNELCMDS "*")))
       return 0;
-    }
 
-    if (sizeof(n) > 20 )
-    {
-      notify_fail("Der Name '"+n+"' ist zu lang.\n");
+    notify_fail("Der Name '"+target_channel+"' ist zu lang.\n");
+    if (sizeof(target_channel) > 20)
       return 0;
-    }
 
-    if (CHMASTER->new(n, this_object(), descr) == E_ACCESS_DENIED) {
-      notify_fail("Diese Ebene darfst du nicht erschaffen!\n");
+    notify_fail("Diese Ebene darfst du nicht erschaffen!\n");
+    if (CHMASTER->new(target_channel, ME, descr) == E_ACCESS_DENIED)
       return 0;
-    }
-    write("Du erschaffst die Ebene '"+n+"'.\n");
-    SetProp(P_CHANNELS, QueryProp(P_CHANNELS) + ({ lower_case(n) }));
+
+    tell_object(ME,"Du erschaffst die Ebene '"+target_channel+"'.\n");
+    SetProp(P_CHANNELS, QueryProp(P_CHANNELS) +
+                        ({lower_case(target_channel)}));
     return 1;
   }
 
-  if (sscanf(args, "beschreibung %s %s", n, descr) == 2)
+  if (sscanf(args, "beschreibung %s %s", target_channel, descr) == 2)
   {
-    cn = CHMASTER->find(n, this_object());
-    if (!cn || pointerp(cn))
+    chans = CHMASTER->find(target_channel, ME);
+
+    if (!chans)
     {
-      notify_fail("Die Ebene '"+n+"' existiert nicht oder die Angabe "
-        "war nicht eindeutig.\n");
+      notify_fail("Ebene '"+target_channel+"' nicht gefunden!\n");
+      return 0;
+    }
+    else if (pointerp(chans))
+    {
+      notify_fail(
+        "Das war keine eindeutige Angabe! Folgende Ebenen passen:\n"+
+        break_string(CountUp(chans), 78));
       return 0;
     }
 
-    mixed ch = CHMASTER->list(this_object());
-    if (ch[lower_case(cn)][I_MASTER] != this_object())
-    {
-      notify_fail("Du bist nicht berechtigt, die Beschreibung der Ebene"
-        " '"+cn+"' zu aendern.\n");
+    mapping ch = CHMASTER->list(ME);
+    notify_fail("Du bist nicht berechtigt, die Beschreibung der Ebene"
+      " '"+target_channel+"' zu aendern.\n");
+    if (ch[lower_case(chans)][I_MASTER] != ME)
       return 0;
-    }
-    ch[lower_case(cn)][I_INFO] = descr;
-    write("Die Ebene '"+cn+"' hat ab sofort die Beschreibung:\n"+descr+"\n");
+
+    ch[lower_case(target_channel)][I_INFO] = descr;
+    tell_object(ME,"Die Ebene '"+target_channel+"' hat ab sofort die "
+      "Beschreibung:\n"+descr+"\n");
     return 1;
   }
 
-  if sscanf(args, "%s=%s", sh, n) == 2 && sizeof(n))
+  if (sscanf(args, "%s=%s", _sh_cut, target_channel) == 2 &&
+      sizeof(target_channel))
   {
-    tmp = CHMASTER->find(n, this_object());
-    if (pointerp(tmp) || !tmp)
+    chans = CHMASTER->find(target_channel, ME);
+
+    if (!chans)
     {
-      notify_fail("Benutzung: ebene <Abkuerzung>=<Ebene>\n"+
-                  (pointerp(tmp) ?
-                      implode(tmp, ", ") + "\n" :
-                      "Ebene '"+n+"' nicht gefunden!\n"));
+      notify_fail("Ebene '"+target_channel+"' nicht gefunden!\n");
+      return 0;
+    }
+    else if (pointerp(chans))
+    {
+      notify_fail(
+        "Das war keine eindeutige Angabe! Folgende Ebenen passen:\n"+
+        break_string(CountUp(chans), 78));
       return 0;
     }
 
-    mapping sc = QueryProp(P_CHANNEL_SHORT) || ([]);
-    m_add(sc, sh, tmp);
-    SetProp(P_CHANNEL_SHORT, sc);
+    mapping shortcut_list = QueryProp(P_CHANNEL_SHORT) || ([]);
+    m_add(shortcut_list, _sh_cut, chans);
+    SetProp(P_CHANNEL_SHORT, shortcut_list);
 
     shortcut = QueryProp(P_CHANNEL_SHORT);
 
-    write("'"+sh+"' wird jetzt als Abkuerzung fuer '"+tmp+"' anerkannt.\n");
+    tell_object(ME,"'"+_sh_cut+"' wird jetzt als Abkuerzung fuer '"+chans+
+      "' anerkannt.\n");
     return 1;
   }
 
-  if (sscanf(args, "%s=", sh))
+  if (sscanf(args, "%s=", _sh_cut))
   {
     SetProp(P_CHANNEL_SHORT,
-      m_copy_delete(QueryProp(P_CHANNEL_SHORT) || ([]), sh));
+      m_delete(QueryProp(P_CHANNEL_SHORT) || ([]), _sh_cut));
     shortcut = QueryProp(P_CHANNEL_SHORT);
-    write("Du loeschst die Abkuerzung '"+sh+"'.\n");
+    tell_object(ME,"Du loeschst die Abkuerzung '"+_sh_cut+"'.\n");
     return 1;
   }
 
@@ -614,12 +755,12 @@
     if (pointerp(QueryProp(P_SWAP_CHANNELS)))
       SetProp(P_CHANNELS, QueryProp(P_SWAP_CHANNELS));
     else
-      SetProp(P_CHANNELS, m_indices(CHMASTER->list(this_object())));
+      SetProp(P_CHANNELS, m_indices(CHMASTER->list(ME)));
 
     // <excl> enthaelt die Channelnamen, deren Channel nicht erstellt wurden.
     string* excl = RegisterChannels();
-    write("Du schaltest folgende Ebenen ein:\n"+
-          break_string(implode(QueryProp(P_CHANNELS) - excl, ", "), 78));
+    tell_object(ME,"Du schaltest folgende Ebenen ein:\n"+
+          break_string(CountUp(QueryProp(P_CHANNELS) - excl), 78));
     SetProp(P_SWAP_CHANNELS, 0);
     return 1;
   }
@@ -629,66 +770,60 @@
     SetProp(P_SWAP_CHANNELS, QueryProp(P_CHANNELS));
     RemoveChannels();
     SetProp(P_CHANNELS, ({}));
-    write("Du stellst die Ebenen ab.\n");
+    tell_object(ME,"Du stellst die Ebenen ab.\n");
     return 1;
   }
 
   string* pa = old_explode(args, " ");
-  if (!strstr("abkuerzungen", pa[0]))
+  if (strstr("abkuerzungen", pa[0]) == 0)
   {
     string txt = "";
-    if (sizeof(pa) > 1 && !strstr("standard", pa[1]))
+    if (sizeof(pa) > 1 && strstr("standard", pa[1]) == 0)
     {
-      write("Die Standardabkuerzungen werden gesetzt.\n");
-      SetProp(P_CHANNEL_SHORT, DEFAULT_SHORTCUTS
-              + (IS_LEARNER(this_object()) ? WIZARD_SHORTCUTS : ([])));
+      tell_object(ME,"Die Standardabkuerzungen werden gesetzt.\n");
+      SetProp(P_CHANNEL_SHORT,
+        DEFAULT_SHORTCUTS + (IS_LEARNER(ME) ? WIZARD_SHORTCUTS : ([])));
     }
-    foreach (string abk, string ch_name : QueryProp(P_CHANNEL_SHORT)) {
-      txt += sprintf("%5.5s = %s\n", abk, ch_name);
+
+    mapping scut = QueryProp(P_CHANNEL_SHORT);
+    string* abbreviations = m_indices(scut);
+    foreach (string abk : sort_array(abbreviations, #'>))
+    {
+      txt += sprintf("%5.5s = %s\n", abk, scut[abk]);
     }
-    txt = sprintf("Folgende Abkuerzungen sind definiert:\n%-78#s\n",
-            implode(sort_array(old_explode(txt, "\n"), #'>/*'*/), "\n"));
-    More(txt);
+    More( "Folgende Abkuerzungen sind definiert:\n"+
+          sprintf("%-78#s\n", txt) );
     return 1;
   }
 
-  if (!strstr("standard", pa[0]))
+  if (strstr("standard", pa[0]) == 0)
   {
     if (sizeof(pa) < 2)
     {
-      notify_fail("Benutzung: ebene standard <Ebene>\n"
-                  +(QueryProp(P_STD_CHANNEL) ?
-                    "Momentan ist '"+QueryProp(P_STD_CHANNEL)+
-                      "' eingestellt.\n" :
-                    "Es ist keine Standardebene eingestellt.\n"));
+      notify_fail("Benutzung: ebene standard <Ebene>\n"+
+        (QueryProp(P_STD_CHANNEL)
+          ? "Momentan ist '"+QueryProp(P_STD_CHANNEL)+"' eingestellt.\n"
+          : "Es ist keine Standardebene eingestellt.\n"));
       return 0;
     }
-    else
+
+    chans = CHMASTER->find(pa[1], ME);
+    if (!chans)
     {
-      tmp = CHMASTER->find(pa[1], this_object());
-      if (pointerp(tmp))
-      {
-        notify_fail("Das war keine eindeutige Angabe! "
-                    "Folgende Ebenen passen:\n"+
-                    break_string(implode(tmp, ", "), 78));
-        return 0;
-      }
-      else
-      {
-        if (!tmp)
-        {
-          notify_fail("Ebene '"+pa[1]+"' nicht gefunden!\n");
-          return 0;
-        }
-        else
-        {
-          write("'"+tmp+"' ist jetzt die Standardebene.\n");
-          SetProp(P_STD_CHANNEL, tmp);
-          return 1;
-        }
-      }
+      notify_fail("Ebene '"+pa[1]+"' nicht gefunden!\n");
+      return 0;
     }
+    else if (pointerp(chans))
+    {
+      notify_fail(
+        "Das war keine eindeutige Angabe! Folgende Ebenen passen:\n"+
+        break_string(CountUp(chans), 78));
+      return 0;
+    }
+
+    tell_object(ME,"'"+chans+"' ist jetzt die Standardebene.\n");
+    SetProp(P_STD_CHANNEL, chans);
+    return 1;
   }
   return(0);
 }
-