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