Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/d/seher/haeuser/modules/haustuer.c b/d/seher/haeuser/modules/haustuer.c
new file mode 100644
index 0000000..b9d8784
--- /dev/null
+++ b/d/seher/haeuser/modules/haustuer.c
@@ -0,0 +1,215 @@
+// haustuer.c -- Beschreibung, Oeffnen und Schliessen
+//
+// (c) 1995 Wargon@MorgenGrauen
+//
+// $Id: haustuer.c,v 1.1.1.1 2000/08/20 20:22:42 mud Exp $
+//
+
+#define NEED_PROTOTYPES
+#include "../haus.h"
+#include <properties.h>
+#include <thing/properties.h>
+#include <thing/description.h>
+#include <thing/commands.h>
+
+static int validHouse = 0;
+static string owner;
+
+void create()
+{
+ Set( H_DOOR, "Die Haustuer ist %s.\n" );
+ Set( H_DOORSTAT, 0);
+ Set( H_DOORLSTAT, ({ "geoeffnet", "geschlossen", 0, "abgeschlossen" }));
+
+ Set( H_DOOR, SAVE, F_MODE);
+ Set( H_DOORSTAT, SAVE, F_MODE);
+ Set( H_DOORLSTAT, SAVE, F_MODE);
+
+ AddCmd( ({"schliess", "schliesse"}), "schliesse");
+ AddCmd("oeffne","oeffne");
+
+ owner = getuid(this_object());
+}
+
+varargs string long(int mode)
+{
+ return sprintf(Query(H_DOOR), Query(H_DOORLSTAT)[Query(H_DOORSTAT)]);
+}
+
+static string SetTuerOwner(string o)
+{
+ validHouse=1;
+ Set( H_DOORSTAT, D_CLOSED | D_LOCKED);
+ return owner=o;
+}
+
+int oeffne(string str)
+{
+ int doorstat;
+
+ notify_fail("Was willst Du oeffnen?\n");
+ doorstat = Query(H_DOORSTAT);
+
+ if (!id(str) && str != "tuer" && str != "haustuer")
+ return 0;
+
+ if (!(doorstat & D_CLOSED)) {
+ notify_fail(capitalize(name(WER,1))+" ist gar nicht geschlossen!\n");
+ return 0;
+ }
+ if (doorstat & D_LOCKED) {
+ notify_fail(capitalize(name(WER,1))+" ist abgeschlossen.\n");
+ return 0;
+ }
+
+ doorstat &= ~D_CLOSED;
+ Set(H_DOORSTAT, doorstat & ~D_CLOSED);
+
+ write("Du oeffnest "+name(WEN,1)+" von "+capitalize(owner)+".\n");
+ tell_room(environment(this_object()),
+ sprintf("%s oeffnet %s von %s.\n",
+ this_player()->name(WER),
+ name(WEN,1),
+ capitalize(owner) ),
+ ({this_player()}));
+ if (validHouse) {
+ tell_room(find_object(RAUMNAME(owner,0))||this_object(),
+ this_player()->name(WER)+" oeffnet "+name(WEN)+".\n",
+ ({this_player()}));
+ Save();
+ }
+ return 1;
+}
+
+private int close_door()
+{
+ if (Query(H_DOORSTAT) & D_CLOSED) {
+ notify_fail(capitalize(name(WER,1))+" ist doch schon zu!\n");
+ return 0;
+ }
+ Set(H_DOORSTAT, Query(H_DOORSTAT) | D_CLOSED);
+
+ write("Du schliesst "+name(WEN,1)+" von "+capitalize(owner)+".\n");
+ tell_room(environment(this_object()),
+ sprintf( "%s schliesst %s von %s.\n",
+ capitalize(this_player()->name(WER)),
+ name(WEN,1), capitalize(owner)),
+ ({this_player()}));
+ if (validHouse) {
+ tell_room(find_object(RAUMNAME(owner,0))||this_object(),
+ capitalize(this_player()->name(WER))+" schliesst "+name(WEN)+".\n",
+ ({this_player()}));
+ Save();
+ }
+ return 1;
+}
+
+private int lock_door()
+{
+ object tp, home;
+ string nam;
+ int doorstat;
+
+ tp = this_player();
+ nam = capitalize(tp->name(WER));
+
+ if (validHouse) {
+ call_other(RAUMNAME(owner,0), "???");
+ home = find_object(RAUMNAME(owner,0));
+
+ if (!home->allowed_check(this_player())) {
+ notify_fail("Du darfst "+name(WEN,1)+" von "+capitalize(owner)+" aber nicht abschliessen.\n");
+ return 0;
+ }
+ }
+ if ((doorstat = Query(H_DOORSTAT)) & D_LOCKED) {
+ notify_fail("Die Tuer ist abgeschlossen.\n");
+ return 0;
+ }
+ doorstat |= (D_LOCKED|D_CLOSED);
+ write("Du schliesst "+name(WEN)+" ab.\n");
+ Set(H_DOORSTAT, doorstat);
+ tell_room(environment(this_object()),
+ sprintf( "%s schliesst %s ab.\n",
+ nam,
+ (getuid(tp)==owner ?
+ (tp->QueryPossPronoun(this_object(), WEN)+" "+name(RAW)) :
+ name(WEN,1) )),
+ ({tp}));
+ if (validHouse) {
+ tell_room(home, nam+" schliesst "+name(WEN)+" ab.\n",({tp}));
+ Save();
+ }
+ return 1;
+}
+
+private int unlock_door()
+{
+ object tp, home;
+ string nam;
+ int doorstat;
+
+ tp = this_player();
+ nam = capitalize(tp->name(WER));
+
+ if (validHouse) {
+ call_other(RAUMNAME(owner,0), "???");
+ home = find_object(RAUMNAME(owner,0));
+
+ if (!home->allowed_check(this_player())) {
+ notify_fail("Du darfst "+name(WEN,1)+" von "+capitalize(owner)+" aber nicht aufschliessen.\n");
+ return 0;
+ }
+ }
+
+ if (!((doorstat = Query(H_DOORSTAT)) & D_LOCKED)) {
+ notify_fail("Die Tuer ist nicht abgeschlossen.\n");
+ return 0;
+ }
+
+ Set(H_DOORSTAT, doorstat & ~D_LOCKED);
+ write("Du schliesst "+name(WEN)+" auf.\n");
+ tell_room(environment(this_object()),
+ sprintf("%s schliesst %s auf.\n",
+ nam,
+ (getuid(tp)==owner ?
+ (tp->QueryPossPronoun(this_object(), WEN)+" "+name(RAW)) :
+ name(WEN,1) )),
+ ({tp}));
+ if(validHouse) {
+ tell_room(home,nam+" schliesst "+name(WEN)+" auf.\n",({tp}));
+ Save();
+ }
+ return 1;
+}
+
+int schliesse(string str)
+{
+ string was, wen, *s;
+
+ notify_fail("Was willst Du schliessen ?\n");
+ if (!str||str=="")
+ return 0;
+ s = old_explode(str, " ");
+ if ((was = s[<1]) == "auf" || was == "ab")
+ s = s[0..<2];
+ else
+ was = 0;
+
+ wen=implode(s, " ");
+ if (!id(wen) && wen != "tuer" && wen != "haustuer")
+ return 0;
+ if (!was || was=="")
+ return close_door();
+ switch (was)
+ {
+ case "auf": return unlock_door();
+ case "ab": return lock_door();
+ }
+ return 0;
+}
+
+// $Log: haustuer.c,v $
+// Revision 1.1.1.1 2000/08/20 20:22:42 mud
+// Ins CVS eingecheckt
+//
diff --git a/d/seher/haeuser/modules/losa.c b/d/seher/haeuser/modules/losa.c
new file mode 100644
index 0000000..893ca51
--- /dev/null
+++ b/d/seher/haeuser/modules/losa.c
@@ -0,0 +1,378 @@
+// losa.c -- Modul fuer Laden und Speichern der Hausdaten
+//
+// (c) 1995 Wargon@MorgenGrauen
+// 2006 Vanion@MorgenGrauen, fuer die rebootfesten Moebel
+// $Id: losa.c,v 1.1.1.1 2000/08/20 20:22:42 mud Exp $
+//
+#pragma strong_types,rtt_checks
+
+#define NEED_PROTOTYPES
+#include "../haus.h"
+#include <container.h>
+#include <thing/properties.h>
+#include <room/exits.h>
+#include <thing/description.h>
+#undef NEED_PROTOTYPES
+#include <properties.h>
+#include <wizlevels.h>
+#include <moving.h>
+
+static void start_reload_furniture();
+
+private int csaved;
+
+// Variablen zur Verwaltung der Moebel im Raum.
+mapping furniture;
+mapping broken_furniture;
+
+protected void
+create()
+{
+ Set(H_CHEST, SAVE, F_MODE);
+ Set(H_CHEST, 0);
+
+ Set(H_FURNITURE, SAVE, F_MODE);
+ Set(H_FURNITURE, 0);
+
+// Set(H_SPECIAL, SAVE|SECURED, F_MODE);
+// Set(H_SPECIAL, ([:2]), F_VALUE);
+}
+
+/* Scheint nirgendwo benutzt zu werden...
+varargs int AddSpecial(int typ, string key, string extra)
+{
+ if (!this_interactive() || !IS_ARCH(this_interactive()) ||
+ !(PATH+"access_rights")->access_rights(geteuid(this_interactive()),""))
+ return -1;
+
+ if (typ != HS_EXIT && typ != HS_ITEM)
+ return 0;
+
+ Set(H_SPECIAL, Query(H_SPECIAL) + ([ key : typ; extra ]));
+ Save();
+ return 1;
+}
+
+void RemoveSpecial(string key)
+{
+ if (!this_interactive() || !IS_ARCH(this_interactive()) ||
+ !(PATH+"access_rights")->access_rights(geteuid(this_interactive()),""))
+ return;
+
+ Set(H_SPECIAL, m_delete(Query(H_SPECIAL), key));
+ Save();
+}
+*/
+
+void
+reset()
+{
+ if (QueryOwner() &&
+ !sizeof(filter(all_inventory(),#'interactive)) &&
+ !csaved)
+ Save(1);
+}
+
+// crunched komprimiert das Savefile
+varargs void
+Save(int crunched)
+{
+ mixed o1, o2, o3;
+ closure pc;
+ object *obs;
+ int i, found;
+
+ o3 = 0;
+
+ if (!(o1 = Query(P_DETAILS)))
+ Set(P_DETAILS, QueryProp(P_DETAILS), F_VALUE);
+
+ if (!(o2 = Query(P_READ_DETAILS)))
+ Set(P_READ_DETAILS, QueryProp(P_READ_DETAILS), F_VALUE);
+
+ if (csaved = crunched)
+ {
+ pc = symbol_function("PCrunch", VERWALTER);
+ Set(P_DETAILS, funcall(pc, Query(P_DETAILS)), F_VALUE);
+ Set(P_READ_DETAILS, funcall(pc, Query(P_READ_DETAILS)), F_VALUE);
+ o3 = Query(H_COMMANDS, F_VALUE);
+ Set(H_COMMANDS, funcall(pc, o3), F_VALUE);
+ }
+
+ // Autoload-Einrichtung identifizieren und speichern
+ // Code in Anlehnung an dem Autoload-Mechanismus fuer Spieler
+ furniture=([]);
+
+ // Alle Autoloader filtern
+ obs=filter_objects(all_inventory(this_object()), "QueryProp", H_FURNITURE);
+ found = 0;
+
+ // Ueber alle Moebel iteritieren
+ for( i=sizeof(obs)-1;i>=0;i--)
+ {
+ if( clonep(obs[i]))
+ {
+ if ( ++found <= MAX_FURNITURE_PER_ROOM )
+ furniture += ([ object_name(obs[i]):obs[i]->QueryProp(H_FURNITURE) ]);
+ }
+ }
+ if (found > MAX_FURNITURE_PER_ROOM)
+ {
+ tell_object(this_player(),
+ break_string("Du hast "+found+" Moebelstuecke im Raum stehen. "
+ "Gespeichert werden nur "+MAX_FURNITURE_PER_ROOM+". "
+ "Du solltest Dich von einigen Einrichtungsgegenstaenden "
+ "trennen.",78));
+
+ }
+ HDEBUG("Saving "+ sizeof (furniture) +" (plus "+sizeof(broken_furniture)+
+ " broken) objects in room "+
+ object_name(this_object()) + ".");
+
+ save_object( HAUSSAVEPATH+QueryOwner(1));
+
+ Set(P_DETAILS, o1, F_VALUE);
+ Set(P_READ_DETAILS, o2, F_VALUE);
+ if (o3)
+ Set(H_COMMANDS, o3, F_VALUE);
+}
+
+void
+Load()
+{
+ mixed prop;
+ int i;
+
+ restore_object( HAUSSAVEPATH+QueryOwner(1));
+
+ // Details und Kommandos werden beim Speichern de-dupliziert und in einem
+ // speziellen Format abgespeichert (s. PCrunch() im Hausverwalter). Sie
+ // muessen nach dem Laden durch die entsprechenden Add...()-Aufrufe
+ // wieder eingetragen werden.
+ prop=Query(P_DETAILS, F_VALUE);
+ RemoveDetail(0);
+ if (pointerp(prop))
+ {
+ foreach(<string*|string>* item : prop)
+ AddDetail(item[0], item[1]);
+ }
+ else if (mappingp(prop))
+ {
+ foreach(string key, mixed val : prop)
+ AddDetail(key, val);
+ }
+ else
+ SetProp(P_DETAILS, prop);
+
+ prop = Query(P_READ_DETAILS, F_VALUE);
+ RemoveReadDetail(0);
+ if (pointerp(prop))
+ {
+ foreach(<string*|string>* item : prop)
+ AddDetail(item[0], item[1]);
+ }
+ else if (mappingp(prop))
+ {
+ foreach(string key, mixed val : prop)
+ AddReadDetail(key, val);
+ }
+ else
+ SetProp(P_READ_DETAILS, prop);
+
+ prop = Query(P_EXITS, F_VALUE);
+ RemoveExitNoCheck(0);
+ if (mappingp(prop))
+ {
+ if (widthof(prop) <= 1)
+ {
+ foreach(string key, string dest : prop)
+ AddExitNoCheck(key, dest);
+ }
+ else
+ {
+ foreach(string key, string dest, string msg : prop)
+ {
+ if (stringp(msg))
+ _AddExit(key, dest, msg);
+ else if (stringp(dest) && strstr(dest,"#") != -1)
+ AddExitNoCheck(key, dest);
+ else
+ _AddExit(key, dest, 0);
+ }
+ }
+ }
+
+ prop=Query(H_COMMANDS, F_VALUE);
+ if (pointerp(prop))
+ {
+ Set(H_COMMANDS, ([]), F_VALUE);
+ for (i=sizeof(prop)-1; i>=0; i--)
+ this_object()->AddUserCmd(prop[i][0], 0, prop[i][1], prop[i][2]);
+ }
+
+ if (environment())
+ environment()->SetProp(P_NEVER_CLEAN, 1);
+
+ if (previous_object() && object_name(previous_object())==VERWALTER)
+ {
+ if (Query(H_CHEST))
+ this_object()->AddItem(PATH+"truhe",REFRESH_NONE,
+ ([ "owner" : QueryOwner() ]));
+/* Das scheint nirgendwo benutzt zu werden und in allen Seherhaeusern leer zu
+ * sein.
+ mapping special = Query(H_SPECIAL, F_VALUE);
+ if (special)
+ {
+ foreach(string key, int type, string extra : special)
+ {
+ switch(type)
+ {
+ case HS_ITEM:
+ AddItem(SPECIALPATH + extra, REFRESH_DESTRUCT);
+ break;
+ case HS_EXIT:
+ AddExitNoCheck(key, extra);
+ break;
+ }
+ }
+ }
+*/
+ }
+
+ // Das Laden der Autoloader wird erst am Ende angestossen.
+ // Dann ist es nicht schlimm, wenn alle Eval Ticks verbraucht werden.
+ start_reload_furniture();
+}
+
+// Mehrere Save-Anforderungen zusammenfassen.
+static void queued_save()
+{
+ HDEBUG("QS");
+ while (remove_call_out("Save")!=-1);
+ call_out("Save",3);
+}
+
+static int reload_error(string file, mixed data, string message)
+{
+ HDEBUG(message);
+ broken_furniture+=([file:data]);
+ log_file("seher/haeuser/autoloader_error",
+ dtime(time())+"\n"+
+ break_string(object_name(this_object())+" ("+QueryOwner(1)+")",78, " FILE: ",1)+
+ break_string(message, 78, " MSG: ",1)+
+ break_string(sprintf("%O", data),78, " DATA: ",1)+"\n");
+
+ return 0; // 0 fuer das filter, damit dieser Eintrag
+ // aus furniture geloescht wird.
+}
+
+// Laedt ein einzelnes Moebelstuecks
+static int load_furniture_object( string file, mixed data )
+{
+ object ob;
+ string error;
+ string blueprint;
+ closure pc;
+ // mixed data;
+
+ // Wenn genug Ticks frei sind, wird versucht, das Objekt zu erzeugen.
+ // Ansonsten ist die Gefahr zu gross, dass ein Erzeugungs-Prozess abbricht.
+ if (get_eval_cost() < 500000)
+ {
+ // HDEBUG("Suspending Object: "+file+". Only "+to_string(get_eval_cost())+" ticks left.");
+ return 1; // 1 bedeutet, dass dieser Eintrag es im Mapping bleibt.
+ }
+
+ // HDEBUG("Processing Object: "+file+" with Data: "+sprintf("%O",data)+".");
+
+ // Nummern der Clones sind beim Speichern noetig, um die Identitaeten
+ // der Objekte zu bestimmen (mehrere Objekte vom gleichen Blueprint
+ // speichern). Hier braucht man sie nicht mehr
+
+ blueprint = explode(file,"#")[0];
+
+ // Data aus dem Mapping holen
+ // data=furniture[file];
+
+ // Muss ich die Blueprint suchen?
+ ob = find_object(file);
+
+ // Nein.
+ if (!ob)
+ {
+ // Existiert die BP oder ein VC fuers File?
+ if (file_size(blueprint+".c")<0&&
+ file_size(implode(explode(blueprint,"/")[0..<2],"/")+
+ "/virtual_compiler.c")<0)
+ {
+ return reload_error(file, data, "Error in file: "+ file +
+ ". File does not exist.");
+
+ }
+
+ // File gefunden. Versuch, es zu laden.
+ if (error = catch(call_other( blueprint,"???")))
+ {
+ return reload_error(file, data, "Error loading file: "+file+". "+error);
+ }
+ }
+
+ // Clone erzeugen
+ if ( error = catch(ob = clone_object(blueprint)) )
+ {
+ return reload_error(file, data, "Error cloning object: "+file+". "+error);
+ }
+
+ HDEBUG(sprintf("%O",furniture));
+ // Autoload-Daten setzen
+ HDEBUG(object_name(ob)+"->SetProp("+sprintf("%O", data)+")");
+ if (ob)
+ catch(ob->SetProp( H_FURNITURE, data ));
+
+ // Furniture in das Seherhaus moven
+ if ( error = catch(ob->move( this_object(), M_NOCHECK )) ) {
+ ob->remove();
+ if(ob) destruct(ob);
+ return reload_error(file, data, "Error moving object: "+file+". "+error);
+ }
+
+ // post_create anstossen
+ pc=symbol_function("post_create", ob);
+ if (closurep(pc))
+ call_out(pc, 1);
+
+ return 0; // 0 bedeutet hier, dieses Objekt nicht noch einmal anstossen.
+}
+
+static void load_furniture()
+{
+ int i;
+ string rv;
+ string current_key;
+
+ // Abbruchbedingung ist, dass nichts mehr zu laden ist.
+ if (sizeof(furniture)==0) return;
+
+ // Anstoßen des naechsten Durchlaufs, falls die Ticks nicht reichen.
+ while (remove_call_out(#'load_furniture) != -1);
+ call_out(#'load_furniture, 1);
+
+ // Laden aller Moebel anstoßen
+ furniture=filter(furniture, #'load_furniture_object);
+}
+
+// Diese Funktion bereitet das Reloaden der Einrichtung vor
+static void start_reload_furniture()
+{
+ // Wenn es keine Moebel gibt, ist das Laden beendet.
+ if (!mappingp(furniture)) return;
+ if (broken_furniture==0) broken_furniture=([]);
+
+ // Falls ein Key von furniture 0 ist, wird dieser geloescht.
+ m_delete(furniture,0);
+
+ // Laden des Furniture anstossen
+ load_furniture();
+}
+
+
diff --git a/d/seher/haeuser/modules/usercmd.c b/d/seher/haeuser/modules/usercmd.c
new file mode 100644
index 0000000..a83b819
--- /dev/null
+++ b/d/seher/haeuser/modules/usercmd.c
@@ -0,0 +1,341 @@
+// usercmd.c -- Modul fuer benutzerdefinierte Befehle
+//
+// (c) 1995 Wargon@MorgenGrauen
+//
+// $Id: usercmd.c,v 1.4 2003/11/15 13:48:46 mud Exp $
+//
+
+#define NEED_PROTOTYPES
+#include "../haus.h"
+#include <properties.h>
+#include <wizlevels.h>
+#include <thing/properties.h>
+
+private string ucFilter(string str);
+private string ucText(string str);
+private void splitCmd(string *cmd, string *verb, string *para);
+
+create()
+{
+ Set(H_COMMANDS, SAVE, F_MODE);
+ Set(H_COMMANDS, ([]));
+
+ AddCmd("", "userCommands", 1);
+}
+
+/*
+ * AddUserCmd: eigenes Kommando hinzufuegen
+ * cmd: String oder Array von Strings. Enthaelt entweder nur die Verben oder
+ * die kompletten Befehle (Verb + Parameter). Siehe auch pa.
+ * pa: Zahl oder Array von Strings. Falls pa eine Zahl ist, so enthaelt cmd
+ * die kompletten Befehle (samt Parametern). Ansonsten enthaelt pa die
+ * Parameter fuer das entsprechende Verb in cmd.
+ * Ist pa = ({ "@NF@"}) , so handelt es sich um ein notify_fail, das an
+ * die Beschreibung des Verbs (falls vorhanden) angehaengt wird.
+ * me: Text fuer den Ausfuehrenden. Der Text muss schon geparsed worden
+ * sein!
+ * oth: Text fuer die Umstehenden oder 0. Der Text muss schon geparsed wor-
+ * den sein!
+ */
+varargs void
+AddUserCmd(mixed cmd, mixed pa, string me, string oth)
+{
+ int v,p;
+ mapping cmds, desc;
+ string *verb, *para, txt;
+
+ cmds = Query(H_COMMANDS);
+ if (stringp(cmd))
+ verb = ({ cmd });
+ else
+ verb = cmd;
+
+ if (intp(pa))
+ splitCmd(verb[0..], &verb, ¶);
+ else {
+ if (stringp(pa))
+ para = ({ pa });
+ else if (pointerp(pa))
+ para = pa;
+ for (desc = ([]), p=sizeof(para)-1; p>=0; p--)
+ desc += ([ para[p] : me; oth ]);
+ }
+
+ for (v = sizeof(verb)-1; v>= 0; v--) {
+ if (member(cmds, verb[v])) {
+ if (intp(pa))
+ cmds[verb[v]] += ([para[v] : me; oth ]);
+ else
+ cmds[verb[v]] += desc;
+ }
+ else {
+ if (intp(pa))
+ cmds += ([ verb[v] : ([para[v] : me; oth ]) ]);
+ else
+ cmds += ([ verb[v] : desc ]);
+ }
+ }
+ Set(H_COMMANDS, cmds);
+}
+
+/*
+ * RemUserCmd: Kommando(s) wieder entfernen
+ * com: String oder Array von Strings. Enthaelt das zu loeschende Kommando
+ * (Verb oder Verb + Parameter).
+ * all: Falls != 0, so werden die Verben aus com komplett mit allen Para-
+ * metern geloescht.
+ */
+varargs void
+RemUserCmd(mixed com, int all)
+{
+ mapping cmd, tmp;
+ string *verb, *para;
+ int v, p;
+
+ cmd = Query(H_COMMANDS);
+ splitCmd(stringp(com) ? ({com}) : com, &verb, ¶);
+
+ if (all)
+ for (v=sizeof(verb)-1; v>=0; v--)
+ cmd = m_copy_delete(cmd, verb[v]);
+ else {
+ for (v=sizeof(verb)-1; v>=0; v--) {
+ if (tmp = cmd[verb[v]]) {
+ for (p=sizeof(para)-1; p>=0; p--)
+ tmp = m_copy_delete(tmp, para[p]);
+ cmd[verb[v]] = tmp;
+ }
+ }
+ }
+ Set(H_COMMANDS,cmd);
+}
+
+/*
+ * userCommands: Auswertung der benutzerdefinierten Befehle.
+ */
+static int
+userCommands(string str)
+{
+ mapping ucmd, uparm;
+ string *parts, text;
+ int i;
+
+ ucmd = QueryProp(H_COMMANDS);
+ str = this_player()->_unparsed_args(1)||"";
+ if (uparm = ucmd[query_verb()]) {
+ if (member(uparm, str)) {
+ text = ucText(uparm[str,0]);
+ if (sizeof(old_explode(text, "\n")) >= this_player()->QueryProp(P_SCREENSIZE))
+ this_player()->More(capitalize(text)[0..<2]);
+ else
+ write(capitalize(text));
+
+ if (uparm[str,1])
+ say(ucText(uparm[str,1]));
+
+ if (member(m_indices(QueryProp(P_EXITS)), query_verb()) == -1)
+ return 1;
+ }
+ else {
+ if (str && str != "" && text = uparm["@NF@"])
+ notify_fail(implode(old_explode(text,"@F"),str));
+ }
+ }
+ return 0;
+}
+
+/*** Functions private to this module... ***/
+
+/*
+ * ucFilter: Ersetzen von Name, Personal- und Possessivpronomen.
+ * Gibt den ersetzten String zurueck.
+ */
+private string
+ucFilter(string str)
+{
+ int p,g;
+
+ switch(str[0..1]) {
+ case "@W": // Name in entsprechendem Fall...
+ return this_player()->name(to_int(str[2..2]));
+ case "@P": // Personalpronomen in entprechendem Fall...
+ return this_player()->QueryPronoun(to_int(str[2..2]));
+ case "@B": // Possesivpronomen in entprechendem Fall...
+ p = to_int(str[4..4]);
+ g = to_int(str[3..3]);
+ return this_player()->QueryPossPronoun(g, to_int(str[2..2]), p);
+ }
+ return str;
+}
+
+/*
+ * ucText: Rassen- und geschlechtsspezifische Teile bearbeiten sowie
+ * Namen und Pronomina einsetzen.
+ *
+ * str enthaelt den String, der beim Beschreiben des Befehls
+ * eingegeben wurde. Bis auf den Default-Text sind alle Teile
+ * optional. Der String kann folgenden Aufbau haben:
+ * ------ schnipp ------
+ * Default-Text, der ausgegeben wird, wenn die folgenden spezielleren
+ * Texte nicht auftreten oder auf den Ausfuehrenden nicht zutreffen
+ * @NAME:nameA
+ * Text, der ausgegeben wird, wenn der Spieler "nameA" das Kommando
+ * eingegeben hat
+ * @NAME:nameB
+ * Und so weiter fuer Spieler "nameB" etc...
+ * @RA
+ * Text, der ausgegeben wird, wenn der Ausfuehrende auf der Erlaube-
+ * Liste des Hauses steht.
+ * @RE
+ * Text, der ausgegeben wird, wenn der Ausfuehrende ein Elf
+ * ist. Ebenso gibt es @RD fuer Dunkelelfen, @RF fuer Felinen,
+ * @RH fuer Hobbits, @RM fuer Menschen, @RG fuer Goblins und @RZ
+ * fuer Zwerge.
+ * ------ schnapp ------
+ *
+ * Der Default-Text muss immer am Anfang stehen. Die anderen Bloecke
+ * koennen in beliebiger Reihenfolge folgen. Die Reihenfolge, in der
+ * die Bloecke betrachtet werden, ist dabei folgende:
+ * - zuerst werden @NAME-Bloecke untersucht
+ * - dann wird @RA (Erlaube-Liste) getestet
+ * - danach wird die Rasse ueberprueft (@RD/@RE/@RF/@RH/@RM/@RZ/@RG)
+ * - zuletzt wird der Default-Text betrachtet
+ *
+ * Innerhalb jedes der Bloecke kann man noch mit @G zwischen
+ * maennlichen und weiblichen Vertretern unterscheiden (bei @NAME:
+ * macht das aber wenig Sinn). Beispiel:
+ * ------ schnipp ------
+ * @RZ
+ * Der Zwerg war maennlich
+ * @G
+ * Der Zwerg war weiblich
+ * ------ schnapp ------
+ *
+ * Die Funktion gibt den fuer den Ausfuehrenden zutreffenden Text
+ * zurueck.
+ */
+private string
+ucText(string str)
+{
+ string *parts, *names, *lnames;
+ int i, n;
+
+ // Text nach Namen- und Rassentrennern aufteilen
+ parts = regexplode(str, "(@NAME:[A-Za-z1-9]*\n)|(@R[A-Z]\n)");
+ i = -1;
+
+ if (sizeof(parts) > 1) {
+
+ // Zuerst wird nach Namenstrennern gesucht
+ names = regexp(parts, "@NAME:");
+
+ if (sizeof(names) > 0) {
+ // ein kleiner Umweg, da der Name im Eingabestring nicht
+ // notwendigerweise in Kleinbuchstaben vorliegt.
+ lnames = map(names, #'lower_case);
+ n = member(lnames, "@name:"+getuid(this_player())+"\n");
+
+ if (n >= 0) {
+ i = member(parts, names[n]);
+ }
+ }
+
+ // Kein passender Namenstrenner gefunden: Erlaube-Liste
+ // ueberpruefen
+ if (i<0 && allowed_check(this_player())) {
+ i=member(parts, "@RA\n");
+ }
+
+ // Weder Namenstrenner noch Erlaube-Liste passen: Rasse
+ // ueberpruefen
+ if (i<0) {
+ switch(this_player()->QueryProp(P_RACE)) {
+ case "Zwerg":
+ i=member(parts, "@RZ\n");
+ break;
+ case "Elf":
+ i=member(parts, "@RE\n");
+ break;
+ case "Feline":
+ i=member(parts, "@RF\n");
+ break;
+ case "Hobbit":
+ i = member(parts, "@RH\n");
+ break;
+ case "Dunkelelf":
+ i = member(parts, "@RD\n");
+ break;
+ case "Goblin":
+ i = member(parts, "@RG\n");
+ break;
+ case "Ork":
+ i = member(parts, "@RO\n");
+ break;
+ default:
+ i=member(parts, "@RM\n");
+ break;
+ }
+ }
+
+ // Den richtigen Teil des Strings herauspicken
+ if (i>-1)
+ str = parts[i+1];
+ else
+ str = parts[0];
+ }
+ if (sizeof(parts = old_explode(str, "@G\n"))==2)
+ str = parts[(this_player()->QueryProp(P_GENDER) == MALE ? 0 : 1)];
+
+ parts = regexplode(str, "(@W[0-3]|@P[0-3]|@B[0-3][0-2][0-1])");
+ parts = map(parts, #'ucFilter);
+ return implode(parts, "");
+}
+
+/*
+ * splitCmd: Komplettes Kommando in Arrays von Verben und Parametern zerlegen.
+ * cmd: Array von Strings. Dieses Array enthaelt die aufzuspaltenden Befehle.
+ * verb: Referenz auf ein Array von Strings fuer die Verben.
+ * para: Referenz auf ein Array von Strings fuer die Parameter.
+ */
+private void splitCmd(string *cmd, string *verb, string *para)
+{
+ int c, sp;
+
+ for (verb = ({}), para = ({}), c = sizeof(cmd)-1; c>=0; c--) {
+ if ((sp=member(cmd[c], ' ')) >= 0) {
+ verb += ({ cmd[c][0..sp-1] });
+ para += ({ cmd[c][sp+1..] });
+ }
+ else {
+ verb += ({ cmd[c] });
+ para += ({ "" });
+ }
+ }
+}
+
+// $Log: usercmd.c,v $
+// Revision 1.4 2003/11/15 13:48:46 mud
+// @RD als Trenner fuer Dunkelelfen
+//
+// Revision 1.3 2000/12/03 17:15:40 mud
+// ucText: Neuer Trenner @NAME:foo, mit dem man in Seherhausbefehlen
+// Texte fuer bestimmte Spieler vorsehen kann.
+//
+// Revision 1.2 2000/08/20 20:38:40 mud
+// @RF als Trenner fuer Felinen
+//
+// Revision 1.1.1.1 2000/08/20 20:22:42 mud
+// Ins CVS eingecheckt
+//
+// Revision 1.4 1996/04/19 23:10:52 Wargon
+// @RA fuer Leute mit Erlaubnis
+//
+// Revision 1.3 1995/10/31 12:59:52 Wargon
+// @RH fuer Hobbits
+//
+// Revision 1.2 1995/06/22 19:48:31 Wargon
+// Bugfix in userCommands()
+//
+// Revision 1.1 1995/04/21 09:22:50 Wargon
+// Initial revision
+//