blob: c15e33e8833e8ea79d0cf6320758e9c207f4d5b1 [file] [log] [blame]
MG Mud User88f12472016-06-24 23:31:02 +02001//
2// pub.c -- Alles, was eine Kneipe braucht.
3//
4// $Id: pub.c 8778 2014-04-30 23:04:06Z Zesstra $
5// spendiere ueberarbeitet, 22.05.2007 - Miril
6#pragma strong_types
7#pragma save_types
8#pragma pedantic
9#pragma range_check
10#pragma no_clone
11
12#define NEED_PROTOTYPES
13#include <thing/commands.h>
14#include <thing/properties.h>
15
16#include <defines.h>
17#include <rooms.h>
18#include <properties.h>
19#include <routingd.h>
20#include <bank.h>
21#include <exploration.h>
22#include <wizlevels.h>
23#include <pub.h>
24
25// Alle nicht-privaten werden in erbenen Objekten verwendet.
26private nosave int max_list;
27private nosave int refresh_count;
28private nosave int sum;
29private nosave mapping refresh_list;
30nosave mapping id_list;
31nosave mapping menu_list;
32nosave object *waiting;
33
34#define PM_RATE_PUBMASTER "rate"
35#define PM_DELAY_PUBMASTER "delay"
36
37protected void create()
38{ object router;
39
40 SetProp( P_ROOM_TYPE, QueryProp(P_ROOM_TYPE) | RT_PUB );
41
42 SetProp( P_PUB_NOT_ON_MENU,
43 "Tut mir leid, das fuehren wir nicht! Wir sind ein anstaendiges "+
44 "Lokal!\n" );
45 SetProp( P_PUB_UNAVAILABLE,
46 "Davon ist leider nichts mehr da.\n" );
47 SetProp(P_PUB_NO_MONEY,
48 "Das kostet %d Goldstuecke, und Du hast nicht so viel!\n" );
49 SetProp(P_PUB_NO_KEEPER,
50 "Es ist niemand anwesend, der Dich bedienen koennte.\n");
51
52 AddCmd( "menue","menue" );
53 AddCmd( ({"kauf","kaufe","bestell","bestelle"}),"bestelle" );
54 AddCmd( ({"spendier","spendiere"}),"spendiere" );
55 AddCmd( "pubinit","pubinit" );
56
57 max_list=0;
58 refresh_count=0;
59 waiting = ({ });
60 id_list=([]);
61 menu_list=([]);
62 refresh_list=([]);
63
64 if ( !clonep(ME) && objectp(router=find_object(ROUTER)) )
65 router->RegisterTarget(TARGET_PUB,object_name(ME));
66
67 call_out("add_std_drinks",1);
68}
69
70protected void create_super() {
71 set_next_reset(-1);
72}
73
74/* Zur Syntax:
75 *
76 * menuetext - Der Text steht im Menue
77 *
78 * ids - Array der Namen, mit denen bestellt werden kann
79 *
80 * minfo - Mapping mit Eintraegen fuer:
81 * P_HP (HP-Heilung),
82 * P_SP (SP-Heilung),
83 * P_FOOD (Saettigung),
84 * P_DRINK (Fluessigkeit)
85 * P_ALCOHOL (Alkoholisierung)
86 * P_VALUE (Preis)
87 * Die Eintraege werden ueber eval_anything ausgewertet
88 * (siehe da)
89 *
90 * rate - Heilrate (auch ueber eavl_anything) in Punkte / heart_beat()
91 *
92 * msg - Meldung beim Essen.
93 * a) closure (wird mit funcall(msg,zahler,empfaenger)
94 * ausgewertet)
95 * b) string (wie closure: call_other(this_object...))
96 * c) array mit 2 strings: 1) fuer Essenden, 2) fuer andere
97 * siehe auch mess()
98 *
99 * refresh - Mapping mit den moeglichen Eintraegen:
100 * PR_USER : <Kontingent> ; <Update> (pro Spieler)
101 * PR_ALL : <Kontingent> ; <Update> (fuer alle)
102 * Es wird zunaechst geprueft, ob noch etwas fuer den
103 * (zahlenden!) Spieler selbst vorhanden ist wenn nein wird
104 * geschaut, ob das Kontingent fuer alle schon erschoepft ist.
105 * Sind beide Kontingente erschoepft, kann der Spieler das
106 * Bestellte nicht bekommen.
107 * Die Kontingente wird alle <Update> reset()s "aufgefrischt".
108 * <Kontingent> wird ueber eval_anything() ausgewertet.
109 * Alternativ kann man einen Int-Wert angeben. Dieser wird wie
110 * ([ PR_ALL : <wert> ; 1 ]) behandelt.
111 *
112 * delay - Zahl der Sekunden, um die verzoegert die Heilung eintritt
113 * z.B. weil das Essen erst zubereitet werden muss.
114 * Ebenfalls ueber eval_anything()
115 *
116 * d_msg - Meldung beim bestellen, falls Delay. Wie msg.
117 */
118varargs string AddToMenu(string menuetext, mixed ids, mapping minfo,
119 mixed rate, mixed msg, mixed refresh,
120 mixed delay, mixed d_msg)
121{ string ident;
122 int i;
123
124 if ( !stringp(menuetext) || !ids || !mappingp(minfo) )
125 return 0;
126
127 if ( stringp(ids) )
128 ids = ({ ids });
129 else if ( !pointerp(ids) )
130 return 0;
131
132 ident = sprintf("menuentry%d",max_list);
133 max_list++;
134
135 /* Fuer schnelles Nachschlagen ein eigenes Mapping fuer Ids */
136 for( i=sizeof(ids)-1;i>=0;i-- )
137 id_list += ([ ids[i] : ident ]);
138
139 if ( intp(refresh) && (refresh>0) )
140 refresh = ([ PR_ALL : refresh; 1 ]);
141 else if ( !mappingp(refresh) )
142 refresh = 0;
143
144 menu_list += ([ ident : menuetext; minfo; rate; msg; refresh;
145 delay; d_msg; ids ]);
146 return ident;
147}
148
149// Diese Methode ist nur noch aus Kompatibilitaetsgruenden vorhanden und darf
150// nicht mehr verwendet werden!!!
151void AddFood(string nameOfFood, mixed ids, int price, int heal,
152 mixed myFunction)
153{
154 if ( !nameOfFood || !ids || !price)
155 return; /* Food not healing is ok ! */
156
157 AddToMenu( nameOfFood,ids,
158 ([ P_VALUE : price, P_FOOD : heal, P_HP : heal, P_SP : heal ]),
159 ((heal>5)?5:heal), myFunction, 0,0,0);
160}
161
162// Diese Methode ist nur noch aus Kompatibilitaetsgruenden vorhanden und darf
163// nicht mehr verwendet werden!!!
164void AddDrink(string nameOfDrink, mixed ids, int price, int heal,
165 int strength, int soak, mixed myFunction)
166{
167 if ( !nameOfDrink || !ids || !price )
168 return;
169
170 heal=heal/2;
171 /* Kleine Korrektur, damit man in alten Pubs ueberhaupt trinken kann */
172 AddToMenu(nameOfDrink,ids,
173 ([ P_VALUE : price, P_DRINK : soak, P_ALCOHOL : strength,
174 P_HP : heal, P_SP : heal ]),
175 ((heal>5)?5:heal), myFunction,0,0,0);
176}
177
178int RemoveFromMenu(mixed ids) {
179 int ret;
180
181 if (stringp(ids))
182 ids = ({ids});
183
184 if (pointerp(ids)) {
185 foreach (string id: ids) {
186 // look if the id has a matching ident
187 string ident = id_list[id];
188 if (stringp(ident)) {
189 // remove this ident-entry from the id-list ...
190 m_delete(id_list, id);
191 // ... and remove all others now too (so it won't bug later, if
192 // the wizard calling this method forgot an id)
193 foreach (string listid: m_indices(id_list))
194 if (id_list[listid] == ident)
195 m_delete(id_list, listid);
196
197 // now remove the ident from the menu_list
198 if (member(menu_list, ident)) {
199 ret++;
200 m_delete(menu_list, ident);
201
202 // decrease the ident-counter, if this entry was the last one
203 int oldnum;
204 if (sscanf(ident, "menuentry%d", oldnum) == 1 &&
205 oldnum == (max_list-1))
206 max_list--;
207 }
208 }
209 }
210 }
211 // return removed entries
212 return ret;
213}
214
215/* Zum Auswerten der Eintraege fuer Preis, Rate, HP...
216 * a) integer-Wert -> wird direkt uebernommen
217 * b) mapping -> Wird als RaceModifiere verstanden. Es wird der
218 * Eintrag gewaehlt, der der Rasse des Spielers
219 * entspricht, falls vorhanden, ansonsten der Eintrag
220 * fuer 0.
221 * c) Array -> In erstem Element (muss Objekt oder dessen Filename
222 * sein) wird die Funktion mit dem Namen im 2.Element
223 * aufgerufen.
224 * d) String -> Die genannte Funktion wird in der Kneipe selbst
225 * aufgerufen.
226 * e) Closure -> Die Closure wird ausgewertet.
227 * Alle Funktionsaufrufe bekommen den essenden Spieler (bei Price und Delay
228 * den bestellenden Spieler) als Argument uebergeben. Die Auswertung erfolgt
229 * in der angegebenen Reihenfolge. Am Ende muss ein Intergerwert herauskommen
230 */
231int eval_anything(mixed what, object pl)
232{ mixed re;
233
234 if (intp(what))
235 return what;
236
237 if (mappingp(what) && pl)
238 {
239 re = what[pl->QueryProp(P_RACE)]||what[0];
240 }
241
242 if (re)
243 what=re;
244
245 if ( pointerp(what) && (sizeof(what)>=2)
246 && ( objectp(what[0]) || stringp(what[0]) )
247 && stringp(what[1]) )
248 what = call_other(what[0],what[1],pl);
249
250 if ( stringp(what) && function_exists(what,ME) )
251 what = call_other(ME,what,pl);
252
253 if ( closurep(what) )
254 what = funcall(what,pl);
255
256 if ( intp(what) )
257 return what;
258
259 return 0;
260}
261
262/* Diese Funktion ueberprueft, ob das Kontingent eines Menueeintrags
263 * fuer einen Spieler erschoepft ist.
264 */
265string CheckAvailability(string ident, object zahler)
266{ string uid;
267
268 if ( !stringp(ident) || !member(menu_list,ident) || !objectp(zahler) )
269 return 0;
270 if ( !mappingp(menu_list[ident,PM_REFRESH]) )
271 return PR_NONE;
272
273 if ( !member(refresh_list,ident) )
274 refresh_list += ([ ident : ([ ]) ]);
275
276 if ( query_once_interactive(zahler) )
277 uid=getuid(zahler);
278 else
279 uid=object_name(zahler);
280
281 if ( member(menu_list[ident,PM_REFRESH],PR_USER) )
282 {
283 if ( !member(refresh_list[ident],uid) )
284 {
285 refresh_list[ident] += ([ uid : 0 ; refresh_count ]);
286 }
287
288 /* Kontingent des Zahlenden pruefen */
289 if ( refresh_list[ident][uid,PRV_AMOUNT] <
290 eval_anything(menu_list[ident,PM_REFRESH][PR_USER,PRV_AMOUNT],
291 zahler) )
292 return uid;
293 }
294
295 if ( member(menu_list[ident,PM_REFRESH],PR_ALL) )
296 {
297 if ( !member(refresh_list[ident],PR_DEFAULT) )
298 {
299 refresh_list[ident] += ([ PR_DEFAULT : 0 ; refresh_count ]);
300 }
301
302 /* Kontingent der Allgemeinheit pruefen */
303 if ( refresh_list[ident][PR_DEFAULT,PRV_AMOUNT] <
304 eval_anything(menu_list[ident,PM_REFRESH][PR_ALL,PRV_AMOUNT],
305 zahler) )
306 return PR_DEFAULT;
307 }
308
309 return 0;
310}
311
312/* Diese Funktion reduziert das Kontingent des Lebewesens uid beim
313 * Menueeintrag ident um 1
314 */
315void DecreaseAvailability(string ident, string uid)
316{
317 if ( !stringp(ident) || !stringp(uid) )
318 return;
319 refresh_list[ident][uid,PRV_AMOUNT]++;
320}
321
322/* Diese Funktion sorgt dafuer, dass die Kontingente an limitierten
323 * Sachen in regelmaessigen Abstaenden wiederhergestellt werden.
324 */
325static void UpdateAvailability()
326{ int i1,i2;
327 string *ind1,*ind2,chk;
328
329 /* Keine Refresh-Eintraege, kein Update */
330 if ( !mappingp(refresh_list)
331 || (i1=sizeof(ind1=m_indices(refresh_list)))<1 )
332 return;
333
334 /* Es muss jeder Menueeintrag, der in der refresh_list steht,
335 * durchgegangen werden.
336 */
337 for ( --i1 ; i1>=0 ; i1-- )
338 {
339 if ( !mappingp(refresh_list[ind1[i1]])
340 || (i2=sizeof(ind2=m_indices(refresh_list[ind1[i1]])))<1 )
341 continue;
342
343 /* Fuer jeden Menueeintrag muss jeder Spielereintrag durchgegangen
344 * werden, der in dem entspr. mapping steht.
345 */
346 for ( --i2 ; i2>=0 ; i2-- ) // Alle Spieler
347 {
348 if ( ind2[i2]==PR_DEFAULT )
349 chk = PR_ALL;
350 else
351 chk = PR_USER;
352
353 if ( ( refresh_list[ind1[i1]][ind2[i2],PRV_REFRESH]
354 + menu_list[ind1[i1],PM_REFRESH][chk,PRV_REFRESH] )
355 <= refresh_count )
356 {
357 refresh_list[ind1[i1]][ind2[i2],PRV_AMOUNT]=0;
358 refresh_list[ind1[i1]][ind2[i2],PRV_REFRESH]=refresh_count;
359 }
360 }
361 }
362}
363
364mixed DBG(mixed o) {
365 if(find_player("rumata"))
366 tell_object(
367 find_player("rumata"),
368 sprintf("DBG: %O\n", o)
369 );
370 return 0;
371}
372
373/* Erweitert um die Moeglichkeit, Speise- oder Getraenke-Karte zu sehen. */
374string menue_text(string str)
375{ int i,sdr,sfo;
376 string ident,res;
377 string *fo=({}),*dr=({});
378
379 if ( !max_list )
380 return "Hier scheint es derzeit nichts zu geben.\n";
381
382 if ( !stringp(str) || str=="" )
383 str="alles";
384
385 /* Fuers Menue entscheiden ob Drink oder Food */
386 foreach(string id, string menuetext, mapping minfo: menu_list)
387 {
388 if (eval_anything(minfo[P_FOOD],this_player()))
389 fo+=({ id });
390 else
391 dr+=({ id });
392 }
393
394 sdr = sizeof(dr);
395 sfo = sizeof(fo);
396
397 if ( member(({"alle","alles"}),str)!=-1)
398 {
399 if ( !sfo )
400 str="drinks";
401 else if ( !sdr )
402 str="speise";
403 else
404 {
405 /* Gemischte Karte */
406 res = "Getraenke Preis alc | "+
407 "Speisen Preis\n"+
408 "---------------------------------------+-"+
409 "--------------------------------------\n";
410
411 for ( i=0 ; ( i<sdr || i<sfo ) ; i++ )
412 {
413 if ( i<sdr )
414 res += sprintf("%-29.29s%5.5d %c | ",
415 menu_list[dr[i],PM_TEXT],
416 eval_anything(menu_list[dr[i],PM_INFO][P_VALUE],PL),
417 (eval_anything(menu_list[dr[i],PM_INFO][P_ALCOHOL],PL)>0) ? 'J' : 'N');
418 else
419 res += " | ";
420
421 if ( i<sfo )
422 res += sprintf("%-33.33s%5.5d",
423 menu_list[fo[i],PM_TEXT],
424 eval_anything(menu_list[fo[i],PM_INFO][P_VALUE],PL));
425
426 res += "\n";
427 }
428
429 return res;
430 }
431 }
432
433 /* Reine Getraenkekarte */
434 if ( member(({"getraenke","drinks","getraenk","trinken"}),str)!=-1 )
435 {
436 if ( !sdr )
437 return "Hier gibt es leider nichts zu trinken.\n";
438
439 res = "Getraenke Preis alc | "+
440 "Getraenke Preis alc\n"+
441 "---------------------------------------+-"+
442 "--------------------------------------\n";
443
444 for ( i=0 ; i<sdr ; i++ )
445 {
446 ident = dr[i];
447
448 if ( !eval_anything(menu_list[ident,PM_INFO][P_FOOD], PL) )
449 res += sprintf("%-29.29s%5.5d %c%s",
450 menu_list[ident,PM_TEXT],
451 eval_anything(menu_list[ident,PM_INFO][P_VALUE],PL),
452 (eval_anything(menu_list[ident,PM_INFO][P_ALCOHOL],PL)>0) ? 'J' : 'N',
453 ((i%2)?"\n":" | "));
454 }
455
456 if ( res[<1..<1]!="\n" )
457 res += "\n";
458
459 return res;
460 }
461
462 /* Reine Speisekarte */
463 if ( member(({"speise","speisen","essen"}),str)!=-1 )
464 {
465 if ( !sfo )
466 return "Hier gibt es leider nichts zu essen.\n";
467
468 res = "Speisen Preis | "+
469 "Speisen Preis\n"+
470 "---------------------------------------+-"+
471 "--------------------------------------\n";
472
473 for ( i=0 ; i<sfo ; i++ )
474 {
475 ident = fo[i];
476 if (eval_anything(menu_list[ident,PM_INFO][P_FOOD],PL) )
477 res += sprintf("%-33.33s%5.5d%s",
478 menu_list[ident,PM_TEXT],
479 eval_anything(menu_list[ident,PM_INFO][P_VALUE],PL),
480 ((i%2)?"\n":" | "));
481 }
482
483 if ( res[<1..<1]!="\n" )
484 res += "\n";
485
486 return res;
487 }
488
489 return 0;
490}
491
492int menue(string str)
493{ string txt;
494
495 _notify_fail("Welchen Teil des Menues moechtest Du sehen?\n");
496 if ( !stringp(txt=menue_text(str)) || sizeof(txt)<1 )
497 return 0;
498 write(txt);
499 return 1;
500}
501
502/* Diese Funktion kann/soll bei Bedarf ueberladen werden, um simultane
503 * Aenderungen des Mappings zu ermoeglichen (zu Beispiel wie es in guten
504 * Tagen groesser Portionen gib, Hobbits per se mehr kriegen, ...
505 */
506mapping adjust_info(string ident, mapping minfo, object zahler,
507 object empfaenger)
508{
509 return 0;
510}
511
512/* Hier hats jede Menge neue Platzhalter */
513string mess(string str,object pl)
514{
515 string dummy1, dummy2;
516
517 if ( !pl )
518 pl=PL;
519
520 if ( !stringp(str) || str=="" )
521 return str;
522
523 str=implode(explode(str,"&&"),pl->name(WER,2));
524 str=implode(explode(str,"&1&"),pl->name(WER,2));
525 str=implode(explode(str,"&2&"),pl->name(WESSEN,2));
526 str=implode(explode(str,"&3&"),pl->name(WEM,2));
527 str=implode(explode(str,"&4&"),pl->name(WEN,2));
528 str=implode(explode(str,"&1#"),capitalize(pl->name(WER,2)));
529 str=implode(explode(str,"&2#"),capitalize(pl->name(WESSEN,2)));
530 str=implode(explode(str,"&3#"),capitalize(pl->name(WEM,2)));
531 str=implode(explode(str,"&4#"),capitalize(pl->name(WEN,2)));
532 str=implode(explode(str,"&!"),pl->QueryPronoun(WER));
533 str=implode(explode(str,"&5&"),pl->QueryPronoun(WER));
534 str=implode(explode(str,"&6&"),pl->QueryPronoun(WESSEN));
535 str=implode(explode(str,"&7&"),pl->QueryPronoun(WEM));
536 str=implode(explode(str,"&8&"),pl->QueryPronoun(WEN));
537 str=implode(explode(str,"&5#"),capitalize(pl->QueryPronoun(WER)));
538 str=implode(explode(str,"&6#"),capitalize(pl->QueryPronoun(WESSEN)));
539 str=implode(explode(str,"&7#"),capitalize(pl->QueryPronoun(WEM)));
540 str=implode(explode(str,"&8#"),capitalize(pl->QueryPronoun(WEN)));
541
542 return break_string(capitalize(str),78,"", BS_LEAVE_MY_LFS);
543}
544
545protected void _copy_menulist_values(mapping entryinfo, string ident) {
546 /* Kopieren aller Werte ins minfo-Mapping, um Problemen bei Loeschung
547 aus dem Weg zu gehen. Slow and dirty */
548 entryinfo[PM_TEXT] = deep_copy(menu_list[ident, PM_TEXT]);
549 // PM_INFO is already flat in entryinfo
550 entryinfo[PM_RATE_PUBMASTER]
551 = deep_copy(menu_list[ident, PM_RATE]);
552 entryinfo[PM_SERVE_MSG] = deep_copy(menu_list[ident, PM_SERVE_MSG]);
553 entryinfo[PM_REFRESH] = deep_copy(menu_list[ident, PM_REFRESH]);
554 // PM_DELAY is already evaluated in entryinfo
555 entryinfo[PM_DELAY_MSG] = deep_copy(menu_list[ident, PM_DELAY_MSG]);
556 entryinfo[PM_IDS] = deep_copy(menu_list[ident, PM_IDS]);
557}
558
559int do_deliver(string ident, object zahler, object empfaenger,
560 mapping entryinfo) {
561 waiting -= ({ empfaenger,0 });
562
563 /* Empfaenger muss natuerlich noch da sein */
564 if ( !objectp(empfaenger) || !present(empfaenger) )
565 return 0;
566
567 /* Zahler wird nur wegen der Abwaertskompatibilitaet gebraucht */
568 if ( !objectp(zahler) )
569 zahler = empfaenger;
570
571 // alte Pubs, die do_deliver irgendwie selbst aufrufen, sollten
572 // mit der Zeit korrigiert werden
573 if(!mappingp(entryinfo))
574 raise_error("Pub ruft do_deliver() ohne sinnvolle Argumente auf.\n");
575 if(!member(entryinfo, PM_RATE_PUBMASTER)) {
576 if(!member(menu_list, ident))
577 raise_error("Pub ruft do_deliver() mit geloeschtem Getraenk und "
578 "teilweisen Argumenten auf!\n");
579
580 _copy_menulist_values(entryinfo, ident);
581 call_out(#'raise_error, 1,
582 "Pub ruft do_deliver() nur mit teilweisen Argumenten auf.\n");
583 }
584
585 entryinfo[PM_RATE_PUBMASTER] = eval_anything(entryinfo[PM_RATE_PUBMASTER],
586 empfaenger);
587 entryinfo[P_HP] = eval_anything(entryinfo[P_HP], empfaenger);
588 entryinfo[P_SP] = eval_anything(entryinfo[P_SP], empfaenger);
589
590 /* Ueberpruefen, ob Heilmoeglichkeit legal */
591 if ( query_once_interactive(empfaenger)
592 && ((PUBMASTER->RegisterItem(entryinfo[PM_TEXT], entryinfo))<1) ) {
593 tell_object(empfaenger,
594 "Mit diesem Getraenk/Gericht scheint etwas nicht in Ordnung "+
595 "zu sein.\nVerstaendige bitte den Magier, der fuer diesen "+
596 "Raum verantwortlich ist.\n");
597 return -4;
598 }
599
600 if ( QueryProp(P_NPC_FASTHEAL) && !query_once_interactive(empfaenger) ) {
601 entryinfo[H_DISTRIBUTION] = HD_INSTANT;
602 }
603 else {
604 entryinfo[H_DISTRIBUTION] = entryinfo[PM_RATE_PUBMASTER];
605 }
606 empfaenger->consume(entryinfo);
607
608 /* Meldung ausgeben */
609 /* Hinweis: Da die ausfuehrenden Funktionen auch ident und minfo
610 * uebergeben bekommen, kann man hier auch ueber adjust_info oder
611 * an anderer Stelle zusaetzliche Informationen uebergeben...
612 */
613 if (closurep(entryinfo[PM_SERVE_MSG]) )
614 funcall(entryinfo[PM_SERVE_MSG], zahler, empfaenger, ident, entryinfo);
615 else if (stringp(entryinfo[PM_SERVE_MSG]) &&
616 function_exists(entryinfo[PM_SERVE_MSG],ME))
617 call_other(ME, entryinfo[PM_SERVE_MSG], zahler, empfaenger, ident,
618 entryinfo);
619 else if (pointerp(entryinfo[PM_SERVE_MSG]) &&
620 sizeof(entryinfo[PM_SERVE_MSG])>=2) {
621 if (stringp(entryinfo[PM_SERVE_MSG][0]) &&
622 sizeof(entryinfo[PM_SERVE_MSG][0]))
623 tell_object(empfaenger,
624 mess(entryinfo[PM_SERVE_MSG][0]+"\n", empfaenger));
625 if (stringp(entryinfo[PM_SERVE_MSG][1]) &&
626 sizeof(entryinfo[PM_SERVE_MSG][1]))
627 tell_room(environment(empfaenger)||ME,
628 mess(entryinfo[PM_SERVE_MSG][1]+"\n",empfaenger),
629 ({empfaenger}) );
630 }
631
632 return 1;
633}
634
635/* Testet, ob genug Geld zur Verfuegung steht.
636 * Falls die Bonitaet anderen Beschraenkungen unterliegt, als
637 * dass der Zahler genug Geld dabei hat, muss diese Methode
638 * ueberschrieben werden.
639 * Rueckgabewerte:
640 * -2 : Out of Money
641 * 0 : Alles OK.
642 * Rueckgabewerte != 0 fuehren zu einem Abbruch der Bestellung
643 */
644int CheckSolvency(string ident, object zahler, object empfaenger,
645 mapping entryinfo)
646{
647 if ( (zahler->QueryMoney())<entryinfo[P_VALUE] )
648 {
649 string res;
650 if ( !stringp(res=QueryProp(P_PUB_NO_MONEY)) )
651 res = "Das kostet %d Goldstuecke, und Du hast nicht so viel!\n";
652 tell_object(zahler,sprintf(res, entryinfo[P_VALUE]));
653 return -2;
654 }
655 return 0;
656}
657
658/* Fuehrt die Bezahlung durch.
659 * Falls die Bezahlung anders erfolgt, als durch Abzug des Geldes vom Zahler,
660 * muss diese Methode ueberschrieben werden
661 * Rueckgabewerte:
662 * Anzahl der Muenzen, die im Pub landen und bei Reset in die Zentralbank
663 * eingezahlt werden
664 */
665int DoPay(string ident, object zahler, object empfaenger, mapping entryinfo)
666{
667 zahler->AddMoney(-entryinfo[P_VALUE]);
668 return entryinfo[P_VALUE];
669}
670
671/* Rueckgabewerte:
672 * -6 : Nicht vorraetig
673 * -5 : Wirt nicht anwesend
674 * -4 : Illegales Getraenk/Gericht. Ausgabe verweigert.
675 * Nur bei sofortiger Lieferung...
676 * -3 : Empfaenger bereits voll
677 * -2 : Out of Money
678 * -1 : spendieren ignoriert
679 * 0 : Empfaenger ausgeflogen (sollte eigentlich nicht passieren)
680 * 1 : Alles OK.
681 */
682int consume_something(string ident, object zahler, object empfaenger) {
683 if ( !objectp(zahler) )
684 zahler=PL;
685
686 if ( !objectp(empfaenger) )
687 empfaenger=PL;
688
689 /* Die Abfrage auf anwesenden Wirt erfolgt NUR an dieser Stelle, damit */
690 /* kein Spieler darunter leiden muss, wenn jemand anderes zwischen */
691 /* Bestellung und Lieferung den Wirt meuchelt. */
692 if ( stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME))
693 {
694 string res = QueryProp(P_PUB_NO_KEEPER);
695 if ( !stringp(res) ) {
696 res = "Es ist niemand anwesend, der Dich bedienen koennte.\n";
697 }
698 tell_object(zahler,res);
699 return -5;
700 }
701
702 /* Spendiert und ignoriert? */
703 if ( zahler!=empfaenger )
704 {
705 mixed res = ({"spendiere"});
706 if ( eval_anything(menu_list[ident,PM_INFO][P_DRINK],empfaenger) )
707 res += ({"spendiere.getraenke"});
708 if ( eval_anything(menu_list[ident,PM_INFO][P_FOOD],empfaenger) )
709 res += ({"spendiere.essen"});
710 if ( eval_anything(menu_list[ident,PM_INFO][P_ALCOHOL],empfaenger) )
711 res += ({"spendiere.alkohol"});
712 if ( empfaenger->TestIgnoreSimple(res) )
713 {
714 tell_object(zahler,
715 empfaenger->Name(WER)+" will das nicht.\n");
716 return -1;
717 }
718 }
719
720 /* Hier kann das Info-Mapping erst mal als ganzes angepasst werden. */
721 mapping xinfo;
722 mapping entryinfo = deep_copy(menu_list[ident, PM_INFO]);
723 if ( (xinfo=adjust_info(ident,entryinfo,zahler,empfaenger)) &&
724 mappingp(xinfo) )
725 entryinfo += xinfo;
726
727 /* Genug Geld vorhanden? */
728 entryinfo[P_VALUE] = eval_anything(entryinfo[P_VALUE], zahler);
729 {
730 int res = CheckSolvency(ident, zahler, empfaenger, entryinfo);
731 if (res != 0) return res;
732 }
733
734 string avb;
735 if ( !stringp(avb=CheckAvailability(ident, zahler)) )
736 {
737 string res = QueryProp(P_PUB_UNAVAILABLE);
738 if ( !stringp(res) )
739 res = "Davon ist leider nichts mehr da.\n";
740 tell_object(zahler,res);
741 return -6;
742 }
743
744 /* Textausgabe beim spendieren */
745 if ( empfaenger!=zahler)
746 {
747 tell_room(environment(empfaenger)||ME,
748 zahler->Name(WER)+" spendiert "+empfaenger->name(WEM)+" etwas.\n",
749 ({zahler, empfaenger}) );
750 tell_object(empfaenger,
751 zahler->Name(WER)+" spendiert Dir etwas.\n");
752 tell_object(zahler,
753 "Du spendierst "+empfaenger->name(WEM)+" etwas.\n");
754 }
755
756 /* Testen, ob mans noch essen / trinken kann */
757 /* Die int-Werte werden in minfo uebernommen fuer die Auswertung */
758 /* im Pubmaster. */
759 entryinfo[P_FOOD] = eval_anything(entryinfo[P_FOOD], empfaenger);
760 entryinfo[P_ALCOHOL] = eval_anything(entryinfo[P_ALCOHOL],empfaenger);
761 entryinfo[P_DRINK] = eval_anything(entryinfo[P_DRINK], empfaenger);
762
763 int result = empfaenger->consume(entryinfo, 1);
764 if (result < 0) {
765 if (result & HC_MAX_FOOD_REACHED)
766 tell_object(empfaenger,
767 "Du bist zu satt, das schaffst Du nicht mehr.\n");
768 else if (result & HC_MAX_DRINK_REACHED)
769 tell_object(empfaenger,
770 "So viel kannst Du im Moment nicht trinken.\n");
771 else if (result & HC_MAX_ALCOHOL_REACHED)
772 tell_object(empfaenger,
773 "Soviel Alkohol vertraegst Du nicht mehr.\n");
774 return -3;
775 }
776
777 /* Gezahlt wird auch sofort */
778 sum += DoPay(ident, zahler, empfaenger, entryinfo);
779
780 /* FPs gibts auch sofort */
781 if (zahler == empfaenger)
782 GiveEP(EP_PUB,menu_list[ident,PM_IDS][0]);
783
784 /* Falls die Anzahl des Bestellten beschraenkt ist, muss diese natuerlich
785 * angepasst werden.
786 */
787 if ( avb!=PR_NONE )
788 DecreaseAvailability(ident,avb);
789
790 /* Gibt es eine Zeitverzoegerung zwischen Bestellen und Servieren? */
791 entryinfo[PM_DELAY_PUBMASTER] = eval_anything(menu_list[ident, PM_DELAY], zahler);
792
793 // alle fuer einen Drink notwendigen Werte kopieren
794 _copy_menulist_values(entryinfo, ident);
795
796 if (entryinfo[PM_DELAY_PUBMASTER]<=0)
797 return do_deliver(ident,zahler,empfaenger,entryinfo);
798
799 /* Bestell-Meldung ausgeben */
800 if (closurep(entryinfo[PM_DELAY_MSG]))
801 funcall(entryinfo[PM_DELAY_MSG], zahler, empfaenger,ident, entryinfo);
802 else if (stringp(entryinfo[PM_DELAY_MSG]) &&
803 function_exists(entryinfo[PM_DELAY_MSG],ME))
804 call_other(ME, entryinfo[PM_DELAY_MSG], zahler, empfaenger, ident,
805 entryinfo);
806 else if (pointerp(entryinfo[PM_DELAY_MSG]) &&
807 sizeof(entryinfo[PM_DELAY_MSG])>=2) {
808 if (stringp(entryinfo[PM_DELAY_MSG][0]) &&
809 sizeof(entryinfo[PM_DELAY_MSG][0]))
810 tell_object(empfaenger,
811 mess(entryinfo[PM_DELAY_MSG][0]+"\n",empfaenger));
812 if (stringp(entryinfo[PM_DELAY_MSG][1]) &&
813 sizeof(entryinfo[PM_DELAY_MSG][1]))
814 tell_room(environment(empfaenger)||ME,
815 mess(entryinfo[PM_DELAY_MSG][1]+"\n",empfaenger),
816 ({empfaenger}) );
817 }
818
819 waiting += ({ empfaenger });
820 call_out("do_deliver", entryinfo[PM_DELAY_PUBMASTER],
821 ident, zahler, empfaenger, entryinfo);
822
823 return 1;
824}
825
826/* Rueckgabewere: 0: Nicht im Menue gefunde, 1 sonst */
827int search_what(string str,object zahler,object empfaenger)
828{ string ident;
829
830 if ( member(waiting,empfaenger)!=-1 )
831 {
832 if ( PL==empfaenger )
833 write("Du wartest doch noch auf etwas!\n");
834 else
835 write(empfaenger->Name(WER,2)+" wartet noch auf etwas.\n");
836 return 1;
837 }
838
839 str = lower_case(str);
840 if ( ident=id_list[str] )
841 {
842 consume_something(ident,zahler,empfaenger);
843 return 1;
844 }
845
846 return 0;
847}
848
849// Neue Version von Mesi:
850int spendiere(string str)
851{
852 _notify_fail("spendiere <spieler> <drink>\n");
853
854 if ( !stringp(str) || str=="" )
855 return 0;
856
857 string who,what;
858 int whoidx;
859
860 if (sscanf(str,"%s %d %s",who,whoidx,what)!=3
861 && sscanf(str,"%s %s",who,what)!=2)
862 return 0;
863 object target=present(who, whoidx);
864 if(!target && this_player()) target=present(who, whoidx, this_player());
865 if ( !target || !living(target) )
866 {
867 write("Das Lebewesen ist nicht hier...\n");
868 return 1;
869 }
870
871 notify_fail((string)QueryProp(P_PUB_NOT_ON_MENU)||"So etwas gibt es hier nicht!\n");
872
873 return search_what(what,PL,target);
874}
875
876int bestelle(string str)
877{
878 notify_fail((string)QueryProp(P_PUB_NOT_ON_MENU));
879
880 if ( !stringp(str) )
881 return 0;
882
883 return search_what(str,PL,PL);
884}
885
886int pubinit()
887{ string *liste,ident,fn;
888 int si,erg,max;
889 mapping minfo,xinfo;
890
891 if ( !PL || !IS_WIZARD(PL) )
892 return 0;
893
894 si=sizeof(liste=sort_array(m_indices(menu_list),#'<));
895 if ( si<1 )
896 return notify_fail("Keine Gerichte/Getraenke vorhanden.\n"),0;
897
898 fn=old_explode(object_name(ME),"#")[0];
899 printf("\n%'_'|30s %3s %3s %3s %5s %2s %2s %3s %3s %4s %3s\n",
900 "ITEM","ALC","DRI","FOO","VALUE","RT","DL","_HP","_SP","TEST","MAX");
901 for ( --si ; si>=0 ; si-- )
902 {
903 ident=liste[si];
904 minfo=deep_copy(menu_list[ident,PM_INFO]);
905
906 if ( (xinfo=adjust_info(ident,minfo,PL,PL)) && mappingp(xinfo) )
907 minfo+=xinfo;
908
909 minfo[P_VALUE] = eval_anything(minfo[P_VALUE], PL);
910 minfo[P_FOOD] = eval_anything(minfo[P_FOOD], PL);
911 minfo[P_ALCOHOL] = eval_anything(minfo[P_ALCOHOL], PL);
912 minfo[P_DRINK] = eval_anything(minfo[P_DRINK], PL);
913 minfo[PM_DELAY_PUBMASTER]
914 = eval_anything(menu_list[ident,PM_DELAY], PL);
915 minfo[PM_RATE_PUBMASTER]
916 = eval_anything(menu_list[ident,PM_RATE], PL);
917 minfo[P_HP] = eval_anything(minfo[P_HP], PL);
918 minfo[P_SP] = eval_anything(minfo[P_SP], PL);
919 erg=PUBMASTER->RegisterItem(menu_list[ident,0],minfo);
920 max=PUBMASTER->CalcMax(minfo,fn);
921
922 printf("%-'..'30.30s %3d %3d %3d %5d %2d %2d %3d %3d %|4s %3d\n",
923 menu_list[ident,PM_TEXT],
924 minfo[P_ALCOHOL], minfo[P_DRINK], minfo[P_FOOD],
925 minfo[P_VALUE],
926 minfo[PM_RATE_PUBMASTER],
927 minfo[PM_DELAY_PUBMASTER],
928 minfo[P_HP], minfo[P_SP],
929 ( erg ? "OK" : "FAIL" ),max);
930 }
931 write("Done.\n");
932 return 1;
933}
934
935void reset()
936{
937 if ( sum>0 )
938 ZENTRALBANK->PayIn(sum);
939 sum=0;
940 refresh_count++;
941 UpdateAvailability();
942}
943
944void add_gluehwein()
945{
946 if ( ctime(time())[4..6]=="Dec" )
947 AddToMenu( "Gluehwein",({"gluehwein"}),([
948 P_VALUE : 80,
949 P_DRINK : 5,
950 P_ALCOHOL : 20,
951 P_HP : 15,
952 P_SP : 15 ]),2,({
953 ("Du trinkst ein Glas Gluehwein, an dem Du Dir beinahe die Zunge "+
954 "verbrennst.\n"),
955 ("&& bestellt ein Glas Gluehwein und verbrennt sich beim\n"+
956 "Trinken beinahe die Zunge.\n") }), 0, 0, 0);
957}
958
959void add_std_drinks()
960{
961 if ( QueryProp(P_NO_STD_DRINK) )
962 return ;
963 add_gluehwein();
964}
965
966mapping query_menulist()
967{
968 return deep_copy(menu_list);
969}
970
971string *query_drinks()
972{
973 string *dr=({});
974 foreach( string ident, string menuetext, mapping minfo: menu_list) {
975 if (eval_anything(minfo[P_DRINK], 0))
976 dr += ({ ident });
977 }
978 return dr;
979}
980
981string *query_food()
982{
983 string *fo=({});
984 foreach( string ident, string menuetext, mapping minfo: menu_list) {
985 if (eval_anything(minfo[P_FOOD], 0))
986 fo += ({ ident });
987 }
988 return fo;
989}