Added public files

Roughly added all public files. Probably missed some, though.
diff --git a/std/room/pub.c b/std/room/pub.c
new file mode 100644
index 0000000..c15e33e
--- /dev/null
+++ b/std/room/pub.c
@@ -0,0 +1,989 @@
+//
+// pub.c -- Alles, was eine Kneipe braucht.
+//
+// $Id: pub.c 8778 2014-04-30 23:04:06Z Zesstra $
+// spendiere ueberarbeitet, 22.05.2007 - Miril
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+#define NEED_PROTOTYPES
+#include <thing/commands.h>
+#include <thing/properties.h>
+
+#include <defines.h>
+#include <rooms.h>
+#include <properties.h>
+#include <routingd.h>
+#include <bank.h>
+#include <exploration.h>
+#include <wizlevels.h>
+#include <pub.h>
+
+// Alle nicht-privaten werden in erbenen Objekten verwendet.
+private nosave int     max_list;
+private nosave int     refresh_count;
+private nosave int     sum;
+private nosave mapping refresh_list;
+nosave mapping id_list;
+nosave mapping menu_list;
+nosave object  *waiting;
+
+#define PM_RATE_PUBMASTER  "rate"
+#define PM_DELAY_PUBMASTER "delay"
+
+protected void create()
+{ object router;
+
+  SetProp( P_ROOM_TYPE, QueryProp(P_ROOM_TYPE) | RT_PUB );
+
+  SetProp( P_PUB_NOT_ON_MENU,
+    "Tut mir leid, das fuehren wir nicht! Wir sind ein anstaendiges "+
+    "Lokal!\n" );
+  SetProp( P_PUB_UNAVAILABLE,
+    "Davon ist leider nichts mehr da.\n" );
+  SetProp(P_PUB_NO_MONEY,
+    "Das kostet %d Goldstuecke, und Du hast nicht so viel!\n" );
+  SetProp(P_PUB_NO_KEEPER,
+    "Es ist niemand anwesend, der Dich bedienen koennte.\n");
+
+  AddCmd( "menue","menue" );
+  AddCmd( ({"kauf","kaufe","bestell","bestelle"}),"bestelle" );
+  AddCmd( ({"spendier","spendiere"}),"spendiere" );
+  AddCmd( "pubinit","pubinit" );
+
+  max_list=0;
+  refresh_count=0;
+  waiting = ({ });
+  id_list=([]);
+  menu_list=([]);
+  refresh_list=([]);
+
+  if ( !clonep(ME) && objectp(router=find_object(ROUTER)) )
+    router->RegisterTarget(TARGET_PUB,object_name(ME));
+
+  call_out("add_std_drinks",1);
+}
+
+protected void create_super() {
+  set_next_reset(-1);
+}
+
+/* Zur Syntax:
+ *
+ * menuetext - Der Text steht im Menue
+ *
+ * ids       - Array der Namen, mit denen bestellt werden kann
+ *
+ * minfo     - Mapping mit Eintraegen fuer:
+ *                 P_HP (HP-Heilung),
+ *                 P_SP (SP-Heilung),
+ *                 P_FOOD (Saettigung),
+ *                 P_DRINK (Fluessigkeit)
+ *                 P_ALCOHOL (Alkoholisierung)
+ *                 P_VALUE (Preis)
+ *             Die Eintraege werden ueber eval_anything ausgewertet
+ *             (siehe da)
+ *
+ * rate      - Heilrate (auch ueber eavl_anything) in Punkte / heart_beat()
+ *
+ * msg       - Meldung beim Essen.
+ *             a) closure (wird mit funcall(msg,zahler,empfaenger)
+ *                ausgewertet)
+ *             b) string (wie closure: call_other(this_object...))
+ *             c) array mit 2 strings: 1) fuer Essenden, 2) fuer andere
+ *                siehe auch mess()
+ *
+ * refresh   - Mapping mit den moeglichen Eintraegen:
+ *                 PR_USER : <Kontingent> ; <Update> (pro Spieler)
+ *                 PR_ALL  : <Kontingent> ; <Update> (fuer alle)
+ *             Es wird zunaechst geprueft, ob noch etwas fuer den
+ *             (zahlenden!) Spieler selbst vorhanden ist wenn nein wird
+ *             geschaut, ob das Kontingent fuer alle schon erschoepft ist.
+ *             Sind beide Kontingente erschoepft, kann der Spieler das
+ *             Bestellte nicht bekommen.
+ *             Die Kontingente wird alle <Update> reset()s "aufgefrischt".
+ *             <Kontingent> wird ueber eval_anything() ausgewertet.
+ *             Alternativ kann man einen Int-Wert angeben. Dieser wird wie
+ *             ([ PR_ALL : <wert> ; 1 ]) behandelt.
+ *
+ * delay     - Zahl der Sekunden, um die verzoegert die Heilung eintritt
+ *             z.B. weil das Essen erst zubereitet werden muss.
+ *             Ebenfalls ueber eval_anything()
+ *
+ * d_msg     - Meldung beim bestellen, falls Delay. Wie msg.
+ */
+varargs string AddToMenu(string menuetext, mixed ids, mapping minfo,
+                         mixed rate, mixed msg, mixed refresh,
+                         mixed delay, mixed d_msg)
+{ string ident;
+  int i;
+
+  if ( !stringp(menuetext) || !ids || !mappingp(minfo) )
+    return 0;
+
+  if ( stringp(ids) )
+    ids = ({ ids });
+  else if ( !pointerp(ids) )
+    return 0;
+
+  ident = sprintf("menuentry%d",max_list);
+  max_list++;
+
+  /* Fuer schnelles Nachschlagen ein eigenes Mapping fuer Ids */
+  for( i=sizeof(ids)-1;i>=0;i-- )
+    id_list += ([ ids[i] : ident ]);
+
+  if ( intp(refresh) && (refresh>0) )
+    refresh = ([ PR_ALL : refresh; 1 ]);
+  else if ( !mappingp(refresh) )
+    refresh = 0;
+
+  menu_list += ([ ident : menuetext; minfo; rate; msg; refresh;
+                   delay; d_msg; ids ]);
+  return ident;
+}
+
+// Diese Methode ist nur noch aus Kompatibilitaetsgruenden vorhanden und darf
+// nicht mehr verwendet werden!!!
+void AddFood(string nameOfFood, mixed ids, int price, int heal,
+             mixed myFunction)
+{
+  if ( !nameOfFood || !ids || !price)
+    return; /* Food not healing is ok ! */
+
+  AddToMenu( nameOfFood,ids,
+             ([ P_VALUE : price, P_FOOD : heal, P_HP : heal, P_SP : heal ]),
+             ((heal>5)?5:heal), myFunction, 0,0,0);
+}
+
+// Diese Methode ist nur noch aus Kompatibilitaetsgruenden vorhanden und darf
+// nicht mehr verwendet werden!!!
+void AddDrink(string nameOfDrink, mixed ids, int price, int heal,
+              int strength, int soak, mixed myFunction)
+{
+  if ( !nameOfDrink || !ids || !price )
+    return;
+
+  heal=heal/2;
+  /* Kleine Korrektur, damit man in alten Pubs ueberhaupt trinken kann */
+  AddToMenu(nameOfDrink,ids,
+             ([ P_VALUE : price, P_DRINK : soak, P_ALCOHOL : strength,
+                P_HP : heal, P_SP : heal ]),
+             ((heal>5)?5:heal), myFunction,0,0,0);
+}
+
+int RemoveFromMenu(mixed ids) {
+  int ret;
+
+  if (stringp(ids))
+    ids = ({ids});
+
+  if (pointerp(ids)) {
+    foreach (string id: ids) {
+      // look if the id has a matching ident
+      string ident = id_list[id];
+      if (stringp(ident)) {
+        // remove this ident-entry from the id-list ...
+        m_delete(id_list, id);
+        // ... and remove all others now too (so it won't bug later, if
+        //     the wizard calling this method forgot an id)
+        foreach (string listid: m_indices(id_list))
+          if (id_list[listid] == ident)
+            m_delete(id_list, listid);
+
+        // now remove the ident from the menu_list
+        if (member(menu_list, ident)) {
+          ret++;
+          m_delete(menu_list, ident);
+
+          // decrease the ident-counter, if this entry was the last one
+          int oldnum;
+          if (sscanf(ident, "menuentry%d", oldnum) == 1 &&
+              oldnum == (max_list-1))
+            max_list--;
+        }
+      }
+    }
+  }
+  // return removed entries
+  return ret;
+}
+
+/* Zum Auswerten der Eintraege fuer Preis, Rate, HP...
+ * a) integer-Wert    -> wird direkt uebernommen
+ * b) mapping         -> Wird als RaceModifiere verstanden. Es wird der
+ *                       Eintrag gewaehlt, der der Rasse des Spielers
+ *                       entspricht, falls vorhanden, ansonsten der Eintrag
+ *                       fuer 0.
+ * c) Array           -> In erstem Element (muss Objekt oder dessen Filename
+ *                       sein) wird die Funktion mit dem Namen im 2.Element
+ *                       aufgerufen.
+ * d) String          -> Die genannte Funktion wird in der Kneipe selbst
+ *                       aufgerufen.
+ * e) Closure         -> Die Closure wird ausgewertet.
+ * Alle Funktionsaufrufe bekommen den essenden Spieler (bei Price und Delay
+ * den bestellenden Spieler) als Argument uebergeben. Die Auswertung erfolgt
+ * in der angegebenen Reihenfolge. Am Ende muss ein Intergerwert herauskommen
+ */
+int eval_anything(mixed what, object pl)
+{ mixed re;
+
+  if (intp(what))
+    return what;
+
+  if (mappingp(what) && pl)
+  {
+    re = what[pl->QueryProp(P_RACE)]||what[0];
+  }
+
+  if (re)
+    what=re;
+
+  if ( pointerp(what) && (sizeof(what)>=2)
+      && ( objectp(what[0]) || stringp(what[0]) )
+      && stringp(what[1]) )
+    what = call_other(what[0],what[1],pl);
+
+  if ( stringp(what) && function_exists(what,ME) )
+    what = call_other(ME,what,pl);
+
+  if ( closurep(what) )
+    what = funcall(what,pl);
+
+  if ( intp(what) )
+    return what;
+
+  return 0;
+}
+
+/* Diese Funktion ueberprueft, ob das Kontingent eines Menueeintrags
+ * fuer einen Spieler erschoepft ist.
+ */
+string CheckAvailability(string ident, object zahler)
+{ string uid;
+
+  if ( !stringp(ident) || !member(menu_list,ident) || !objectp(zahler) )
+    return 0;
+  if ( !mappingp(menu_list[ident,PM_REFRESH]) )
+    return PR_NONE;
+
+  if ( !member(refresh_list,ident) )
+    refresh_list += ([ ident : ([ ]) ]);
+
+  if ( query_once_interactive(zahler) )
+    uid=getuid(zahler);
+  else
+    uid=object_name(zahler);
+
+  if ( member(menu_list[ident,PM_REFRESH],PR_USER) )
+  {
+    if ( !member(refresh_list[ident],uid) )
+    {
+      refresh_list[ident] += ([ uid : 0 ; refresh_count ]);
+    }
+
+    /* Kontingent des Zahlenden pruefen */
+    if ( refresh_list[ident][uid,PRV_AMOUNT] <
+          eval_anything(menu_list[ident,PM_REFRESH][PR_USER,PRV_AMOUNT],
+                        zahler) )
+      return uid;
+  }
+
+  if ( member(menu_list[ident,PM_REFRESH],PR_ALL) )
+  {
+    if ( !member(refresh_list[ident],PR_DEFAULT) )
+    {
+      refresh_list[ident] += ([ PR_DEFAULT : 0 ; refresh_count ]);
+    }
+
+    /* Kontingent der Allgemeinheit pruefen */
+    if ( refresh_list[ident][PR_DEFAULT,PRV_AMOUNT] <
+          eval_anything(menu_list[ident,PM_REFRESH][PR_ALL,PRV_AMOUNT],
+                        zahler) )
+      return PR_DEFAULT;
+  }
+
+  return 0;
+}
+
+/* Diese Funktion reduziert das Kontingent des Lebewesens uid beim
+ * Menueeintrag ident um 1
+ */
+void DecreaseAvailability(string ident, string uid)
+{
+  if ( !stringp(ident) || !stringp(uid) )
+    return;
+  refresh_list[ident][uid,PRV_AMOUNT]++;
+}
+
+/* Diese Funktion sorgt dafuer, dass die Kontingente an limitierten
+ * Sachen in regelmaessigen Abstaenden wiederhergestellt werden.
+ */
+static void UpdateAvailability()
+{ int    i1,i2;
+  string *ind1,*ind2,chk;
+
+  /* Keine Refresh-Eintraege, kein Update */
+  if ( !mappingp(refresh_list)
+      || (i1=sizeof(ind1=m_indices(refresh_list)))<1 )
+    return;
+
+  /* Es muss jeder Menueeintrag, der in der refresh_list steht,
+   * durchgegangen werden.
+   */
+  for ( --i1 ; i1>=0 ; i1-- )
+  {
+    if ( !mappingp(refresh_list[ind1[i1]])
+        || (i2=sizeof(ind2=m_indices(refresh_list[ind1[i1]])))<1 )
+      continue;
+
+    /* Fuer jeden Menueeintrag muss jeder Spielereintrag durchgegangen
+     * werden, der in dem entspr. mapping steht.
+     */
+    for ( --i2 ; i2>=0 ; i2-- ) // Alle Spieler
+    {
+      if ( ind2[i2]==PR_DEFAULT )
+        chk = PR_ALL;
+      else
+        chk = PR_USER;
+
+      if ( ( refresh_list[ind1[i1]][ind2[i2],PRV_REFRESH]
+            + menu_list[ind1[i1],PM_REFRESH][chk,PRV_REFRESH] )
+          <= refresh_count )
+      {
+        refresh_list[ind1[i1]][ind2[i2],PRV_AMOUNT]=0;
+        refresh_list[ind1[i1]][ind2[i2],PRV_REFRESH]=refresh_count;
+      }
+    }
+  }
+}
+
+mixed DBG(mixed o) {
+  if(find_player("rumata"))
+    tell_object(
+        find_player("rumata"),
+        sprintf("DBG: %O\n", o)
+    );
+  return 0;
+}
+
+/* Erweitert um die Moeglichkeit, Speise- oder Getraenke-Karte zu sehen. */
+string menue_text(string str)
+{ int i,sdr,sfo;
+  string ident,res;
+  string *fo=({}),*dr=({});
+
+  if ( !max_list )
+    return "Hier scheint es derzeit nichts zu geben.\n";
+
+  if ( !stringp(str) || str=="" )
+    str="alles";
+
+  /* Fuers Menue entscheiden ob Drink oder Food */
+  foreach(string id, string menuetext, mapping minfo: menu_list)
+  {
+    if (eval_anything(minfo[P_FOOD],this_player()))
+      fo+=({ id });
+    else
+      dr+=({ id });
+  }
+
+  sdr = sizeof(dr);
+  sfo = sizeof(fo);
+
+  if ( member(({"alle","alles"}),str)!=-1)
+  {
+    if ( !sfo )
+      str="drinks";
+    else if ( !sdr )
+      str="speise";
+    else
+    {
+      /* Gemischte Karte */
+      res = "Getraenke                    Preis alc | "+
+            "Speisen                          Preis\n"+
+            "---------------------------------------+-"+
+            "--------------------------------------\n";
+
+      for ( i=0 ; ( i<sdr || i<sfo ) ; i++ )
+      {
+        if ( i<sdr )
+          res += sprintf("%-29.29s%5.5d  %c  | ",
+                   menu_list[dr[i],PM_TEXT],
+                   eval_anything(menu_list[dr[i],PM_INFO][P_VALUE],PL),
+                   (eval_anything(menu_list[dr[i],PM_INFO][P_ALCOHOL],PL)>0) ? 'J' : 'N');
+        else
+          res += "                                       | ";
+
+        if ( i<sfo )
+          res += sprintf("%-33.33s%5.5d",
+                   menu_list[fo[i],PM_TEXT],
+                   eval_anything(menu_list[fo[i],PM_INFO][P_VALUE],PL));
+
+        res += "\n";
+      }
+
+      return res;
+    }
+  }
+
+  /* Reine Getraenkekarte */
+  if ( member(({"getraenke","drinks","getraenk","trinken"}),str)!=-1 )
+  {
+    if ( !sdr )
+      return "Hier gibt es leider nichts zu trinken.\n";
+
+    res = "Getraenke                    Preis alc | "+
+          "Getraenke                    Preis alc\n"+
+          "---------------------------------------+-"+
+          "--------------------------------------\n";
+
+    for ( i=0 ; i<sdr ; i++ )
+    {
+      ident = dr[i];
+
+      if ( !eval_anything(menu_list[ident,PM_INFO][P_FOOD], PL) )
+        res += sprintf("%-29.29s%5.5d  %c%s",
+                 menu_list[ident,PM_TEXT],
+                 eval_anything(menu_list[ident,PM_INFO][P_VALUE],PL),
+                 (eval_anything(menu_list[ident,PM_INFO][P_ALCOHOL],PL)>0) ? 'J' : 'N',
+                 ((i%2)?"\n":"  | "));
+    }
+
+    if ( res[<1..<1]!="\n" )
+      res += "\n";
+
+    return res;
+  }
+
+  /* Reine Speisekarte */
+  if ( member(({"speise","speisen","essen"}),str)!=-1 )
+  {
+    if ( !sfo )
+      return "Hier gibt es leider nichts zu essen.\n";
+
+    res = "Speisen                          Preis | "+
+          "Speisen                          Preis\n"+
+          "---------------------------------------+-"+
+          "--------------------------------------\n";
+
+    for ( i=0 ; i<sfo ; i++ )
+    {
+      ident = fo[i];
+      if (eval_anything(menu_list[ident,PM_INFO][P_FOOD],PL) )
+        res += sprintf("%-33.33s%5.5d%s",
+                 menu_list[ident,PM_TEXT],
+                 eval_anything(menu_list[ident,PM_INFO][P_VALUE],PL),
+                 ((i%2)?"\n":" | "));
+    }
+
+    if ( res[<1..<1]!="\n" )
+      res += "\n";
+
+    return res;
+  }
+
+  return 0;
+}
+
+int menue(string str)
+{ string txt;
+
+  _notify_fail("Welchen Teil des Menues moechtest Du sehen?\n");
+  if ( !stringp(txt=menue_text(str)) || sizeof(txt)<1 )
+    return 0;
+  write(txt);
+  return 1;
+}
+
+/* Diese Funktion kann/soll bei Bedarf ueberladen werden, um simultane
+ * Aenderungen des Mappings zu ermoeglichen (zu Beispiel wie es in guten
+ * Tagen groesser Portionen gib, Hobbits per se mehr kriegen, ...
+ */
+mapping adjust_info(string ident, mapping minfo, object zahler,
+                    object empfaenger)
+{
+  return 0;
+}
+
+/* Hier hats jede Menge neue Platzhalter */
+string mess(string str,object pl)
+{
+  string dummy1, dummy2;
+
+  if ( !pl )
+    pl=PL;
+
+  if ( !stringp(str) || str=="" )
+    return str;
+
+  str=implode(explode(str,"&&"),pl->name(WER,2));
+  str=implode(explode(str,"&1&"),pl->name(WER,2));
+  str=implode(explode(str,"&2&"),pl->name(WESSEN,2));
+  str=implode(explode(str,"&3&"),pl->name(WEM,2));
+  str=implode(explode(str,"&4&"),pl->name(WEN,2));
+  str=implode(explode(str,"&1#"),capitalize(pl->name(WER,2)));
+  str=implode(explode(str,"&2#"),capitalize(pl->name(WESSEN,2)));
+  str=implode(explode(str,"&3#"),capitalize(pl->name(WEM,2)));
+  str=implode(explode(str,"&4#"),capitalize(pl->name(WEN,2)));
+  str=implode(explode(str,"&!"),pl->QueryPronoun(WER));
+  str=implode(explode(str,"&5&"),pl->QueryPronoun(WER));
+  str=implode(explode(str,"&6&"),pl->QueryPronoun(WESSEN));
+  str=implode(explode(str,"&7&"),pl->QueryPronoun(WEM));
+  str=implode(explode(str,"&8&"),pl->QueryPronoun(WEN));
+  str=implode(explode(str,"&5#"),capitalize(pl->QueryPronoun(WER)));
+  str=implode(explode(str,"&6#"),capitalize(pl->QueryPronoun(WESSEN)));
+  str=implode(explode(str,"&7#"),capitalize(pl->QueryPronoun(WEM)));
+  str=implode(explode(str,"&8#"),capitalize(pl->QueryPronoun(WEN)));
+
+  return break_string(capitalize(str),78,"", BS_LEAVE_MY_LFS);
+}
+
+protected void _copy_menulist_values(mapping entryinfo, string ident) {
+  /* Kopieren aller Werte ins minfo-Mapping, um Problemen bei Loeschung
+     aus dem Weg zu gehen. Slow and dirty */
+  entryinfo[PM_TEXT]      = deep_copy(menu_list[ident, PM_TEXT]);
+  // PM_INFO is already flat in entryinfo
+  entryinfo[PM_RATE_PUBMASTER]
+                          = deep_copy(menu_list[ident, PM_RATE]);
+  entryinfo[PM_SERVE_MSG] = deep_copy(menu_list[ident, PM_SERVE_MSG]);
+  entryinfo[PM_REFRESH]   = deep_copy(menu_list[ident, PM_REFRESH]);
+  // PM_DELAY is already evaluated in entryinfo
+  entryinfo[PM_DELAY_MSG] = deep_copy(menu_list[ident, PM_DELAY_MSG]);
+  entryinfo[PM_IDS]       = deep_copy(menu_list[ident, PM_IDS]);
+}
+
+int do_deliver(string ident, object zahler, object empfaenger,
+               mapping entryinfo) {
+  waiting -= ({ empfaenger,0 });
+
+  /* Empfaenger muss natuerlich noch da sein */
+  if ( !objectp(empfaenger) || !present(empfaenger) )
+    return 0;
+
+  /* Zahler wird nur wegen der Abwaertskompatibilitaet gebraucht */
+  if ( !objectp(zahler) )
+    zahler = empfaenger;
+
+  // alte Pubs, die do_deliver irgendwie selbst aufrufen, sollten
+  // mit der Zeit korrigiert werden
+  if(!mappingp(entryinfo))
+    raise_error("Pub ruft do_deliver() ohne sinnvolle Argumente auf.\n");
+  if(!member(entryinfo, PM_RATE_PUBMASTER)) {
+    if(!member(menu_list, ident))
+      raise_error("Pub ruft do_deliver() mit geloeschtem Getraenk und "
+                  "teilweisen Argumenten auf!\n");
+
+    _copy_menulist_values(entryinfo, ident);
+    call_out(#'raise_error, 1,
+             "Pub ruft do_deliver() nur mit teilweisen Argumenten auf.\n");
+  }
+
+  entryinfo[PM_RATE_PUBMASTER] = eval_anything(entryinfo[PM_RATE_PUBMASTER],
+                                               empfaenger);
+  entryinfo[P_HP]              = eval_anything(entryinfo[P_HP], empfaenger);
+  entryinfo[P_SP]              = eval_anything(entryinfo[P_SP], empfaenger);
+
+  /* Ueberpruefen, ob Heilmoeglichkeit legal */
+  if ( query_once_interactive(empfaenger)
+      && ((PUBMASTER->RegisterItem(entryinfo[PM_TEXT], entryinfo))<1) ) {
+     tell_object(empfaenger,
+       "Mit diesem Getraenk/Gericht scheint etwas nicht in Ordnung "+
+       "zu sein.\nVerstaendige bitte den Magier, der fuer diesen "+
+       "Raum verantwortlich ist.\n");
+     return -4;
+  }
+
+  if ( QueryProp(P_NPC_FASTHEAL) && !query_once_interactive(empfaenger) ) {
+    entryinfo[H_DISTRIBUTION] = HD_INSTANT;
+  }
+  else {
+    entryinfo[H_DISTRIBUTION] = entryinfo[PM_RATE_PUBMASTER];
+  }
+  empfaenger->consume(entryinfo);
+
+  /* Meldung ausgeben */
+  /* Hinweis: Da die ausfuehrenden Funktionen auch ident und minfo
+   * uebergeben bekommen, kann man hier auch ueber adjust_info oder
+   * an anderer Stelle zusaetzliche Informationen uebergeben...
+   */
+  if (closurep(entryinfo[PM_SERVE_MSG]) )
+    funcall(entryinfo[PM_SERVE_MSG], zahler, empfaenger, ident, entryinfo);
+  else if (stringp(entryinfo[PM_SERVE_MSG]) &&
+           function_exists(entryinfo[PM_SERVE_MSG],ME))
+    call_other(ME, entryinfo[PM_SERVE_MSG], zahler, empfaenger, ident,
+                   entryinfo);
+  else if (pointerp(entryinfo[PM_SERVE_MSG]) &&
+           sizeof(entryinfo[PM_SERVE_MSG])>=2)  {
+    if (stringp(entryinfo[PM_SERVE_MSG][0]) &&
+        sizeof(entryinfo[PM_SERVE_MSG][0]))
+      tell_object(empfaenger,
+        mess(entryinfo[PM_SERVE_MSG][0]+"\n", empfaenger));
+    if (stringp(entryinfo[PM_SERVE_MSG][1]) &&
+        sizeof(entryinfo[PM_SERVE_MSG][1]))
+      tell_room(environment(empfaenger)||ME,
+        mess(entryinfo[PM_SERVE_MSG][1]+"\n",empfaenger),
+        ({empfaenger}) );
+  }
+
+  return 1;
+}
+
+/* Testet, ob genug Geld zur Verfuegung steht.
+ * Falls die Bonitaet anderen Beschraenkungen unterliegt, als
+ * dass der Zahler genug Geld dabei hat, muss diese Methode
+ * ueberschrieben werden.
+ * Rueckgabewerte:
+ * -2 : Out of Money
+ *  0 : Alles OK.
+ * Rueckgabewerte != 0 fuehren zu einem Abbruch der Bestellung
+ */
+int CheckSolvency(string ident, object zahler, object empfaenger,
+                   mapping entryinfo)
+{
+  if ( (zahler->QueryMoney())<entryinfo[P_VALUE] )
+  {
+    string res;
+    if ( !stringp(res=QueryProp(P_PUB_NO_MONEY)) )
+      res = "Das kostet %d Goldstuecke, und Du hast nicht so viel!\n";
+    tell_object(zahler,sprintf(res, entryinfo[P_VALUE]));
+    return -2;
+  }
+  return 0;
+}
+
+/* Fuehrt die Bezahlung durch.
+ * Falls die Bezahlung anders erfolgt, als durch Abzug des Geldes vom Zahler,
+ * muss diese Methode ueberschrieben werden
+ * Rueckgabewerte:
+ * Anzahl der Muenzen, die im Pub landen und bei Reset in die Zentralbank
+ * eingezahlt werden
+ */
+int DoPay(string ident, object zahler, object empfaenger, mapping entryinfo)
+{
+  zahler->AddMoney(-entryinfo[P_VALUE]);
+  return entryinfo[P_VALUE];
+}
+
+/* Rueckgabewerte:
+ * -6 : Nicht vorraetig
+ * -5 : Wirt nicht anwesend
+ * -4 : Illegales Getraenk/Gericht. Ausgabe verweigert.
+ *      Nur bei sofortiger Lieferung...
+ * -3 : Empfaenger bereits voll
+ * -2 : Out of Money
+ * -1 : spendieren ignoriert
+ *  0 : Empfaenger ausgeflogen (sollte eigentlich nicht passieren)
+ *  1 : Alles OK.
+ */
+int consume_something(string ident, object zahler, object empfaenger) {
+  if ( !objectp(zahler) )
+    zahler=PL;
+
+  if ( !objectp(empfaenger) )
+    empfaenger=PL;
+
+  /* Die Abfrage auf anwesenden Wirt erfolgt NUR an dieser Stelle, damit */
+  /* kein Spieler darunter leiden muss, wenn jemand anderes zwischen     */
+  /* Bestellung und Lieferung den Wirt meuchelt.                         */
+  if ( stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME))
+  {
+    string res = QueryProp(P_PUB_NO_KEEPER);
+    if ( !stringp(res) ) {
+      res = "Es ist niemand anwesend, der Dich bedienen koennte.\n";
+    }
+    tell_object(zahler,res);
+    return -5;
+  }
+
+  /* Spendiert und ignoriert? */
+  if ( zahler!=empfaenger )
+  {
+    mixed res = ({"spendiere"});
+    if ( eval_anything(menu_list[ident,PM_INFO][P_DRINK],empfaenger) )
+      res += ({"spendiere.getraenke"});
+    if ( eval_anything(menu_list[ident,PM_INFO][P_FOOD],empfaenger) )
+      res += ({"spendiere.essen"});
+    if ( eval_anything(menu_list[ident,PM_INFO][P_ALCOHOL],empfaenger) )
+      res += ({"spendiere.alkohol"});
+    if ( empfaenger->TestIgnoreSimple(res) )
+    {
+      tell_object(zahler,
+        empfaenger->Name(WER)+" will das nicht.\n");
+      return -1;
+    }
+  }
+
+  /* Hier kann das Info-Mapping erst mal als ganzes angepasst werden. */
+  mapping xinfo;
+  mapping entryinfo = deep_copy(menu_list[ident, PM_INFO]);
+  if ( (xinfo=adjust_info(ident,entryinfo,zahler,empfaenger)) &&
+       mappingp(xinfo) )
+    entryinfo += xinfo;
+
+  /* Genug Geld vorhanden? */
+  entryinfo[P_VALUE] = eval_anything(entryinfo[P_VALUE], zahler);
+  {
+    int res = CheckSolvency(ident, zahler, empfaenger, entryinfo);
+    if (res != 0) return res;
+  }
+
+  string avb;
+  if ( !stringp(avb=CheckAvailability(ident, zahler)) )
+  {
+    string res = QueryProp(P_PUB_UNAVAILABLE);
+    if ( !stringp(res) )
+      res = "Davon ist leider nichts mehr da.\n";
+    tell_object(zahler,res);
+    return -6;
+  }
+
+  /* Textausgabe beim spendieren */
+  if ( empfaenger!=zahler)
+  {
+    tell_room(environment(empfaenger)||ME,
+      zahler->Name(WER)+" spendiert "+empfaenger->name(WEM)+" etwas.\n",
+      ({zahler, empfaenger}) );
+    tell_object(empfaenger,
+      zahler->Name(WER)+" spendiert Dir etwas.\n");
+    tell_object(zahler,
+      "Du spendierst "+empfaenger->name(WEM)+" etwas.\n");
+  }
+
+  /* Testen, ob mans noch essen / trinken kann */
+  /* Die int-Werte werden in minfo uebernommen fuer die Auswertung */
+  /* im Pubmaster. */
+  entryinfo[P_FOOD]    = eval_anything(entryinfo[P_FOOD],   empfaenger);
+  entryinfo[P_ALCOHOL] = eval_anything(entryinfo[P_ALCOHOL],empfaenger);
+  entryinfo[P_DRINK]   = eval_anything(entryinfo[P_DRINK],  empfaenger);
+
+  int result = empfaenger->consume(entryinfo, 1);
+  if (result < 0) {
+    if (result & HC_MAX_FOOD_REACHED)
+      tell_object(empfaenger,
+                  "Du bist zu satt, das schaffst Du nicht mehr.\n");
+    else if (result & HC_MAX_DRINK_REACHED)
+      tell_object(empfaenger,
+                  "So viel kannst Du im Moment nicht trinken.\n");
+    else if (result & HC_MAX_ALCOHOL_REACHED)
+      tell_object(empfaenger,
+                  "Soviel Alkohol vertraegst Du nicht mehr.\n");
+    return -3;
+  }
+
+  /* Gezahlt wird auch sofort */
+  sum += DoPay(ident, zahler, empfaenger, entryinfo);
+
+  /* FPs gibts auch sofort */
+  if (zahler == empfaenger)
+    GiveEP(EP_PUB,menu_list[ident,PM_IDS][0]);
+
+  /* Falls die Anzahl des Bestellten beschraenkt ist, muss diese natuerlich
+   * angepasst werden.
+   */
+  if ( avb!=PR_NONE )
+    DecreaseAvailability(ident,avb);
+
+  /* Gibt es eine Zeitverzoegerung zwischen Bestellen und Servieren? */
+  entryinfo[PM_DELAY_PUBMASTER] = eval_anything(menu_list[ident, PM_DELAY], zahler);
+
+  // alle fuer einen Drink notwendigen Werte kopieren
+  _copy_menulist_values(entryinfo, ident);
+
+  if (entryinfo[PM_DELAY_PUBMASTER]<=0)
+    return do_deliver(ident,zahler,empfaenger,entryinfo);
+
+  /* Bestell-Meldung ausgeben */
+  if (closurep(entryinfo[PM_DELAY_MSG]))
+    funcall(entryinfo[PM_DELAY_MSG], zahler, empfaenger,ident, entryinfo);
+  else if (stringp(entryinfo[PM_DELAY_MSG]) &&
+           function_exists(entryinfo[PM_DELAY_MSG],ME))
+    call_other(ME, entryinfo[PM_DELAY_MSG], zahler, empfaenger, ident,
+               entryinfo);
+  else if (pointerp(entryinfo[PM_DELAY_MSG]) &&
+           sizeof(entryinfo[PM_DELAY_MSG])>=2) {
+    if (stringp(entryinfo[PM_DELAY_MSG][0]) &&
+       sizeof(entryinfo[PM_DELAY_MSG][0]))
+      tell_object(empfaenger,
+        mess(entryinfo[PM_DELAY_MSG][0]+"\n",empfaenger));
+    if (stringp(entryinfo[PM_DELAY_MSG][1]) &&
+        sizeof(entryinfo[PM_DELAY_MSG][1]))
+      tell_room(environment(empfaenger)||ME,
+        mess(entryinfo[PM_DELAY_MSG][1]+"\n",empfaenger),
+        ({empfaenger}) );
+  }
+
+  waiting += ({ empfaenger });
+  call_out("do_deliver", entryinfo[PM_DELAY_PUBMASTER],
+           ident, zahler, empfaenger, entryinfo);
+
+  return 1;
+}
+
+/* Rueckgabewere: 0: Nicht im Menue gefunde, 1 sonst */
+int search_what(string str,object zahler,object empfaenger)
+{ string ident;
+
+  if ( member(waiting,empfaenger)!=-1 )
+  {
+    if ( PL==empfaenger )
+      write("Du wartest doch noch auf etwas!\n");
+    else
+      write(empfaenger->Name(WER,2)+" wartet noch auf etwas.\n");
+    return 1;
+  }
+
+  str = lower_case(str);
+  if ( ident=id_list[str] )
+  {
+    consume_something(ident,zahler,empfaenger);
+    return 1;
+  }
+
+  return 0;
+}
+
+// Neue Version von Mesi:
+int spendiere(string str)
+{
+   _notify_fail("spendiere <spieler> <drink>\n");
+
+   if ( !stringp(str) || str=="" )
+     return 0;
+
+   string who,what;
+   int whoidx;
+
+   if (sscanf(str,"%s %d %s",who,whoidx,what)!=3
+    && sscanf(str,"%s %s",who,what)!=2)
+      return 0;
+  object target=present(who, whoidx);
+  if(!target && this_player()) target=present(who, whoidx, this_player());
+   if ( !target || !living(target) )
+   {
+     write("Das Lebewesen ist nicht hier...\n");
+     return 1;
+   }
+
+   notify_fail((string)QueryProp(P_PUB_NOT_ON_MENU)||"So etwas gibt es hier nicht!\n");
+
+   return search_what(what,PL,target);
+}
+
+int bestelle(string str)
+{
+  notify_fail((string)QueryProp(P_PUB_NOT_ON_MENU));
+
+  if ( !stringp(str) )
+    return 0;
+
+  return search_what(str,PL,PL);
+}
+
+int pubinit()
+{ string  *liste,ident,fn;
+  int     si,erg,max;
+  mapping minfo,xinfo;
+
+  if ( !PL || !IS_WIZARD(PL) )
+    return 0;
+
+  si=sizeof(liste=sort_array(m_indices(menu_list),#'<));
+  if ( si<1 )
+    return notify_fail("Keine Gerichte/Getraenke vorhanden.\n"),0;
+
+  fn=old_explode(object_name(ME),"#")[0];
+  printf("\n%'_'|30s %3s %3s %3s %5s %2s %2s %3s %3s %4s %3s\n",
+    "ITEM","ALC","DRI","FOO","VALUE","RT","DL","_HP","_SP","TEST","MAX");
+  for ( --si ; si>=0 ; si-- )
+  {
+    ident=liste[si];
+    minfo=deep_copy(menu_list[ident,PM_INFO]);
+
+    if ( (xinfo=adjust_info(ident,minfo,PL,PL)) && mappingp(xinfo) )
+      minfo+=xinfo;
+
+    minfo[P_VALUE]   = eval_anything(minfo[P_VALUE],            PL);
+    minfo[P_FOOD]    = eval_anything(minfo[P_FOOD],             PL);
+    minfo[P_ALCOHOL] = eval_anything(minfo[P_ALCOHOL],          PL);
+    minfo[P_DRINK]   = eval_anything(minfo[P_DRINK],            PL);
+    minfo[PM_DELAY_PUBMASTER]
+                     = eval_anything(menu_list[ident,PM_DELAY], PL);
+    minfo[PM_RATE_PUBMASTER]
+                     = eval_anything(menu_list[ident,PM_RATE],  PL);
+    minfo[P_HP]      = eval_anything(minfo[P_HP],               PL);
+    minfo[P_SP]      = eval_anything(minfo[P_SP],               PL);
+    erg=PUBMASTER->RegisterItem(menu_list[ident,0],minfo);
+    max=PUBMASTER->CalcMax(minfo,fn);
+
+    printf("%-'..'30.30s %3d %3d %3d %5d %2d %2d %3d %3d %|4s %3d\n",
+      menu_list[ident,PM_TEXT],
+      minfo[P_ALCOHOL], minfo[P_DRINK], minfo[P_FOOD],
+      minfo[P_VALUE],
+      minfo[PM_RATE_PUBMASTER],
+      minfo[PM_DELAY_PUBMASTER],
+      minfo[P_HP], minfo[P_SP],
+      ( erg ? "OK" : "FAIL" ),max);
+  }
+  write("Done.\n");
+  return 1;
+}
+
+void reset()
+{
+  if ( sum>0 )
+    ZENTRALBANK->PayIn(sum);
+  sum=0;
+  refresh_count++;
+  UpdateAvailability();
+}
+
+void add_gluehwein()
+{
+  if ( ctime(time())[4..6]=="Dec" )
+    AddToMenu( "Gluehwein",({"gluehwein"}),([
+      P_VALUE   : 80,
+      P_DRINK   :  5,
+      P_ALCOHOL : 20,
+      P_HP      : 15,
+      P_SP      : 15 ]),2,({
+      ("Du trinkst ein Glas Gluehwein, an dem Du Dir beinahe die Zunge "+
+       "verbrennst.\n"),
+      ("&& bestellt ein Glas Gluehwein und verbrennt sich beim\n"+
+       "Trinken beinahe die Zunge.\n") }), 0, 0, 0);
+}
+
+void add_std_drinks()
+{
+  if ( QueryProp(P_NO_STD_DRINK) )
+    return ;
+  add_gluehwein();
+}
+
+mapping query_menulist()
+{
+  return deep_copy(menu_list);
+}
+
+string *query_drinks()
+{
+  string *dr=({});
+  foreach( string ident, string menuetext, mapping minfo: menu_list) {
+    if (eval_anything(minfo[P_DRINK], 0))
+      dr += ({ ident });
+  }
+  return dr;
+}
+
+string *query_food()
+{
+  string *fo=({});
+  foreach( string ident, string menuetext, mapping minfo: menu_list) {
+    if (eval_anything(minfo[P_FOOD], 0))
+      fo += ({ ident });
+  }
+  return fo;
+}