Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/std/room/commands.c b/std/room/commands.c
new file mode 100644
index 0000000..5aa75c3
--- /dev/null
+++ b/std/room/commands.c
@@ -0,0 +1,37 @@
+// MorgenGrauen MUDlib
+//
+// room/commands.c -- room commands handling
+//
+// $Id: commands.c 8201 2012-11-07 17:55:12Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/thing/commands";
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <config.h>
+#include <properties.h>
+#include <language.h>
+#include <defines.h>
+
+void init()
+{
+ ::init();
+
+ add_action("imposs", "such");
+ add_action("imposs", "suche");
+}
+
+/* Fuer etwas bessere Fehlermeldungen als 'Wie bitte?' bei einigen */
+/* allgemeinen Kommandos. */
+int imposs()
+{
+ _notify_fail("Du suchst, findest aber nichts.\n");
+ return 0;
+}
diff --git a/std/room/description.c b/std/room/description.c
new file mode 100644
index 0000000..60d3b28
--- /dev/null
+++ b/std/room/description.c
@@ -0,0 +1,222 @@
+// MorgenGrauen MUDlib
+//
+// room/description.c -- room description handling
+//
+// $Id: description.c 9468 2016-02-19 21:07:04Z Gloinson $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/container/description";
+
+#define NEED_PROTOTYPES
+
+#include <properties.h>
+#include <defines.h>
+#include <wizlevels.h>
+#include <language.h>
+#include <doorroom.h>
+
+void create()
+{
+ ::create();
+ SetProp(P_NAME, "Raum");
+ SetProp(P_INT_SHORT,"<namenloser Raum>");
+ SetProp(P_INT_LONG,0);
+ SetProp(P_ROOM_MSG, ({}) );
+ SetProp(P_FUNC_MSG, 0);
+ SetProp(P_MSG_PROB, 30);
+ AddId(({"raum", "hier"}));
+}
+
+void init()
+{
+ // Wenn P_ROOM_MSG gesetzt oder P_FUNC_MSG und kein Callout laeuft,
+ // Callout starten.
+ mixed roommsg = QueryProp(P_ROOM_MSG);
+ if( ( (roommsg && sizeof(roommsg)) ||
+ QueryProp(P_FUNC_MSG) ) &&
+ (find_call_out("WriteRoomMessage")==-1))
+ call_out("WriteRoomMessage", random(QueryProp(P_MSG_PROB)));
+}
+
+varargs void AddRoomMessage(string *mesg, int prob, mixed func)
+{
+ if (mesg && !pointerp(mesg))
+ raise_error(sprintf(
+ "AddRoomMessage(): wrong argument type, expected Array or 0, "
+ "got %.20O",mesg));
+
+ SetProp(P_ROOM_MSG, mesg);
+
+ if (prob>0)
+ SetProp(P_MSG_PROB, prob);
+
+ if (func)
+ SetProp(P_FUNC_MSG, func);
+}
+
+static void WriteRoomMessage()
+{
+ int tim, msgid;
+ string *room_msg,func;
+ mixed *func_msg;
+
+ room_msg = (string *)QueryProp(P_ROOM_MSG);
+ func_msg = QueryProp(P_FUNC_MSG);
+ if ((!room_msg || !sizeof(room_msg)) && !func_msg)
+ return;
+
+ if (room_msg&&sizeof(room_msg))
+ {
+ msgid = random(sizeof(room_msg));
+ // Defaultwerte sind fuer Altcode schwierig
+ send_room(this_object(), room_msg[msgid],
+ MT_LOOK|MT_LISTEN|MT_FEEL|MT_SMELL|
+ MSG_DONT_STORE|MSG_DONT_BUFFER|MSG_DONT_WRAP);
+ }
+
+ if (func_msg)
+ {
+ if (stringp(func_msg))
+ func=(string)func_msg;
+ else
+ func=func_msg[random(sizeof(func_msg))];
+ if (func && function_exists(func))
+ call_other (this_object(), func, msgid);
+ }
+
+ while (remove_call_out("WriteRoomMessage")!=-1);
+ tim=QueryProp(P_MSG_PROB);
+ if(this_object() && sizeof(filter(
+ deep_inventory(this_object()), #'interactive))) //')))
+ call_out("WriteRoomMessage", (tim<15 ? 15 : tim));
+}
+
+varargs string int_long(mixed viewer,mixed viewpoint,int flags)
+{
+ string descr, inv_descr;
+
+ flags &= 3;
+ if( IS_LEARNER(viewer) && viewer->QueryProp( P_WANTS_TO_LEARN ) )
+ descr = "[" + object_name(ME) + "]\n";
+ else
+ descr = "";
+
+ descr += process_string(QueryProp(P_INT_LONG)||"");
+
+ // ggf. Tueren hinzufuegen.
+ if (QueryProp(P_DOOR_INFOS)) {
+ string tmp=((string)call_other(DOOR_MASTER,"look_doors"));
+ if (stringp(tmp) && sizeof(tmp))
+ descr += tmp;
+ }
+
+ // ggf. Ausgaenge hinzufuegen.
+ if ( viewer->QueryProp(P_SHOW_EXITS) && (!QueryProp(P_HIDE_EXITS)
+ || pointerp(QueryProp(P_HIDE_EXITS))) )
+ descr += GetExits(viewer) || "";
+
+ // Viewpoint (Objekt oder Objektarray) sind nicht sichtbar
+ inv_descr = (string) make_invlist(viewer, all_inventory(ME)
+ - (pointerp(viewpoint)?viewpoint:({viewpoint})) ,flags);
+
+ if ( inv_descr != "" )
+ descr += inv_descr;
+
+ if(environment() && (inv_descr=QueryProp(P_TRANSPARENT)))
+ {
+ if(stringp(inv_descr)) descr += inv_descr;
+ else descr += "Ausserhalb siehst Du:\n";
+
+ descr += environment()->int_short(viewer,ME);
+ }
+
+ return descr;
+}
+
+string int_short(mixed viewer,mixed viewpoint)
+{
+ string descr, inv_descr;
+
+ descr = process_string( QueryProp(P_INT_SHORT)||"");
+ if( IS_LEARNER(viewer) && viewer->QueryProp( P_WANTS_TO_LEARN ) )
+ descr += " [" + object_name(ME) + "].\n";
+ else
+ descr += ".\n";
+
+ if ( ( viewer->QueryProp(P_SHOW_EXITS)
+ || ( environment(viewer) == ME && !viewer->QueryProp(P_BRIEF) ) )
+ && (!QueryProp(P_HIDE_EXITS) || pointerp(QueryProp(P_HIDE_EXITS))) )
+ descr += GetExits(viewer) || "";
+
+ // Viewpoint (Objekt oder Objektarray) sind nicht sichtbar
+ inv_descr = (string) make_invlist( viewer, all_inventory(ME)
+ - (pointerp(viewpoint)?viewpoint:({viewpoint})) );
+
+ if ( inv_descr != "" )
+ descr += inv_descr;
+
+ return descr;
+}
+
+/** Roommessages abschalten, wenn keine Interactives mehr da sind.
+ */
+// TODO: Irgendwann das varargs loswerden, wenn in der restlichen Mudlib
+// TODO::exit() 'richtig' ueberschrieben wird.
+varargs void exit(object liv, object dest) {
+ // fall erbende Objekte das liv nicht uebergeben. Pruefung nur auf
+ // previous_object(). Wenn Magier da noch irgendwelche Spielchen mit
+ // call_other() & Co treiben, haben wir Pech gehabt, macht aber nicht viel,
+ // weil die Raummeldungen dann im naechsten callout abgeschaltet werden.
+ if (!living(liv=previous_object())) return;
+
+ object *interactives = filter(all_inventory(), #'interactive);
+ // liv wurde noch nicht bewegt, ggf. beruecksichtigen.
+ if ( !sizeof(interactives) ||
+ (interactive(liv) && sizeof(interactives) < 2) )
+ while (remove_call_out("WriteRoomMessage")!=-1);
+}
+
+static string _query_int_long() {return Query(P_INT_LONG, F_VALUE);}
+
+
+// Querymethode fuer P_DOMAIN - gibt die Region an, in der Raum liegt, sofern
+// er unter /d/ liegt...
+static string _query_lib_p_domain()
+{
+ string fn = object_name();
+ if (strstr(fn, "/d/") == 0)
+ {
+ return capitalize(explode(fn, "/")[2]);
+ }
+
+ return "unbekannt";
+}
+
+<string|string*>* _set_harbour_name( <string|string*>* desc)
+{
+ if ( sizeof(desc)!=2 )
+ {
+ raise_error(sprintf("Unacceptable data in P_HARBOUR, sizeof() was %d, "
+ "expected 2.", sizeof(desc)));
+ }
+ else if ( !stringp(desc[0]) )
+ {
+ raise_error("Wrong data type in P_HARBOUR[0]: expected 'string'.");
+ }
+ else if ( pointerp(desc[1]) && sizeof(desc[1])<1 )
+ {
+ raise_error("Insufficient data in P_HARBOUR[1]: expected 'string*', "
+ "got '({})'.");
+ }
+ else if ( stringp(desc[1]) )
+ {
+ desc[1] = ({desc[1]});
+ }
+ return Set(P_HARBOUR, desc, F_VALUE);
+}
+
diff --git a/std/room/doors.c b/std/room/doors.c
new file mode 100644
index 0000000..e05f009
--- /dev/null
+++ b/std/room/doors.c
@@ -0,0 +1,162 @@
+// MorgenGrauen MUDlib
+//
+// room/doors.c -- new doors, managed by doormaster
+//
+// $Id: doors.c 9134 2015-02-02 19:26:03Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+#include <config.h>
+#include <properties.h>
+#include <defines.h>
+#include <language.h>
+#include <doorroom.h>
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <room/exits.h>
+
+protected void create()
+{
+ if (object_name(this_object()) == __FILE__[0..<3])
+ {
+ set_next_reset(-1);
+ return;
+ }
+ SetProp(P_DOOR_INFOS,0);
+}
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+
+varargs int NewDoor(string|string* cmds, string dest, string|string* ids,
+ mapping|<int|string|string*>* props)
+{
+/*
+ cmds: Befehl(e), um durch die Tuer zu gehen (String oder Array von Strings)
+ dest: Zielraum
+ ids: Id(s) der Tuer, default "tuer" (String, Array von Strings oder 0)
+ props: besondere Eigenschaften der Tuer (optional)
+ Array mit Paaren Nummer der Eigenschaft, Inhalt
+ definierte Properties sind:
+ D_FLAGS: Flags wie bei Sir's Tueren
+ default: DOOR_CLOSED | DOOR_RESET_CL
+ Bei Schluesseln wird getestet, ob der String, den
+ QueryDoorKey zurueckliefert, gleich
+ "raumname1:raumname2" ist, wobei raumname1,2 die
+ kompletten Filenamen der beiden Raeume in sortierter
+ Reihenfolge sind.
+ D_LONG: lange Beschreibung der Tuer
+ default: "Eine Tuer.\n"
+ D_SHORT: kurze Beschreibung der Tuer, wird an die Raumbeschreibung
+ angefuegt, wobei %s durch geoeffnet bzw. geschlossen
+ ersetzt wird.
+ default: "Eine %se Tuer. "
+ D_NAME: Name, der beim Oeffnen/Schliessen und bei Fehlermeldungen
+ angezeigt wird.
+ default: "Tuer"
+ D_GENDER: default: FEMALE
+ D_FUNC: Funktion, die im Raum aufgerufen werden soll, wenn die
+ Tuer erfolgreich durchschritten wird.
+ default: 0
+ D_MSGS: Falls String: ausgegebene Richtung fuer move
+ Falls Array: ({direction, textout, textin}) fuer move
+ default: 0
+
+ Beispiel:
+ NewDoor("norden","/players/rochus/room/test2","portal",
+ ({D_NAME,"Portal",
+ D_GENDER,NEUTER,
+ D_SHORT,"Im Norden siehst Du ein %ses Portal. ",
+ D_LONG,"Das Portal ist einfach nur gigantisch.\n"
+ }));
+
+*/
+
+ if (!cmds || !dest) return 0;
+ return call_other(DOOR_MASTER,"NewDoor",cmds,dest,ids,props);
+}
+
+void init()
+{
+ mixed *info;
+ string *cmds;
+ int i,j;
+
+ if (!pointerp(info=(mixed *)QueryProp(P_DOOR_INFOS))) return;
+ add_action("oeffnen","oeffne");
+ add_action("schliessen","schliesse");
+ add_action("schliessen","schliess");
+ for (i=sizeof(info)-1;i>=0;i--) {
+ cmds=(string *)(info[i][D_CMDS]);
+ for (j=sizeof(cmds)-1;j>=0;j--)
+ add_action("go_door",cmds[j]);
+ // Befehle IMMER anfuegen, gechecked wird sowieso erst beim Durchgehen.
+ }
+}
+
+void reset()
+{
+ if (QueryProp(P_DOOR_INFOS))
+ call_other(DOOR_MASTER,"reset_doors");
+}
+
+int oeffnen (string str)
+{
+ if (!str || !QueryProp(P_DOOR_INFOS))
+ return 0;
+ return (int) call_other(DOOR_MASTER,"oeffnen",str);
+}
+
+int schliessen (string str)
+{
+ if (!str || !QueryProp(P_DOOR_INFOS))
+ return 0;
+ return (int) call_other(DOOR_MASTER,"schliessen",str);
+}
+
+varargs int
+go_door (string str)
+{
+ if (!QueryProp(P_DOOR_INFOS))
+ return 0;
+ if (call_other(DOOR_MASTER,"go_door",query_verb()))
+ return 1;
+ return 0;
+}
+
+int set_doors(string *cmds,int open)
+{
+ int j;
+
+ if (!previous_object())
+ return 0;
+ if (object_name(previous_object())!=DOOR_MASTER)
+ return 0;
+ // Andere sollen nicht rumpfuschen.
+ if (!this_player()) return 0;
+ if (environment(this_player())!=this_object())
+ return 0;
+ // Ist sowieso keiner da...
+ if (!pointerp(cmds))
+ return 0;
+ if (open)
+ AddSpecialExit(cmds,"go_door");
+ else
+ RemoveSpecialExit(cmds);
+ for (j=sizeof(cmds)-1;j>=0;j--)
+ add_action("go_door",cmds[j]);
+ return 1;
+}
+
+/* Fuer Tueren, die flexible Langbeschreibungen haben, wird ein
+ * SpecialDetail eingefuehrt.
+ */
+
+string special_detail_doors(string key){
+ return DOOR_MASTER->special_detail_doors(key);
+}
diff --git a/std/room/exits.c b/std/room/exits.c
new file mode 100644
index 0000000..dc724b8
--- /dev/null
+++ b/std/room/exits.c
@@ -0,0 +1,334 @@
+// MorgenGrauen MUDlib
+//
+// room/exits.c -- room exits handling
+//
+// $Id: exits.c 9497 2016-02-21 14:20:03Z Zesstra $
+
+/*
+ * Exits of the room (obvious ones, doors, and special ones)
+ * we define the following function for easy reference:
+ * GetExits() - return a string containing an "Obvious Exits" Statement
+ *
+ * The exits are implemented as properties P_EXITS
+ * They are stored locally (_set_xx, _query_xx)
+ * as mapping to speed up the routines in this module.
+ *
+ */
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <moving.h>
+#include <room/exits.h>
+#include <hook.h>
+#include <exploration.h>
+#undef NEED_PROTOTYPES
+
+#include <sys_debug.h>
+#include <config.h>
+#include <properties.h>
+#include <defines.h>
+#include <daemon.h>
+#include <doorroom.h>
+#include <routingd.h>
+
+#define NUMBERS ({ "zwei", "drei", "vier", "fuenf", "sechs", "sieben", "acht" })
+
+
+// Hilfsfunktion, die bei kaputten Exits eine Notrettung betreibt, aber
+// trotzdem auf Debug eine Meldung macht.
+static mapping rescueExit()
+{
+ catch(raise_error(sprintf(
+ "room/exits.c: Forgotten ::create()? "
+ "P_EXITS in %O is 0!\n", this_object()));publish);
+
+ return ([:2]);
+}
+
+
+static mapping _set_exits( mapping map_ldfied )
+{
+ if( mappingp(map_ldfied) )
+ return Set( P_EXITS, map_ldfied );
+ return 0;
+}
+
+
+static mapping _query_exits()
+{
+ if( (!previous_object() || object_name(previous_object()) != DOOR_MASTER)
+ && QueryProp(P_DOOR_INFOS) )
+ call_other( DOOR_MASTER, "init_doors" );
+
+ mapping exits = Query(P_EXITS) || rescueExit();
+
+ return filter(exits, function int (string key, mixed val)
+ {return stringp(val[0]);} );
+}
+
+
+static int _set_special_exits( mapping map_ldfied )
+{
+ return -1;
+}
+
+
+static mapping _query_special_exits()
+{
+ mapping exits = Query(P_EXITS) || rescueExit();
+
+ return filter(exits, function int (string key, mixed val)
+ {return closurep(val[0]);} );
+}
+
+
+void reset()
+{}
+
+
+protected void create()
+{
+ offerHook(H_HOOK_EXIT_USE, 1);
+ SetProp( P_EXITS, ([:2]) );
+}
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+
+protected void _AddExit(string|string* cmd, string|closure room,
+ string message)
+{
+ mapping exita;
+
+ exita = Query(P_EXITS) || rescueExit();
+
+ if ( !closurep(room) )
+ {
+ object router;
+
+ room = _MakePath(room);
+
+ if ( !clonep(this_object()) && objectp(router = find_object(ROUTER)) )
+ {
+ router->RegisterExit( object_name(this_object()), cmd, room );
+ }
+ }
+
+ if( stringp(cmd) )
+ {
+ exita += ([ cmd : room; message ]);
+ }
+ else
+ {
+ foreach(string c : cmd)
+ {
+ if (stringp(c))
+ exita += ([ c : room; message ]);
+ }
+ }
+
+ Set( P_EXITS, exita );
+}
+
+void AddExit(string|string* cmd, closure|string dest)
+{
+ string msg;
+ if ( stringp(dest) )
+ {
+ int s;
+ if( (s = member(dest, '#')) != -1 )
+ {
+ msg = dest[0..s-1];
+ dest = dest[s+1..];
+ }
+ }
+ _AddExit(cmd, dest, msg);
+}
+
+void RemoveExit(string|string* cmd )
+{
+ mapping exita;
+
+ if ( !cmd ) {
+ SetProp(P_EXITS, ([:2]) );
+ return;
+ }
+
+ if ( stringp(cmd) )
+ cmd = ({ cmd });
+
+ exita = Query(P_EXITS, F_VALUE) || rescueExit();
+ foreach(string c : cmd)
+ m_delete( exita, c );
+
+ Set( P_EXITS, exita, F_VALUE );
+}
+
+
+void AddSpecialExit(string|string* cmd, string|closure functionname )
+{
+
+ if ( stringp(functionname) )
+ functionname = symbol_function( functionname, this_object() );
+
+ if ( !closurep(functionname) )
+ {
+ catch(raise_error(sprintf( "method %O doesn't exist\n",
+ functionname)); publish);
+ return;
+ }
+
+ AddExit( cmd, functionname );
+}
+
+
+void RemoveSpecialExit(string|string* cmd)
+{
+ RemoveExit( cmd );
+}
+
+
+varargs string GetExits( object viewer )
+{
+ string *indices, *hidden;
+ string exits;
+
+ if ( QueryProp(P_DOOR_INFOS) )
+ call_other( DOOR_MASTER, "init_doors" );
+
+ indices = m_indices( Query(P_EXITS) || rescueExit() );
+
+ if ( pointerp(hidden = QueryProp(P_HIDE_EXITS)) )
+ indices -= hidden;
+
+ int n=sizeof(indices);
+ switch (n) {
+ case 0:
+ return "Es gibt keine sichtbaren Ausgaenge.\n";
+
+ case 1:
+ return "Es gibt einen sichtbaren Ausgang: " + indices[0] + ".\n";
+
+ case 2: case 3: case 4: case 5: case 6: case 7: case 8:
+ exits = "Es gibt " + NUMBERS[n-2] + " sichtbare Ausgaenge: ";
+ break;
+
+ default:
+ exits = "Es gibt viele sichtbare Ausgaenge: ";
+ }
+ exits += CountUp(indices);
+ return break_string( exits+".", 78 );
+}
+
+
+// Richtungsbefehle nur interpretieren, wenn der Spieler *im* Raum steht und
+// nicht davor (Transporter etc.)/o
+void init()
+{
+ if ( environment(this_player()) == this_object() )
+ add_action( "_normalfunction", "", 1 );
+}
+
+
+/* not only normal exits are handled here */
+
+int _normalfunction()
+{
+ int ret;
+ mapping exits = Query(P_EXITS, F_VALUE) || ([:3]);
+ if (!member(exits,query_verb()))
+ return 0;
+
+ string verb = query_verb();
+ string destroom = exits[query_verb(),0];
+ string message = exits[query_verb(),1];
+
+ mixed hres = HookFlow(H_HOOK_EXIT_USE, ({verb, destroom, message}));
+ if(hres && pointerp(hres) && sizeof(hres)>H_RETDATA)
+ {
+ if(hres[H_RETCODE]==H_CANCELLED)
+ {
+ return 1;
+ }
+ else if(hres[H_RETCODE]==H_ALTERED
+ && pointerp(hres[H_RETDATA])
+ && sizeof(hres[H_RETDATA]) >= 3)
+ {
+ <string|closure>* hdata = hres[H_RETDATA];
+ if (!stringp(hdata[0])
+ || (!stringp(hdata[1]) && !closurep(hdata[1]))
+ || (hdata[2] && !stringp(hdata[2])) )
+ raise_error(sprintf("Invalide Daten aus H_HOOK_EXIT_USE: %.150O\n",
+ hdata));
+ verb = hdata[0];
+ destroom = hdata[1];
+ message = hdata[2];
+ }
+ }
+
+ if( closurep(destroom) )
+ {
+ ret = funcall( destroom, verb );
+
+ if(ret==MOVE_OK)
+ {
+ GiveEP( EP_EXIT, verb );
+ }
+
+ return ret;
+ }
+
+ if (!stringp(message))
+ {
+ if( member( ({ "sueden", "suedwesten", "westen","nordwesten", "norden",
+ "nordosten", "osten","suedosten" }), verb ) != -1 )
+ {
+ message = "nach " + capitalize(verb);
+ }
+ else if ( member( ({ "oben", "unten" }), verb ) != -1 )
+ {
+ message = "nach " + verb;
+ }
+ else
+ {
+ message = verb;
+ }
+ }
+
+ ret = this_player()->move( destroom, M_GO, message );
+
+ if (ret==MOVE_OK)
+ {
+ GiveEP( EP_EXIT, verb );
+ }
+
+ return ret;
+}
+
+static string _MakePath( string str )
+{
+ string *comp;
+
+ comp = explode( object_name(this_object()), "/" ) - ({""});
+
+ switch( str[0] ){
+ case '.':
+ str = "/" + implode( comp[0..<2], "/" ) + "/" + str;
+ break;
+
+ case '~':
+ str = "/" + comp[0] + "/" + (comp[0] == "d" ? comp[1] + "/" : "")
+ +REAL_UID(this_object()) + str[1..];
+ break;
+ }
+
+ return MASTER->_get_path( str, getuid(this_object()) );
+}
+
diff --git a/std/room/items.c b/std/room/items.c
new file mode 100644
index 0000000..1d4005a
--- /dev/null
+++ b/std/room/items.c
@@ -0,0 +1,42 @@
+// MorgenGrauen MUDlib
+//
+// room/items.c -- creating extra items in room
+//
+// $Id: items.c 9538 2016-03-20 23:46:41Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "std/container/items";
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+
+void reset()
+{
+ ::reset();
+
+ object *inh = all_inventory(this_object());
+ if ( !pointerp(inh) || sizeof(inh) < 10 )
+ return;
+ // nur wenn keine Spieler anwesend sind.
+ if ( !sizeof(inh & users()) )
+ remove_multiple(3);
+}
+
+// Per Default nur an alle Items im Inventar weiterleiten.
+public varargs int ReceiveMsg(string msg, int msg_type, string msg_action,
+ string msg_prefix, object origin)
+{
+ int *res = all_inventory()->ReceiveMsg(msg, msg_type, msg_action,
+ msg_prefix,
+ origin || previous_object());
+ if (sizeof(res))
+ return min(res);
+ return 0;
+}
+
diff --git a/std/room/kraeuter.c b/std/room/kraeuter.c
new file mode 100644
index 0000000..24f5193
--- /dev/null
+++ b/std/room/kraeuter.c
@@ -0,0 +1,240 @@
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#include <thing/language.h>
+#include <thing/description.h>
+#include <thing/commands.h>
+#undef NEED_PROTOTYPES
+#include <moving.h>
+#include <items/kraeuter/kraeuter.h>
+#include <defines.h>
+#include <living/combat.h> // Fuer P_FREE_HANDS
+
+// Standardwert von 2 h fuer vollstaendige Regeneration des Krautes.
+// Einfaches Nachwachsen ist typischerweise die Haelfte davon.
+#define STD_WACHSTUM 7200
+#define BS(x) break_string(x, 78)
+
+// Struktur (6 Eintraege pro Kraut):
+// ([ filename : ({ Zeit_bis_nachgewachsen, Zeit_bis_komplett_regeneriert,
+// Kraut-IDs, Kraut-Adjektive, Kraut->Name(WER,1),
+// ID_des_Bewacher-NPCs }) ])
+mapping plantMap = ([]);
+
+// kann benutzt werden um schnell und einfach eine Pflanze in einem Raum
+// hinzuzufuegen. Beispiel: AddPlant(BAERENKLAU);
+// Diese Funktion erzeugt automatisch ein AddCmd() fuer das Pfluecken und
+// (falls noch nicht vorhanden) Details fuer die Pflanze.
+// Rueckgabewerte:
+// 1 -> Erfolg; -1 -> filename ungueltig
+varargs int AddPlant(string filename, string|string* npcId)
+{
+ mixed arr;
+
+ // Dateiname nicht uebergeben? Dann tun wir erstmal gar nix.
+ if (!stringp(filename))
+ return -1;
+ object ob=load_object(filename);
+
+ // Wenn wir zu dem Kraut schon Daten haben (erkennbar an >2 Eintraegen),
+ // werfen wir einen Fehler, damit das beim Laden des Raumes schon
+ // erkannt wird.
+ if (pointerp(arr=plantMap[filename]) && sizeof(arr)>2)
+ raise_error("AddPlant(): "+filename+" already exists.\n");
+
+ // IDs und Adjektive parsen und den Datensatz zusammenstellen
+ string *ids = ob->Query(P_IDS, F_VALUE)-({ "Ding" });
+ string *adj = ob->Query(P_ADJECTIVES, F_VALUE);
+
+ if (!pointerp(arr) || sizeof(arr)<2)
+ arr = ({0,0});
+ if ( !npcId )
+ npcId = ({});
+ else if (stringp(npcId))
+ npcId = ({npcId});
+ plantMap[filename]=({arr[0], arr[1], ids, adj, ob->Name(WER, 1), npcId });
+
+ // Details erzeugen aus Adjektiven und IDs
+ adj = ob->QueryProp(P_NAME_ADJ);
+
+ // aktuelles Geschlecht zwischenspeichern, wird spaeter wiederhergestellt
+ int gender = Query(P_GENDER, F_VALUE);
+ Set(P_GENDER, ob->Query(P_GENDER, F_VALUE), F_VALUE);
+
+ // erzeugt fuer jede moegliche Kombination aus Adjektiv im Akkusativ
+ // und ID des Krautes ein Detail.
+ adj = map(adj, #'DeclAdj, WEN, 0);
+
+ string *det=({});
+ foreach(string _id : ids) {
+ foreach(string _adj : adj) {
+ det += ({ _adj + _id });
+ }
+ }
+
+ det += ids;
+ // keine existierenden Details ueberschreiben
+ det -= m_indices(Query(P_DETAILS, F_VALUE) || ([]));
+ if (sizeof(det))
+ AddDetail(det, ob->Query(PLANT_ROOMDETAIL, F_VALUE));
+
+ // Eine Befehlsfunktion brauchen wir natuerlich auch.
+ AddCmd(({"pflueck", "pfluecke", "ernte"}), "_pfluecken");
+
+ return 1;
+}
+
+// Wenn jemand per Hand das Plantdetail hinzufuegen moechte...
+// z.B. bei Verwendung von GetPlant() anstelle von AddPlant()
+void AddPlantDetail(string filename)
+{
+ // Pfad in Objektpointer wandeln
+ object ob=load_object(filename);
+
+ // Details erzeugen
+ string *det = ({});
+ string *ids = ob->Query(P_IDS, F_VALUE)-({ "Ding" });
+ string *adj = ob->QueryProp(P_NAME_ADJ);
+ // aktuelles Geschlecht zwischenspeichern, wird spaeter wiederhergestellt
+ int gender=Query(P_GENDER, F_VALUE);
+ Set(P_GENDER, ob->Query(P_GENDER, F_VALUE));
+ // erzeugt fuer jede moegliche Kombination aus Adjektiv im Akkusativ
+ // und ID des Krautes ein Detail.
+ adj = map(adj, #'DeclAdj, WEN, 0);
+ foreach(string _id : ids) {
+ foreach(string _adj : adj) {
+ det += ({ _adj + _id });
+ }
+ }
+ AddDetail(det+ids, ob->Query(PLANT_ROOMDETAIL, F_VALUE));
+ // Geschlecht zuruecksetzen
+ Set(P_GENDER, gender, F_VALUE);
+}
+
+// Prueft, ob die Pflanze zu "filename" in diesem Raum schon nachgewachsen
+// ist.
+protected int CheckPlant(string filename)
+{
+ mixed arr=plantMap[filename];
+ if (!pointerp(arr) || sizeof(arr)<2) {
+ arr=plantMap[filename]=({ 0, 0 });
+ }
+ // Solange die Zeit arr[0] noch nicht erreicht ist, ist das Kraut nicht
+ // nachgewachsen, dann gibt es gar nix.
+ return (time()>arr[0]);
+}
+
+// Moechte man AddPlant() nicht benutzen, weil man die Pflanze nicht einfach
+// pfluecken, sondern vielleicht abschneiden, oder ausgraben soll, so kann
+// man sich mittels GetPlant(filename) das Objekt erzeugen lassen. Gibt
+// GetPlant() 0 zurueck, ist die Pflanze noch nicht wieder weit genug
+// nachgewachsen.
+object GetPlant(string filename)
+{
+ int *arr=plantMap[filename];
+ if (!pointerp(arr) || sizeof(arr)<2)
+ {
+ arr=plantMap[filename]=({ 0, 0 });
+ }
+ // arr[0] enthaelt den Zeitpunkt, wann das Kraut nachgewachsen ist,
+ int nachgewachsen = arr[0];
+ // arr[1] denjenigen, wann es vollstaendig regeneriert ist.
+ int regeneriert = arr[1];
+
+ // Vor dem Nachgewachsen-Zeitpunkt kann man gar nix ernten.
+ if (time()<nachgewachsen) return 0; // noch nicht nachgewachsen
+
+ // Restzeit bis zur vollstaendigen Regeneration.
+ regeneriert-=time();
+
+ // Wenn vollstaendig regeneriert, ist STD_WACHSTUM die neue Zeit bis zur
+ // Regeneration. Wenn noch nicht vollstaendig regenriert, Restzeit
+ // verdoppeln und STD_WACHSTUM nochmal drauf addieren.
+ regeneriert = (regeneriert<=0 ? STD_WACHSTUM
+ : (regeneriert*2)+STD_WACHSTUM);
+ // nachgewachsen ist die halbe Regenerationszeit
+ arr[0]=nachgewachsen=time()+(regeneriert/2);
+ // Zeit voelliger Regeneration
+ arr[1]=regeneriert+=time();
+
+ return clone_object(filename);
+}
+
+static int _pfluecken(string str)
+{
+ int res;
+
+ if (!mappingp(plantMap)) return 0;
+ _notify_fail("WAS moechtest Du pfluecken?\n");
+
+ // IDs und Adjektive zwischenspeichern
+ mixed ids = Query(P_IDS, F_VALUE);
+ mixed adj = Query(P_ADJECTIVES, F_VALUE);
+
+ foreach(string key, mixed krautinfo : plantMap)
+ {
+ if ( sizeof(krautinfo) != 6 )
+ continue;
+
+ // IDs und Adjektive des Krautes kopieren
+ Set(P_IDS, krautinfo[2], F_VALUE);
+ Set(P_ADJECTIVES, krautinfo[3], F_VALUE);
+
+ // Syntaxpruefung wird dann mit id() gemacht.
+ if (id(str))
+ {
+ object ob;
+ object bewacher;
+ res=1;
+
+ // Liste der eingetragenen Bewacher-IDs durchlaufen und pruefen, ob
+ // mindestens einer davon anwesend ist.
+ foreach( string npcId : krautinfo[5] )
+ {
+ bewacher = present(npcId, ME);
+ if (objectp(bewacher))
+ break;
+ }
+
+ if ( !PL->QueryProp(P_FREE_HANDS) )
+ {
+ tell_object(PL, BS("Du hast keine Hand frei, um etwas pfluecken "
+ "zu koennen."));
+ }
+ // Ist der Bewacher anwesend? Dann kann man das Kraut nicht pfluecken.
+ else if ( objectp(bewacher) )
+ {
+ tell_object(PL, BS(bewacher->Name(WER, 2)+" laesst Dich "
+ "leider nicht nah genug heran. Irgendwie musst Du Dich wohl "
+ "zunaechst um "+bewacher->QueryPronoun(WEN)+" kuemmern."));
+ }
+ // Wenn GetPlant() ein Objekt liefert, kann was gepflueckt werden.
+ else if ( objectp(ob=GetPlant(key)) )
+ {
+ if ( ob->move(PL, M_GET) == MOVE_OK )
+ {
+ write(BS("Vorsichtig pflueckst Du "+ob->name(WEN, 1)+
+ " und nimmst "+ob->QueryPronoun(WEN)+" an Dich."));
+ }
+ else
+ {
+ write(BS("Vorsichtig pflueckst Du "+ob->name(WEN, 1)+", kannst "+
+ ob->QueryPronoun(WEN)+" aber nicht nehmen."));
+ ob->move(environment(PL), M_GET);
+ }
+ }
+ // Wenn alles nicht, dann ist das Kraut noch nicht wieder da.
+ else
+ {
+ write(BS(krautinfo[4]+" ist noch nicht reif genug "
+ +"und muss erst noch etwas wachsen."));
+ break;
+ }
+ }
+ }
+ // IDs und Adjektive zuruecksetzen.
+ Set(P_IDS, ids, F_VALUE);
+ Set(P_ADJECTIVES, adj, F_VALUE);
+
+ return res;
+}
+
diff --git a/std/room/kraeuterladen.c b/std/room/kraeuterladen.c
new file mode 100644
index 0000000..cf99793
--- /dev/null
+++ b/std/room/kraeuterladen.c
@@ -0,0 +1,167 @@
+// (c) 2003 by Padreic (padreic@mg.mud.de)
+//
+// Es kann bestimmte Laeden zum Handeln von Kraeutern geben.
+// Zunaechst einmal gibt es einen in der Dunkelelfengilde.
+// Hier koennen Kraeuter ge- und verkauft werden.
+// Grundsaetzlich kann es beliebig viele kraeuterkundige
+// Haendler geben, eine kurze Absprache waere jedoch von Vorteil.
+
+// Der Laden erweitert die room/shop Funktionen.
+// Zur Verwendung muss dieser noch mit einem std/room kombiniert
+// werden. Dies erlaubt die Verwendung von eigenen Standardraeumen oder
+// Kombination mit Kneipen.
+
+inherit "/std/room/shop";
+
+#define NEED_PROTOTYPES
+
+#include <items/kraeuter/kraeuter.h>
+#include <properties.h>
+
+// Flag, das im reset() das Speichern ausloest, wenn es gesetzt ist.
+static int need_save;
+static int basisvalue=400; // gibt den _Durchschnittswert_ eines Krauts an
+static int maxvalue=10000; // bestimmt den max. Wert aller Kraeuter
+
+// Enthaelt fuer jede PlantID einen Zaehler, der angibt, wieviel von dem
+// jeweiligen Kraut verkauft wurde.
+// private int *count=({});
+
+// Summe ueber alle Eintraege in count
+// private int sum;
+
+protected void create()
+{
+ if (object_name(this_object()) == __FILE__[0..<3])
+ {
+ set_next_reset(-1);
+ return;
+ }
+ ::create();
+
+ SetProp(P_BUY_ONLY_PLANTS,1);
+ /*
+ seteuid(getuid()); // wird zum speichern benötigt
+ int si=sizeof(count);
+ // fuer jede PlantID einen Eintrag anlegen, d.h. wenn in count noch
+ // nicht genug vorhanden sind, werden fehlende Eintraege ergaenzt.
+ if (si<PLANTCOUNT)
+ count=count+allocate(PLANTCOUNT-si);
+ */
+}
+
+protected void create_super()
+{
+ set_next_reset(-1);
+}
+
+static string sell_obj_only_plants(object ob, int short)
+{
+ if (!IS_PLANT(ob))
+ return "Tut mir leid, damit kann ich nichts anfangen.";
+ // es werden nur Kraeuter angekauft, die legal geclont wurden,
+ // d.h. im entsprechenden Master ordentlich eingetragen wurden!
+ if (ob->QueryPlantId()<=0)
+ return ob->Name(WER, 2)+" ist illegal auf die Welt gekommen. Ich darf "
+ +ob->QueryPronoun(WEN)+" leider nicht annehmen.";
+ return ::sell_obj(ob, short);
+}
+
+static string sell_obj(object ob, int short)
+{
+ // es werden nur Kraeuter angekauft, die legal geclont wurden,
+ // d.h. im entsprechenden Master ordentlich eingetragen wurden!
+ if (IS_PLANT(ob) && ob->QueryPlantId()<=0)
+ return "Hm, "+ob->Name(WER, 2)+" stammt aber aus einer sehr dubiosen "
+ "Quelle. Ich kann "+ob->QueryPronoun(WEN)+" leider nicht annehmen.";
+ if (QueryProp(P_BUY_ONLY_PLANTS))
+ return sell_obj_only_plants(ob, short);
+ return ::sell_obj(ob,short);
+}
+
+/*
+void reset()
+{
+ ::reset();
+ // Daten sind nicht sooo wichtig, als das bei jeder Aenderung
+ // gesavet werden muesste. Daher nun im reset speichern.
+ if (need_save) {
+ // basisvalue/PLANTCOUNT: Durchschnittswert eines einzelnen Krauts
+ // sum + PLANTCOUNT: Gesamtzahl verkaufter plus Zahl existierender
+ // Kraeuter
+ // Wenn also der Wert all dieser Kraeuter > 10k ist, wird jeder
+ // Einzelzaehler auf 90% gestutzt, damit die Werte nicht ins Uferlose
+ // steigen.
+ if (((sum+PLANTCOUNT)*basisvalue/PLANTCOUNT)>maxvalue) {
+ int i, newsum;
+ for (i=sizeof(count)-1; i>=0; i--) {
+ count[i] = count[i]*9/10;
+ newsum+=count[i];
+ }
+ sum=newsum;
+ }
+ need_save=0;
+ }
+}
+
+// Aktualisiert den Datenbestand beim Kaufen oder Verkaufen eines Objekts
+void UpdateCounter(object ob, int num)
+{
+ int id=ob->QueryPlantId();
+ if (id>0 && is_plant(ob)) {
+ // Kauf oder Verkauf von Kraeutern, veraendert den Wert der
+ // Kraeuter
+ // Zaehler in der Liste hochzaehlen
+ count[id]+=num;
+ if (count[id]<0) count[id]=0; // darf aber ansich nich passieren
+ // Gesamtsumme aktualisieren
+ sum+=num;
+ need_save=1;
+ }
+ ::UpdateCounter(ob, num);
+}
+
+// Die Preise die hier im Labor fuer Kraeuter gezahlt und verlangt
+// werden, sind nicht fix sondern haengen von Angebot und Nachfrage ab.
+// Hier weiss man ueber den wahren Wert der einzelnen Kraeuter bescheid.
+static int realValue(object ob, object player)
+{
+ // Preise fuer normale Gueter ganz normal...
+ if (!is_plant(ob))
+ return ob->QueryProp(P_VALUE);
+
+ // jede Kraeuterkategorie bekommt den gleichen Wert zugewiesen.
+ // val entspricht dem aktuellen "Durchschnittswert" eines Krautes
+ int val=(sum+PLANTCOUNT)*basisvalue/PLANTCOUNT;
+
+ // aber dieser Wert verteilt sich auf unterschiedlich viele Kraeuter
+ // (AN: Dieser Kommentar erschliesst sich mir nicht.)
+ int id=ob->QueryPlantId();
+ if (id<=0) return 0; // illegal geclont -> wertlos
+ // ggf. die Zaehlerliste um die fehlenden Eintraege erweitern.
+ if ((id-1)>sizeof(count))
+ count=count+allocate(id-1-sizeof(count));
+
+ // "mittleren Wert" des abgefragten Krautes errechnen (Division durch
+ // dessen umgesetzte Anzahl)
+ val=val/(count[id]+1);
+
+ // Wert runden auf naechstniedrigeren glatt durch 10 teilbaren Wert.
+ return val-val%10;
+}
+
+// gibt den Preis zurück, der zum Ankauf des Objektes verwendet werden soll
+static varargs int QueryValue(object ob, int value, object player)
+{
+ return ::QueryValue(ob, realValue(ob, player), player);
+}
+
+// gibt den Preis an, zu dem das Objekt verkauft werden soll.
+static varargs int QueryBuyValue(mixed ob, object player)
+{
+ if (objectp(ob))
+ return (realValue(ob, player)*QueryBuyFact(player) + 50)/100;
+ // fixed Objekte...
+ return ::QueryBuyValue(ob, player);
+}
+*/
diff --git a/std/room/light.c b/std/room/light.c
new file mode 100644
index 0000000..a372047
--- /dev/null
+++ b/std/room/light.c
@@ -0,0 +1,61 @@
+// MorgenGrauen MUDlib
+//
+// container/light.c -- Lichtsystemkomponenten fuer Raeume
+//
+// $Id: description.c 6986 2008-08-22 21:32:15Z Zesstra $
+
+inherit "/std/container/light";
+
+#pragma strict_types
+#pragma save_types,rtt_checks
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#define NEED_PROTOTYPES
+#include <thing/properties.h>
+#undef NEED_PROTOTYPES
+
+#include <thing/description.h>
+#include <room/description.h>
+#include <container.h>
+
+protected void create()
+{
+ ::create();
+ SetProp(P_LIGHT_ABSORPTION, 1);
+}
+
+protected void create_super() {set_next_reset(-1);}
+
+/*
+ // das wird selten benutzt und ist zur zeit funktionsunfaehig, da die
+ // interne Speicherung der Prop sich geaendert hat.
+static int _set_int_light(int *light)
+{
+ int tmp;
+
+ // zur Sicherheit
+ if (!pointerp(light)) return -1;
+ if (light[0]>QueryProp(P_LIGHT)) {
+ // Licht verlaeuft sich in einem grossen Raum, daher Modifier abfragen...
+ tmp=light[0]-QueryProp(P_LIGHT_ABSORPTION);
+ // wenn sich das Vorzeichen geaendert hat, auf 0 setzen.
+ light[0]=((tmp^light[0]) & 0x80000000 ? 0 : tmp);
+ }
+ if (light[1]<QueryProp(P_LIGHT) && light[1]<0) {
+ // Licht verlaeuft sich in einem grossen Raum, daher Modifier abfragen...
+ tmp=light[1]+QueryProp(P_LIGHT_ABSORPTION);
+ // wenn sich das Vorzeichen geaendert hat, auf 0 setzen.
+ light[1]=((tmp^light[1]) & 0x80000000 ? 0 : tmp);
+ }
+ light[2]=light[0]+light[1];
+ Set(P_INT_LIGHT, light, F_VALUE);
+ // diese Prop setzen kaum Leute (offiziell gehts ja auch gar nicht. Keiner
+ // davon erwartet nen Rueckgabewert. Daher wird hier 0 zurueckgeben, statt
+ // des aufwaendig berechneten QueryProp(P_INT_LIGHT).
+ // Achja. Der Rueckgabewert vom Set() waere ein int*, was nicht geht, weil
+ // diese Funktion nur int zurueckgeben darf.
+ return 0;
+}
+*/
diff --git a/std/room/moving.c b/std/room/moving.c
new file mode 100644
index 0000000..0f7c2b1
--- /dev/null
+++ b/std/room/moving.c
@@ -0,0 +1,17 @@
+// MorgenGrauen MUDlib
+//
+// room/moving.c -- Entfernen von Raeumen
+//
+// $Id: moving.c 8041 2012-03-19 18:38:21Z Zesstra $
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+public varargs int remove(int silent)
+{
+ destruct( this_object() );
+ return 1;
+}
diff --git a/std/room/para.c b/std/room/para.c
new file mode 100644
index 0000000..4666782
--- /dev/null
+++ b/std/room/para.c
@@ -0,0 +1,18 @@
+// This may look like C code, but it is really -*- C++ -*-
+
+// MorgenGrauen MUDlib
+//
+// room/para.c -- Betreten einer Parallelwelt
+//
+// $Id: para.c 7510 2010-03-25 23:37:19Z Zesstra $
+
+// Veraltet - wird nicht mehr benoetigt.
+// Die Zuordnung der 'richtigen' Welt wird jetzt direkt von move() uebernommen.
+
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+deprecated int paramove() { return 0; }
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;
+}
diff --git a/std/room/restrictions.c b/std/room/restrictions.c
new file mode 100644
index 0000000..aa50c9e
--- /dev/null
+++ b/std/room/restrictions.c
@@ -0,0 +1,61 @@
+// MorgenGrauen MUDlib
+//
+// room/restrictions.c -- weight property handling for rooms
+//
+// $Id: restrictions.c 9020 2015-01-10 21:49:41Z Zesstra $
+
+inherit "std/container/restrictions";
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+//#define NEED_PROTOTYPES
+
+#include <thing/properties.h>
+#include <properties.h>
+
+static int _set_weight(int w);
+static int _query_weight();
+static int _set_total_weight(int w);
+static int _query_total_weight();
+
+void create()
+{
+ Set(P_WEIGHT,PROTECTED,F_MODE);
+ Set(P_TOTAL_WEIGHT,PROTECTED,F_MODE);
+}
+
+int MayAddWeight(int w)
+{
+ return 0;
+}
+
+int MayAddObject(object ob)
+{ return 1; }
+
+int PreventInsert(object ob)
+{
+ return 0;
+}
+
+static int _set_weight(int w)
+{
+ return 0;
+}
+
+static int _query_weight()
+{
+ return 0;
+}
+
+static int _set_total_weight(int w)
+{
+ return 0;
+}
+
+static int _query_total_weight()
+{
+ return 0;
+}
diff --git a/std/room/shop.c b/std/room/shop.c
new file mode 100644
index 0000000..c429127
--- /dev/null
+++ b/std/room/shop.c
@@ -0,0 +1,1110 @@
+#pragma strong_types
+#pragma save_types
+#pragma pedantic
+#pragma range_check
+#pragma no_clone
+
+inherit "/std/trading_price";
+
+#define NEED_PROTOTYPES
+#include <thing/commands.h>
+#include <thing/description.h>
+#undef NEED_PROTOTYPES
+
+#include <defines.h>
+#include <properties.h>
+#include <rooms.h>
+#include <language.h>
+#include <moving.h>
+#include <routingd.h>
+#include <bank.h>
+#include <combat.h>
+#include <input_to.h>
+#include <unit.h>
+#include <money.h>
+
+// TODO: pruefen, um die Variablen private sein koennen.
+
+// allgemein benoetige Variablen
+nosave string storage; // Filename des Stores in dem die Objekte lagern...
+nosave mapping ob_anz; // wie oft ist ein Objekt im Laden vorhanden?
+
+// Jetzt Variablen fuer staendig verfuegbare Objekte:
+nosave string *fixed_obj; // Liste der staendig verfuegbaren Objekte
+nosave mapping fixed_value; // Preise bei Sonderangeboten
+nosave mapping fixed_ids; // Ids
+
+varargs void AddFixedObject(string str, int val, string|string* ids)
+{
+ int i;
+
+ // Achtung, bei solchen Objekten muss die Blueprint initialisiert werden!
+ if (!str) return;
+ if (!val) val=str->QueryProp(P_VALUE);
+ if (!ids)
+ {
+ if (str->QueryProp("u_ids")) // units haben keine P_IDS
+ ids=str->QueryProp("u_ids")[0];
+ else
+ ids=str->QueryProp(P_IDS);
+ }
+ if (!pointerp(ids))
+ {
+ if (stringp(ids))
+ ids=({ids});
+ else
+ ids=({});
+ }
+
+ fixed_obj += ({str});
+ fixed_value[str] = val;
+ // Alle IDs entfernen, die Sonderzeichen enthalten. Die koennte ein Spieler
+ // bei "kaufe" ohnehin nicht eingeben.
+ ids -= regexp(ids, "[\b\n\r\t]");
+ foreach(string id : ids)
+ {
+ // Nur IDs aufnehmen, die keine Grossbuchstaben enthalten, da Spieler
+ // diese ebenfalls nicht eingeben koennte.
+ if ( lowerstring(id) == id )
+ fixed_ids[id]=str;
+ }
+}
+
+void RemoveFixedObject(string filename)
+{
+ if( !stringp(filename) || !sizeof(fixed_obj))
+ return;
+ if( member(fixed_obj, filename)==-1 )
+ return;
+
+ fixed_obj -= ({ filename });
+ m_delete(fixed_value, filename);
+
+ foreach(string id, string file : fixed_ids)
+ {
+ if ( file == filename )
+ m_delete(fixed_ids, id);
+ }
+}
+
+static string SetStorageRoom(string str)
+{
+ if (str && stringp(str)) return storage=str;
+ return 0;
+}
+
+string QueryStorageRoom()
+{ return storage; }
+
+protected void create()
+{
+ object router;
+
+ if (object_name(this_object()) == __FILE__[0..<3])
+ {
+ set_next_reset(-1);
+ return;
+ }
+
+ trading_price::create();
+
+ SetProp( P_NAME, "Haendler" );
+ SetProp( P_GENDER, MALE );
+ SetProp( P_ROOM_TYPE, QueryProp(P_ROOM_TYPE) | RT_SHOP );
+
+ AddCmd("zeige","list");
+ AddCmd(({"kauf","kaufe","bestell","bestelle"}),"buy");
+ AddCmd(({"verkauf","verkaufe","verk"}),"sell");
+ AddCmd(({"versetz","versetze"}),"force_sell");
+ AddCmd(({"schaetz","schaetze"}),"evaluate");
+ AddCmd(({"untersuche","unt"}), "show_obj");
+
+ SetTradingData(50000,300,3);
+
+ ob_anz=([]);
+ fixed_obj=({});fixed_value=([]);fixed_ids=([]);
+
+ AddFixedObject(BOERSE, 80,({ "boerse","geldboerse"}));
+
+ if (!clonep(ME) && objectp(router=find_object(ROUTER)))
+ router->RegisterTarget(TARGET_SHOP,object_name(ME));
+}
+
+protected void create_super() {
+ set_next_reset(-1);
+}
+
+// Legacy-Version von GetShopItems() fuer die erbenden Laeden, die auf
+// die Funktion in dieser Form angewiesen sind.
+static mixed *GetList()
+{
+ object store = load_object(storage);
+ store->_register_shop(ME);
+
+ mixed *output=({});
+ if (!objectp(store))
+ return output;
+
+ mixed tmp = map(fixed_obj, #'load_object)+all_inventory(store);
+ mapping tmp2 = ([]);
+ mixed str;
+ string comp;
+ int i;
+ int s=1;
+
+ for (i=sizeof(tmp)-1 ; i>=0 ; i--)
+ {
+ str = ({ ({ sprintf("%-25.25s%7.7d",
+ (tmp[i]->short()||"???")[0..<3],
+ QueryBuyValue(tmp[i], PL)),
+ tmp[i] }) });
+ comp=str[0][0][0..25];
+ if (!tmp2[comp])
+ {
+ tmp2[comp] = s++;
+ output += str;
+ }
+ else output[tmp2[comp]-1][0] = str[0][0];
+ }
+ return output;
+}
+
+// Legacy-Version von PrintList() fuer die erbenden Laeden, die auf
+// die Funktion in dieser Form angewiesen sind.
+static int DoList(string query_fun)
+{
+ mixed* output=GetList();
+ int si = sizeof(output);
+ if (!si)
+ {
+ write("Im Moment sind wir leider VOELLIG ausverkauft!\n");
+ return 1;
+ }
+ string out="";
+ int i;
+ int indent;
+ for (i=0; i<si; i++)
+ {
+ if (call_other(ME, query_fun, output[i][1]))
+ {
+ indent = !indent;
+ out += sprintf("%3d. %s", i+1, output[i][0]);
+ if (!indent)
+ out += "\n";
+ else out += " | ";
+ }
+ }
+ if (indent)
+ out+="\n";
+ PL->More(out);
+ return 1;
+}
+
+// Liefert eine Liste der Objekte zurueck, die gerade im Storage liegen,
+// pro Blueprint jeweils eins.
+protected object* GetShopItems()
+{
+ object store = load_object(storage);
+ store->_register_shop(ME);
+ object* output = ({});
+ object* all_items = all_inventory(store);
+
+ // Wir brauchen eine Liste, die von jeder Blueprint nur einen Clone
+ // enthaelt. Daher werden die Ladenamen der Objekte als Keys im Mapping
+ // <items> verwendet und jeweils der aktuelle Clone als Value zugeordnet.
+ mapping items = m_allocate(sizeof(all_items));
+ foreach(object ob: all_items)
+ {
+ items[load_name(ob)] = ob;
+ }
+ // Die Fixed Objects werden ans Ende angehaengt, damit in dem Fall, dass
+ // ein Clone eines solchen Objektes im Lager liegt, dieser zuerst verkauft
+ // wird und nicht immer wieder ein neuer erstellt wird.
+ return m_values(items) + map(fixed_obj, #'load_object);
+}
+
+#define LIST_LONG 1
+#define LIST_SHORT 0
+
+// Kuemmert sich um die Listenausgabe fuer den Befehl "zeige"
+varargs protected int PrintList(string filter_fun, int liststyle)
+{
+ // Alle Items im Lager holen. Wenn keine vorhanden, tut uns das leid.
+ object *items_in_store = GetShopItems();
+ if ( !sizeof(items_in_store) ) {
+ write("Im Moment sind wir leider VOELLIG ausverkauft!\n");
+ return 1;
+ }
+
+ // Das Listenformat ist von der Spielereingabe abhaengig. Wurde "lang"
+ // angefordert, geben wir einspaltig aus mit groesserer Spaltenbreite.
+ // Die Spaltenbreite wird dabei von dem Item mit dem laengsten Namen im
+ // gesamten Shop-Inventar bestimmt, damit nicht bei jeder Teilliste
+ // (Waffen, Ruestungen, Verschiedenes) unterschiedliche Breiten rauskommen.
+ //
+ // Der erste Parameter enthaelt die Katalognummer des Items, der zweite
+ // die Kurzbeschreibung, der dritte den Preis.
+ string listformat = "%3d. %-25.25s %6.6d";
+ if ( liststyle == LIST_LONG )
+ {
+ string* names = sort_array(
+ items_in_store->short(),
+ function int (string s1, string s2) {
+ return (sizeof(s1) < sizeof(s2));
+ });
+ // Wenn einspaltig ausgegeben wird, soll die Liste nicht beliebig breit
+ // werden. Daher wird die Short auf 65 Zeichen limitiert.
+ int len = 65;
+ if ( sizeof(names) )
+ len = min(len, sizeof(names[0]));
+ listformat = "%3d. %-"+len+"."+len+"s %6.6d";
+ }
+
+ string out="";
+ // Variablen, die innerhalb der Schleife benoetigt werden.
+ string kurz;
+ int indent, preis;
+ object item;
+ // Ueber die Liste laufen. <i> wird benoetigt, um die ausgegebene Liste
+ // konsistent numerieren zu koennen, damit kaufe <nr> funktioniert.
+ foreach(int i : sizeof(items_in_store))
+ {
+ item = items_in_store[i];
+ if ( call_other(ME, filter_fun, item) )
+ {
+ // Kurzbeschreibung und Preis ermitteln. Items ohne Short werden
+ // als "?" angezeigt.
+ kurz = (item->short() || "???")[0..<3];
+ preis = QueryBuyValue(item, PL);
+ // Beschreibung des Items anfuegen.
+ out += sprintf(listformat, i+1, kurz, preis);
+ indent = !indent;
+ // Wenn indent gesetzt ist, handelt es sich um das linke Item in der
+ // zweispaltigen Liste, dann fuegen wir einen Spaltentrenner an,
+ // ansonsten ist es das rechte, dann brechen wir um.
+ // Gilt natuerlich nur fuer kurze Listen.
+ out += ((indent && liststyle==LIST_SHORT)? " | " : "\n");
+ }
+ }
+ // Wenn die Liste eine ungerade Zahl Items enthaelt, ist in der letzten
+ // Zeile links ein Item aufgefuehrt, daher muss danach umbrochen werden.
+ // Gilt natuerlich nur fuer kurze Listen
+ if (indent && liststyle==LIST_SHORT)
+ out+="\n";
+
+ // Vor den Listen wird eine Info-Zeile ausgegeben, um gefilterte Listen
+ // kenntlich zu machen. Wird nach der Filterung des Inventars erzeugt,
+ // um eine leere Meldung ausgeben zu koennen, wenn nach Filterung nichts
+ // mehr uebrigbleibt.
+ string was;
+ switch(filter_fun)
+ {
+ case "IsArmour": was = "Ruestungen"; break;
+ case "IsWeapon": was = "Waffen"; break;
+ case "NoWeaponNoArmour":
+ was = (out==""?"sonstigen Waren":"sonstige Waren"); break;
+ default: was = "Waren"; break;
+ }
+ // <out> ist ein Leerstring, wenn keine Waren da sind, die dem Filterkri-
+ // terium entsprechen. Dann gibt's eine entsprechende Meldung.
+ if ( out == "" )
+ out = sprintf("Leider sind momentan keine %s im Angebot.\n", was);
+ else
+ out = sprintf("Folgende %s kannst Du hier kaufen:\n",was) + out;
+
+ PL->More(out);
+ return 1;
+}
+
+// Hilfsfunktionen zum Filtern des Ladeninventars fuer den "zeige"-Befehl
+static int AlwaysTrue(object ob)
+{ return 1; }
+
+static string IsWeapon(object ob)
+{ return ob->QueryProp(P_WEAPON_TYPE); }
+
+static string IsArmour(object ob)
+{ return ob->QueryProp(P_ARMOUR_TYPE); }
+
+static int NoWeaponNoArmour(object ob)
+{ return (!ob->QueryProp(P_WEAPON_TYPE) && !ob->QueryProp(P_ARMOUR_TYPE)); }
+
+
+// Diese Funktion ist oeffentlich, falls Magier abfragen wollen, ob ein laden
+// ein Objekt zerstoeren wuerde. Aber: Benutzung auf eigenes Risiko! Es wird
+// nicht garantiert, dass diese Funktion bzw. ihr Interface sich nicht
+// aendert.
+public int CheckForDestruct(object ob) // Pruefen, ob zerstoert werden soll
+{
+ string str;
+ /*
+ * P_NOBUY - Objekte auf jeden Fall zerstoeren
+ */
+ if(ob->QueryProp(P_NOBUY)) return 1;
+ /*
+ * Beschaedigte Objekte werden ebenfalls zerstoert
+ */
+ if(ob->QueryProp(P_DAMAGED)) return 1;
+ /*
+ * Ruestungen wenn sie a) eine DefendFunc definiert haben oder
+ * b) ueber der in KEEP_ARMOUR_CLASS definierten AC
+ * liegen (siehe /sys/combat.h)
+ */
+ if(str = IsArmour(ob))
+ {
+ if(objectp(ob->QueryProp(P_DEFEND_FUNC))) return 1;
+ if(ob->QueryProp(P_AC) >= KEEP_ARMOUR_CLASS[str]) return 1;
+ return 0;
+ }
+ /*
+ * Waffen wenn sie a) 1-haendig sind und eine WC > 120 besitzen oder
+ * b) 2-haendig sind und eine WC > 150 besitzen oder aber
+ * c) eine HitFunc definiert haben
+ */
+ if(str = IsWeapon(ob))
+ {
+ if(ob->QueryProp(P_NR_HANDS) > 1 && ob->QueryProp(P_WC) > 150) return 1;
+ if(ob->QueryProp(P_NR_HANDS) < 2 && ob->QueryProp(P_WC) > 120) return 1;
+ if(objectp(ob->QueryProp(P_HIT_FUNC))) return 1;
+ return 0;
+ }
+ return 0;
+}
+
+static int list(string str)
+{
+ _notify_fail(
+ "Bitte 'zeige', 'zeige waffen', 'zeige ruestungen' oder\n"
+ "'zeige verschiedenes' eingeben. Wenn Du das Schluesselwort 'lang'\n"
+ "oder '-1' anhaengst, wird die Liste einspaltig ausgegeben.\n");
+
+ if (!stringp(str) || !sizeof(str) )
+ return PrintList("AlwaysTrue");
+ if ( str == "lang" || str == "-1" )
+ return PrintList("AlwaysTrue", LIST_LONG);
+
+ string *params = explode(str," ");
+ if (sizeof(params[0])<3)
+ return 0;
+
+ int liststyle = LIST_SHORT;
+ if ( sizeof(params)>1 && params[1] == "lang" )
+ liststyle = LIST_LONG;
+
+ str=params[0][0..2];
+ if (str=="waf")
+ return PrintList("IsWeapon", liststyle);
+ if (str=="ver")
+ return PrintList("NoWeaponNoArmour", liststyle);
+ if (str=="rue")
+ return PrintList("IsArmour", liststyle);
+ return 0;
+}
+/*
+static varargs int QueryBuyValue(mixed ob, object client)
+{
+ if (objectp(ob))
+ return trading_price::QueryBuyValue(ob, client);
+ return (fixed_value[ob]*QueryBuyFact(client))/100;
+}
+*/
+
+static varargs int QueryBuyValue(object ob, object client)
+{
+ int fprice = fixed_value[load_name(ob)];
+
+ return (fprice>0) ?
+ fprice*QueryBuyFact(client)/100 :
+ trading_price::QueryBuyValue(ob, client);
+}
+
+static void UpdateCounter(object ob, int num)
+{
+ string tmp;
+
+ if (!num || !objectp(ob)) return;
+ tmp=BLUE_NAME(ob);
+ if (tmp[0..2]!="/d/" && tmp[0..8]!="/players/")
+ tmp=ob->short()+tmp;
+ ob_anz[tmp] += num;
+ if (ob_anz[tmp] <= 0)
+ m_delete(ob_anz,tmp);
+}
+
+protected object FindInStore(string|int x)
+{
+ object* list = GetShopItems();
+ if ( intp(x) && x>0 && x<=sizeof(list) ) {
+ return list[x-1];
+ }
+ if (stringp(x))
+ {
+ if ( fixed_ids[x] )
+ return load_object(fixed_ids[x]);
+ list = filter_objects(list, "id", x);
+ if ( sizeof(list) )
+ return list[0];
+ // Wenn nix im Store gefunden (das schliesst eigentlicht schon die BPs der
+ // fixen Objekte ein, aber nicht, wenn diese nicht konfiguriert sind. D.h.
+ // diese Pruefung ist fuer nicht-konfigurierte BPs), Liste der
+ // FixedObjects pruefen unde so die eventuell manuell in
+ // AddFixedObject() angegebene Liste von IDs beruecksichtigen.
+ else if ( fixed_ids[x] )
+ return load_object(fixed_ids[x]);
+ }
+ return 0;
+}
+
+static string buy_obj(mixed ob, int short)
+{ return 0; }
+
+private void really_buy(int val, object pl, object ob, int u_req)
+{
+ // Staendig verfuegbare Objekte (fixed_obj) sind daran erkennbar, dass sie
+ // nicht im Lager liegen. Daher hier einen Clone erstellen, der dann
+ // stattdessen rausgegeben wird.
+ if ( !present(ob, find_object(storage)) )
+ ob = clone_object(ob);
+
+ // In Unitobjekten U_REQ (wieder) setzen (wegen input_to (bei dem sich das
+ // Kommandoverb aendert und deswegen U_REQ geloescht wird), und wegen
+ // Kaufens von Fixed-Objekt-Unitobjekten (bei diesen muss U_REQ _nach_ dem
+ // Clonen im Clone gesetzt werden, nicht automagisch in der BP durch den
+ // Aufruf von id() weiter vorher).
+ if (u_req>0)
+ {
+ // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
+ // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
+ // Bei der ersten Abfrage wuerde als das hier gesetzt U_REQ wieder
+ // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
+ // werden...
+ ob->QueryProp(U_REQ);
+ ob->SetProp(U_REQ, u_req);
+ }
+
+ pl->AddMoney(-val);
+ _add_money(val);
+
+ if (ob->move(pl,M_GET) != MOVE_OK) // Kann der Spieler das Objekt tragen?
+ {
+ write(break_string("Du kannst "+ob->name(WEN,1)+" nicht mehr tragen. "
+ "Ich lege "+ob->QueryPronoun(WEN)+" hier auf den Boden.",78,
+ Name(WER)+" sagt: "));
+ ob->move(ME,M_NOCHECK); // Nein :-)
+ }
+ else
+ {
+ // Falls das Objekt sich vereinigt hat, muss jetzt wieder U_REQ
+ // restauriert werden.
+ ob->SetProp(U_REQ, u_req);
+ write(break_string("Du kaufst "+ob->name(WEN,1)+".", 78));
+ }
+
+ say(break_string(PL->Name(WER)+" kauft "+ob->name(WEN)+".",78), ({PL}));
+ UpdateCounter(ob,-1);
+}
+
+static void ask_buy(string str, int val, object pl, object ob, int u_req)
+{
+ _notify_fail(break_string("Gut, Du kannst es Dir ja jederzeit "
+ "noch anders ueberlegen.",78,Name(WER)+" sagt: "));
+
+ if(!str || !stringp(str) || str == "nein" || str == "n")
+ {
+ return;
+ }
+ if(str != "ja" && str != "j")
+ {
+ return;
+ }
+ really_buy(val, pl, ob, u_req);
+}
+
+static int buy(string str)
+{
+ int i, val, par, dex;
+ mixed ob, res;
+ string dummy;
+
+ if (!str) {
+ write("Was willst Du kaufen?\n");
+ return 1;
+ }
+
+ if (stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME)) {
+ write("Es ist niemand da, der Dich bedienen koennte.\n");
+ return 1;
+ }
+
+ _notify_fail(break_string("Das kann ich in meinem Lager nicht finden.",78,
+ Name(WER)+" sagt: "));
+
+ // Um auch Teile von Unit-Stacks kaufen zu koennen, z.B. "kaufe 5 phiolen",
+ // darf hier zusaetzlich <dummy> nur ein Leerstring sein, sonst verzweigt
+ // die Syntaxpruefung hierhin und es wird das 5. Item der Liste gekauft.
+ if (sscanf(str,"%d%s",i,dummy)>0 && i>0 && !sizeof(dummy)) {
+ ob=FindInStore(i);
+ }
+ else ob=FindInStore(str);
+
+ if (!ob) return 0;
+
+ if (str = buy_obj(ob,0))
+ {
+ write(break_string(str,78,Name(WER)+" sagt: "));
+ return 1;
+ }
+
+ val = QueryBuyValue(ob,PL);
+
+ if (PL->QueryMoney() < val)
+ {
+ write(break_string(capitalize(ob->QueryPronoun(WER))+" wuerde "+val+
+ " Muenzen kosten, und Du hast nur "+PL->QueryMoney()+".",78,
+ Name(WER)+" bedauert: "));
+ return 1;
+ }
+
+ // Anzahl der angeforderten Einheiten vor dem Bewegen zwischenspeichern.
+ // Weil dabei im Fall von Units eine Vereinigung mit bereits im Inventar
+ // befindlichen Einheiten stattfindet, muss das ggf. nach Bewegen
+ // zurueckgesetzt werden.
+ int u_req = ob->QueryProp(U_REQ);
+
+ if ((res = ob->QueryProp(P_RESTRICTIONS)) && mappingp(res) &&
+ (res = call_other("/std/restriction_checker",
+ "check_restrictions",PL,res)) &&
+ stringp(res))
+ {
+ _notify_fail(break_string("Du koenntest "+ob->name(WEN,2)+" nicht "
+ "verwenden. Grund: "+res+"Moechtest Du "+ob->QueryPronoun(WEN)+
+ " dennoch kaufen?",78,Name(WER)+" sagt: "));
+
+ input_to("ask_buy",INPUT_PROMPT, "(ja/nein) ", val,PL,ob,u_req);
+ return 0;
+ }
+
+ par = (int)ob->QueryProp(P_PARRY);
+ dex = (int)PL->QueryAttribute(A_DEX);
+
+ if ((((par < PARRY_ONLY) && ((dex + 8) * 10) < ob->QueryProp(P_WC)) ||
+ ((par > PARRY_NOT) && ((dex + 5) * 2) < ob->QueryProp(P_AC))) &&
+ VALID_WEAPON_TYPE(ob))
+ {
+ _notify_fail(break_string("Du koenntest "+ob->name(WEN,2)+" nicht "
+ "zuecken, da Deine Geschicklichkeit dafuer nicht ausreicht. Moechtest "
+ "Du "+ob->QueryPronoun(WEN)+" dennoch kaufen?",78,
+ Name(WER)+" sagt: "));
+
+ input_to("ask_buy",INPUT_PROMPT, "(ja/nein) ", val,PL,ob,u_req);
+ return 0;
+ }
+
+ really_buy(val, PL, ob, u_req);
+
+ return 1;
+}
+
+private void give_money(int value)
+// Geld gutschreiben...
+{
+ if (!value) return;
+ write(break_string(Name(WER, 1)+" zahlt Dir "+value+" Goldstueck"
+ +(value==1?".":"e."), 78));
+ if ((PL->AddMoney(value))<=0) {
+ object mon;
+
+ write("Du kannst das Geld nicht mehr tragen!\n");
+ mon=clone_object(GELD);
+ mon->SetProp(P_AMOUNT,value);
+ mon->move(ME,M_MOVE_ALL|M_NOCHECK);
+ }
+}
+
+static int make_to_money(object ob, int value)
+// Interne Funktion, die ob versucht in das Lager zu ueberfuehren und das
+// Geld das dabei fuer den Spieler abfaellt zurueckliefert.
+{
+ string str;
+ int ret;
+
+ if (!objectp(ob) || environment(ob)==find_object(storage)) {
+ write(break_string(Name(WER, 1)+" wundert sich ueber Dich.", 78));
+ return 0;
+ }
+ if (value>QueryProp(P_CURRENT_MONEY)) {
+ write(break_string("Ich hab das Geld leider nicht mehr.", 78,
+ Name(WER, 1)+" sagt: "));
+ return 0;
+ }
+ // U_REQ merken, falls sich Objekte vereinigen. Sonst stimmt nicht nur der
+ // Name, sondern es werden ggf. auch die falsche Anzahl zerstoert.
+ //TOOO: Oder Units entsorgen und als Feature deklarieren?
+ int req = ob->QueryProp(U_REQ);
+ if (CheckForDestruct(ob) > 0) // soll ob zerstoert werden?
+ {
+ ret = ob->move(storage,M_PUT|M_GET);
+ // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
+ // Einheiten restauriert werden.
+ // Problem: falls das verkaufte Objekt Units hat, beschaedigt ist und sich
+ // vereinigt hat, sind jetzt leider alle Einheiten im Lager beschaedigt.
+ // Das ist unschoen - aber mir jetzt zuviel AUfwand, das korrekt zu bauen,
+ // weil es nur sehr selten vorkommt. (Hint: separater Muellraum)
+ ob->SetProp(U_REQ, req);
+ if (ret > 0) // Sonst werden auch Sachen zerstoert, die man nicht
+ { // weglegen kann
+ say(break_string(PL->Name()+" verkauft "+ob->name(WEN)+".", 78));
+ if(ob->QueryProp(P_DAMAGED)) // Andere Meldung bei beschaedigten
+ { // Objekten ...
+ write(break_string(Name(WER,1)+" findet zwar keinen Gefallen an "
+ +ob->name(WEM,1)+", nimmt "+ob->QueryPronoun(WEN)+" Dir zuliebe "
+ "aber trotzdem.",78));
+ }
+ else
+ {
+ write(break_string(Name(WER, 1)+" findet Gefallen an "
+ +ob->name(WEM, 1) + " und legt "+ob->QueryPronoun(WEN)
+ +" zu "+(QueryProp(P_GENDER)==FEMALE?"ihren":"seinen")
+ +" Privatsachen.", 78));
+ }
+ /* Er zahlt Dir "+value+" Muenze"+(value==1?"":"n")+" dafuer.", 78)); */
+ _add_money(-value);
+ _add_money(value*QueryProp(P_SHOP_PERCENT_LEFT)/100); // Wegen Zerstoerung des Objektes
+ UpdateCounter(ob,1);
+ ob->remove(1);
+ return value;
+ }
+ else if (ret == ME_CANT_BE_DROPPED) {
+ if ((str=ob->QueryProp(P_NODROP)) && stringp(str)) {
+ write(str);
+ return 0;
+ }
+ write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+ return 0;
+ }
+ else
+ write(break_string(ob->Name(WER)+" interessiert mich nicht.", 78,
+ Name(WER, 1)+" sagt: "));
+ }
+ else if ((ret=(ob->move(storage,M_PUT|M_GET)))>0)
+ {
+ // Falls das Objekt sich vereinigt hat (Units), muessen die gewuenschten
+ // Einheiten restauriert werden.
+ ob->SetProp(U_REQ, req);
+ say(break_string(PL->Name(WER)+" verkauft "+ob->name(WEN)+".", 78));
+ _add_money(-value);
+ UpdateCounter(ob,1);
+ return value;
+ }
+ else if (ret == ME_CANT_BE_DROPPED) {
+ if ((str=ob->QueryProp(P_NODROP)) && stringp(str))
+ write(str);
+ else write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+ }
+ else write(break_string("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!", 78));
+ return 0;
+}
+
+static void ask_sell(string str, object ob, int val, int u_req)
+// Wenn ein einzelnen Stueck unter Wert verkauft werden soll, wird nachgefragt
+// u_req ist bei Unitobjekten die Anzahl an zu verkaufenden Einheiten. Bei
+// normalen Objekten ist u_req 0.
+{
+ str=lower_case(str||"");
+ if (str=="ja"||str=="j")
+ {
+ // In Unitobjekten U_REQ (wieder) setzen.
+ if (u_req>0)
+ {
+ // Das QueryProp() ist nicht unnoetig. Bei der Abfrage von U_REQ wird
+ // U_REQ genullt, wenn das aktuelle query_verb() != dem letzten ist.
+ // Bei der ersten Abfrage wuerde als das hier gesetzt U_REQ wieder
+ // geloescht. Daher muss das jetzt hier als erstes einmal abgefragt
+ // werden...
+ ob->QueryProp(U_REQ);
+ ob->SetProp(U_REQ, u_req);
+ }
+ give_money(make_to_money(ob,val));
+ }
+ else
+ write(break_string("Ok, dann behalts!", 78,
+ Name(WER, 1)+" sagt: "));
+}
+
+static string sell_obj(object ob, int short)
+// Ist der Haendler bereit ob zu kaufen? wenn nein, Rueckgabe einer Meldung,
+// die der Haendler sagen soll.
+{ mixed nosell;
+
+ if (BLUE_NAME(ob)==GELD)
+ return "Das waere ja wohl Unsinn, oder ...?";
+ if (nosell=ob->QueryProp(P_NOSELL))
+ {
+ if (stringp(nosell))
+ return nosell;
+ return ("Du kannst "+ob->name(WEN,1)+" nicht verkaufen!");
+ }
+ if (ob->QueryProp(P_CURSED))
+ return ob->Name(WER,1)
+ +" ist mir irgendwie ungeheuer! Das kannst Du nicht verkaufen!";
+ // man sollte keine COntainer mit Inhalt verkaufen koennen, ggf. kauft sie
+ // dann ein anderer Spieler.
+ if (first_inventory(ob))
+ {
+ return ob->Name(WER, 1) + " ist nicht leer!";
+ }
+ return 0;
+}
+
+static varargs int sell(string str, int f)
+{
+ int i, val, oval, tmp;
+ object *obs;
+
+ if (stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER),ME)) {
+ write("Es ist niemand da, der Dich bedienen koennte.\n");
+ return 1;
+ }
+
+ if (!str) {
+ notify_fail("Was moechtest Du denn verkaufen?\n");
+ return 0;
+ }
+
+ /* Ergebnis von find_obs() sollte unifiziert werden, damit ein mehrfach
+ gefundenes Objekt nicht mehrfach versucht wird zu verkaufen.
+ Beispiel: Objekt hat P_NOBUY gesetzt und mehrere IDs gesetzt. Wenn
+ ein Spieler es mit "verkaufe ID1 und ID2" versucht zu verkaufen,
+ wuerde das einen Bug ausloesen. Derselbe Bug entsteht, wenn man mit
+ "verkaufe ID1 und ID1" verkauft. */
+ obs = PL->find_obs(str, PUT_GET_DROP);
+ /* Erst im Inventar schauen, dann im Environment. find_obs() ohne 2.
+ Parameter macht das standardmaessig andersherum.
+ TODO: Aenderung ueberpruefen, sobald das neue put_and_get.c
+ eingebaut wurde. */
+ if ( !sizeof(obs) )
+ obs = PL->find_obs(str, PUT_GET_TAKE) || ({});
+ obs = m_indices(mkmapping(obs));
+ if (!i=sizeof(obs)) {
+ notify_fail("Was moechtest Du denn verkaufen?\n");
+ return 0;
+ }
+ call_other(storage, "_register_shop", ME);
+ if (i==1) {
+ if (str=sell_obj(obs[0], 0)) {
+ write(break_string(str, 78, Name(WER, 1)+" sagt: "));
+ return 1;
+ }
+ if ((oval=obs[0]->QueryProp(P_VALUE))<=0) {
+ write(break_string(obs[0]->Name(WER)
+ +(obs[0]->QueryProp(P_PLURAL) ? " haben" : " hat")
+ +" keinen materiellen Wert, tut mir leid.", 78,
+ Name(WER, 1)+" sagt: "));
+ return 1;
+ }
+ val=QuerySellValue(obs[0], PL);
+ if (!val) {
+ write(break_string("Ich bin absolut pleite. Tut mir aufrichtig leid.",
+ 78, Name(WER, 1)+" sagt: "));
+ return 1;
+ }
+ if (val==oval || f) {
+ give_money(make_to_money(obs[0], val));
+ return 1;
+ }
+ if (str=obs[0]->QueryProp(P_NODROP)) {
+ if (stringp(str))
+ write(str);
+ else write(break_string("Du kannst "+obs[0]->name(WEN,1)
+ +" nicht verkaufen!", 78));
+ return 1;
+ }
+
+ if (obs[0]->QueryProp(P_DAMAGED)) // Bei beschaedigten Objekten gibt
+ { // es auch hier eine andere Meldung
+ write(break_string("Da "+obs[0]->name(WER)+" beschaedigt "
+ +(obs[0]->QueryProp(P_PLURAL)?"sind":"ist")+", kann ich Dir "
+ "nur "+val+" Muenze"+(val == 1?"":"n")+" dafuer bieten. Und "
+ "damit mache ich noch Verlust! Nimmst Du mein Angebot an? "
+ "(ja/nein)",78,Name(WER,1)+" sagt: "));
+ }
+ else // Default
+ {
+ write(break_string(Name(WER, 1)+" sagt: "
+ +"Nach der aktuellen Marktlage kann ich Dir dafuer nur "
+ +val+" Muenze"+(val==1?"":"n")+" bieten, obwohl "
+ +obs[0]->name(WER)+" eigentlich "+oval+" Muenze"
+ +(oval==1?"":"n")+" wert waere. Willst Du "
+ +(QueryProp(P_PLURAL) ? "sie" : "es")
+ +" mir dafuer ueberlassen?", 78));
+ }
+ // in ask_sell() gibt es das query_verb() nicht mehr, weswegen U_REQ in
+ // Unitobjekten zurueckgesetzt wird. Damit geht die info verloren,
+ // wieviele Objekte der Spieler angegeben hat. Diese muss gerettet und
+ // via ask_sell() in make_to_money() ankommen. In normalen Objekten ist
+ // U_REQ 0.
+ input_to("ask_sell",INPUT_PROMPT, "(ja/nein) ",obs[0], val,
+ (obs[0])->QueryProp(U_REQ) );
+ return 1;
+ }
+ for (--i; i>=0 && get_eval_cost()>50000; i--) {
+ if (oval=obs[i]->QueryProp(P_VALUE)) {
+ if (obs[i]->QueryProp(P_KEEP_ON_SELL)==getuid(PL)
+ || obs[i]->QueryProp(P_WORN) || obs[i]->QueryProp(P_WIELDED))
+ write(break_string(obs[i]->Name(WER)+": Du behaeltst "
+ +obs[i]->name(WEN)+".", 78));
+ else if (str=sell_obj(obs[i], 1))
+ write(break_string(obs[i]->Name(WER)+": "+str, 78));
+ else {
+ tmp=QuerySellValue(obs[i], PL);
+ if (!tmp) {
+ write(break_string(
+ "Ich bin absolut pleite. Tut mir aufrichtig leid.", 78,
+ Name(WER, 1)+" sagt: "));
+ break;
+ }
+ else if (!f && tmp*10<oval)
+ write(break_string(obs[i]->Name(WER)+": "+Name(WER, 1)
+ +" bietet Dir aber nur "+tmp+" Goldstueck"
+ +(tmp>1 ? "e" : "")+" dafuer.", 78));
+ else {
+ str=(obs[i]->Name(WER));
+ if (tmp=make_to_money(obs[i], tmp)) {
+ write(break_string(str+": "+Name(WER, 1)
+ +" gibt Dir dafuer "+tmp+" Goldstueck"
+ +(tmp==1?".":"e."), 78));
+ val+=tmp;
+ }
+ }
+ }
+ }
+ }
+ if (!val)
+ write(break_string("Hmmm, Du hast aber nicht besonders viel zu bieten...",
+ 78, Name(WER)+" sagt: "));
+ else give_money(val);
+ return 1;
+}
+
+static int force_sell(string str)
+{ return sell(str, 1); }
+
+static int evaluate(string str)
+{
+ object ob;
+ int val,rval;
+
+ if (!str) return 0;
+ if(stringp(QueryProp(P_KEEPER)) && !present(QueryProp(P_KEEPER), ME)) {
+ write("Es ist niemand da, der Dich bedienen koennte.\n");
+ return 1;
+ }
+
+ ob=present(str, ME);
+ if (!ob) ob=deep_present(str,PL);
+ if (!ob) {
+ write("Hm? "+capitalize(str)+"? Wovon redest Du?\n");
+ return 1;
+ }
+ if (living(ob)) {
+ _notify_fail("Nanu, seit wann werden hier Lebewesen verkauft?\n");
+ return 0;
+ }
+ if (str=sell_obj(ob, 0)) {
+ write(break_string(str, 78, Name(WER)+" sagt: "));
+ return 1;
+ }
+ rval=ob->QueryProp(P_VALUE);
+ if (rval) {
+ val=QuerySellValue(ob, PL);
+ if (rval==val) {
+ tell_object(this_player(),break_string(
+ "Naja, ich denke, " +val+ " Muenze"
+ + (val==1 ? "" : "n")
+ + " waere"+(ob->QueryProp(P_AMOUNT)>1?"n ":" ")
+ + (ob->QueryPronoun(WER))+" schon wert.\n",78));
+ }
+ else if (val) {
+ tell_object(this_player(),break_string(
+ "Oh, nach der aktuellen Marktlage kann ich nur "+val+" Muenze"+
+ (val==1?"":"n")+" bezahlen, obwohl "
+ + (QueryProp(P_PLURAL) ? "sie" : "es")
+ + " eigentlich "+rval
+ + " Muenze"+(rval==1?"":"n")+" wert ist.\n",78));
+ }
+ else write("Ich bin vollkommen pleite. Tut mir leid.\n");
+ }
+ else write("Das ist voellig wertlos.\n");
+ return 1;
+}
+
+static int show_obj(string str)
+{
+ int i;
+ string was;
+ mixed ob;
+
+ if (!str) return 0;
+ if (sscanf(str,"%s im laden",was)>0 || sscanf(str,"%s in laden",was)>0) {
+ _notify_fail("Das kann ich im Lager nicht finden.\n");
+ ob=FindInStore(was);
+ } else if (sscanf(str,"%d",i)>0 && i>0) {
+ _notify_fail("Das kann ich im Lager nicht finden.\n");
+ ob=FindInStore(i);
+ }
+ if (!ob) return 0;
+ write(ob->Name(WER)+":\n"+ob->long()+capitalize(ob->QueryPronoun())
+ +" kostet "+QueryBuyValue(ob,PL)+" Muenzen.\n");
+ return 1;
+}
+
+// benutzt von trading_price::QueryValue(object, int, object)
+static int ObjectCount(object ob)
+{
+ string tmp;
+
+ if (!objectp(ob)) return 0;
+ tmp = BLUE_NAME(ob);
+ if (tmp[0..2]!="/d/" && tmp[0..8]!="/players/") tmp=ob->short()+tmp;
+ return ob_anz[tmp];
+}
+
+// benutzt von trading_price::QuerySellValue(object, object)
+static varargs int QueryValue(object ob, int value, object client)
+{
+ int new_value, mymoney;
+
+ if (!objectp(ob)) return 0;
+ if (Query("laden::compat")) {
+ new_value=(value>1000?1000:value);
+ mymoney = QueryProp(P_CURRENT_MONEY);
+ if (new_value>mymoney)
+ return (mymoney>0?mymoney:0);
+ else return new_value;
+ }
+ return ::QueryValue(ob, value, client);
+}
+
+void reset()
+{
+ mixed *keys;
+ int i;
+
+ trading_price::reset();
+
+ if (!mappingp(ob_anz))
+ return;
+ keys=m_indices(ob_anz);
+ for (i=sizeof(keys)-1;i>=0;i--) {
+ ob_anz[keys[i]]=ob_anz[keys[i]]*7/8;
+ if (!ob_anz[keys[i]])
+ m_delete(ob_anz,keys[i]);
+ }
+}
+
+varargs int CheckFindRestrictions(object ob, mixed restr, closure qp) {
+ return 0;
+}
+
+int EvalWeapon(object ob, closure qp) {
+ int wc,val;
+
+ wc=funcall(qp,P_WC);
+ val=funcall(qp,P_EFFECTIVE_WC);
+ if (val>wc) wc=val;
+ return wc;
+}
+
+varargs object FindBestWeapon(mixed type, int maxmon, int maxw, int hands,
+ int bestwc, mixed restr) {
+ object bestob,ob;
+ string otype;
+ int wc,bestval,val,w,bestw;
+ closure qp;
+
+ if (!stringp(storage) || !objectp(ob=find_object(storage))) return 0;
+ if (!maxmon) maxmon=100000;
+ if (!maxw) maxw=75000;
+ if (!hands) hands=2;
+ if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
+ if (type && !pointerp(type) && !mappingp(type)) type=({type});
+
+ for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
+ qp=symbol_function("QueryProp",ob);
+ if (!otype=funcall(qp,P_WEAPON_TYPE)) continue;
+ if (type && member(type,otype)<0) continue;
+ wc=EvalWeapon(ob,qp);
+ if (wc<bestwc) continue;
+ if (funcall(qp,P_NR_HANDS)>hands) continue;
+ w=funcall(qp,P_WEIGHT);
+ if (w>maxw) continue;
+ val=funcall(qp,P_VALUE);
+ if (val>maxmon) continue;
+ if (bestob && wc<=bestwc) {
+ if (val>bestval) continue;
+ else if (val==bestval && w>bestw) continue;
+ }
+ if (val>bestval && bestob && wc<=bestwc) continue;
+ if (CheckFindRestrictions(ob,restr,qp)) continue;
+ bestob=ob;
+ bestwc=wc;
+ bestval=val;
+ bestw=w;
+ }
+ return bestob;
+}
+
+int EvalArmour(object ob,closure qp) {
+ int ac,val;
+
+ ac=funcall(qp,P_AC);
+ val=funcall(qp,P_EFFECTIVE_AC);
+ if (val>ac) ac=val;
+ return ac;
+}
+
+varargs mapping FindBestArmoursT(mixed type, int maxmon, int maxw,
+ mapping bestac, mixed restr) {
+ object ob;
+ string otype;
+ int ac,val,sum,w,wsum;
+ mapping bestob,bestval,bestw;
+ closure qp;
+
+ if (!stringp(storage) || !objectp(ob=find_object(storage))) return ([]);
+ if (!maxmon) maxmon=100000;
+ if (!maxw) maxw=75000;
+ if (val=QueryBuyFact()) maxmon=(maxmon*100)/val;
+ if (type && !pointerp(type) && !mappingp(type)) type=({type});
+ if (!mappingp(bestac)) bestac=([]);
+ bestob=([]);bestval=([]);bestw=([]);
+
+ for (ob=first_inventory(ob);ob;ob=next_inventory(ob)) {
+ qp=symbol_function("QueryProp",ob);
+ if (!otype=funcall(qp,P_ARMOUR_TYPE)) continue;
+ if (type && member(type,otype)<0) continue;
+ ac=EvalArmour(ob,qp);
+ if (ac<bestac[otype]) continue;
+ w=funcall(qp,P_WEIGHT);
+ if (wsum-bestw[otype]+w>maxw) continue;
+ val=funcall(qp,P_VALUE);
+ if (sum-bestval[otype]+val>maxmon) continue;
+ if (bestob[otype] && ac<=bestac[otype]) {
+ if (val>bestval[otype]) continue;
+ else if (val==bestval[otype] && w>bestw[otype]) continue;
+ }
+ if (CheckFindRestrictions(ob,restr,qp)) continue;
+ sum=sum-bestval[otype]+val;
+ wsum=wsum-bestw[otype]+w;
+ bestob[otype]=ob;
+ bestac[otype]=ac;
+ bestval[otype]=val;
+ bestw[otype]=w;
+ }
+ return bestob;
+}
+
+varargs object *FindBestArmours(mixed type, int maxmon, int maxw,
+ mapping bestac, mixed restr) {
+ return m_values(FindBestArmoursT(type,maxmon,maxw,bestac,restr));
+}