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;
+}