Added public files
Roughly added all public files. Probably missed some, though.
diff --git a/std/player/base.c b/std/player/base.c
new file mode 100644
index 0000000..9c9a6e1
--- /dev/null
+++ b/std/player/base.c
@@ -0,0 +1,4355 @@
+// MorgenGrauen MUDlib
+//
+// player/base.c -- the basic player object
+//
+// $Id: base.c 9467 2016-02-19 19:48:24Z Zesstra $
+#pragma strong_types
+#pragma save_types
+#pragma range_check
+#pragma no_clone
+#pragma pedantic
+
+#include <sys_debug.h>
+#include <regexp.h>
+#include <input_to.h>
+#include <logging.h>
+#include <werliste.h>
+#include <time.h>
+#include <errord.h>
+#include <wizlevels.h>
+#include <money.h>
+
+inherit "/std/hook_provider";
+inherit "/std/player/restrictions";
+inherit "/std/living/attributes";
+inherit "/std/living/put_and_get";
+inherit "/std/living/clothing";
+inherit "/std/thing/properties";
+inherit "/std/player/util";
+inherit "/std/thing/language";
+inherit "/std/player/travel";
+inherit "/std/player/combat";
+inherit "/std/player/description";
+inherit "/std/player/moving";
+inherit "/std/player/life";
+inherit "/std/player/comm";
+inherit "/std/player/viewcmd";
+inherit "/std/player/moneyhandler";
+inherit "/std/player/command";
+inherit "/std/living/skill_attributes";
+inherit "/std/living/light";
+inherit "/std/player/skills";
+inherit "/std/player/quests";
+inherit "/std/player/potion";
+inherit "/std/player/soul";
+inherit "/std/more";
+inherit "/std/user_filter";
+inherit "/secure/telnetneg";
+inherit "/std/player/guide";
+inherit "/std/player/reputation";
+inherit "/std/player/protocols/gmcp";
+inherit "/std/living/helpers";
+
+#define NEED_PROTOTYPES
+#include <player/skills.h>
+#include <player/gmcp.h>
+#undef NEED_PROTOTYPES
+#include <player.h>
+#include <properties.h>
+#include <udp.h>
+#include <config.h>
+#include <ansi.h>
+#include <wizlevels.h>
+#include <living.h>
+#include <attributes.h>
+#include <language.h>
+#include <moving.h>
+#include <defines.h>
+#include <terminal.h>
+#include <new_skills.h>
+#include <pager.h>
+#include <combat.h>
+#include "/secure/questmaster.h"
+#include "/secure/lepmaster.h"
+#include <events.h>
+
+#undef NAME /* DEFINED BY UDP.H; BAD NAME CLASH :( */
+#define NAME(who) capitalize(getuid(who))
+
+mapping autoload; /* autoload-mapping */
+int hc_play;
+
+private nosave mapping autoload_rest;
+private nosave string *autoload_error;
+private nosave string realip;
+
+private nosave string passw; /* temporarily for password change */
+private nosave string passwold; /* temporarily for password change */
+
+// HB-Zaehler. Wenn 0 erreicht wird, wird ein Telnet TM Paket als Keep-Alive
+// an den Client gesendet und der Counter wieder auf hochgesetzt.
+// Wenn == 0, ist das Keep-Alive abgeschaltet.
+private int telnet_tm_counter;
+
+nosave string default_home; /* Where to move us if we dont have a home set */
+
+nosave int ndead_lasttime;
+nosave mixed ndead_location;
+nosave string ndead_l_filename;
+nosave int ndead_currently;
+nosave int ndead_next_check;
+nosave object *hb_obs;
+
+private nosave string default_pray_room;
+
+nosave mixed env_ndead_info;
+
+static int _set_invis(int a);
+static string _set_tty(string str);
+static mixed _set_fraternitasdonoarchmagorum(mixed arg);
+
+
+static string al_to_title(int a);
+
+static void ndead_revive();
+
+static int wegmeldung(string player);
+
+int quit();
+void save_me(mixed value_items);
+varargs int remove(mixed arg);
+mixed RaceDefault(string arg);
+
+/** Setzt Defaultwerte vor dem Laden des Savefiles.
+ Nur die Werte bleiben spaeter uebrig, die NICHT aus dem Savefile geladen
+ werden und diese hier ersetzen.
+ Ausserdem kann man hier nicht mit den Werten aus den Savefiles arbeiten.
+ Hierzu muss man updates_after_restore() verwenden.
+*/
+protected void create()
+{
+ if(QueryProp(P_LEVEL))
+ {
+ return; // darf nur EINMAL gemacht werden
+ }
+ call_out("checkConsistency", 0);
+
+ ndead_next_check=NETDEAD_CHECK_TIME;
+ ndead_lasttime=0;
+ ndead_location=0;
+ ndead_l_filename=0;
+ ndead_currently=0;
+ ndead_next_check=0;
+ hc_play=0;
+
+ command::create();
+ properties::create();
+ description::create();
+ light::create();
+ attributes::create();
+ clothing::create();
+ combat::create();
+ life::create();
+ comm::create();
+ viewcmd::create();
+ quests::create();
+ restrictions::create();
+ moving::create();
+ travel::create();
+ skills::create();
+
+ SetProp(P_LEVEL, -1);
+ Set(P_LEVEL, SAVE|SECURED, F_MODE_AS);
+ Set(P_GHOST, SAVE, F_MODE_AS);
+ SetProp(P_SCREENSIZE, -1);
+ Set(P_SCREENSIZE, SAVE,F_MODE_AS);
+ Set(P_MORE_FLAGS, SAVE, F_MODE_AS);
+ SetProp(P_WEIGHT_PERCENT,100);
+ SetProp(P_LONG, 0);
+ SetProp(P_TITLE, "der hoffnungsvolle Anfaenger");
+ SetProp(P_ALIGN, 0);
+ SetProp(P_GENDER, NEUTER);
+ Set(P_GENDER, SAVE, F_MODE_AS);
+ SetProp(P_TTY, "vt100");
+ Set(P_TTY, SAVE, F_MODE_AS);
+ SetProp(P_WEIGHT, 75000);
+ SetProp(P_MAX_HP,50);
+ SetProp(P_MAX_SP,50);
+ SetProp(P_MAX_FOOD,100);
+ SetProp(P_MAX_DRINK,100);
+ SetProp(P_MAX_ALCOHOL,100);
+ Set( P_WIMPY, 20, F_VALUE);
+
+ SetProp(P_HANDS, ({" mit blossen Haenden", 30}));
+ Set(P_HANDS, SAVE, F_MODE_AS);
+ SetProp(P_MAX_HANDS, 2);
+
+ Set(P_MARRIED, SAVE, F_MODE_AS);
+ Set(P_EXTRA_LOOK, SAVE, F_MODE_AS);
+ Set(P_SHOW_EXITS, SAVE, F_MODE_AS);
+ Set(P_SHOW_EXITS, 1);
+ Set(P_WANTS_TO_LEARN, SAVE, F_MODE_AS);
+ Set(P_CAN_FLAGS, SAVE, F_MODE_AS);
+ Set(P_TESTPLAYER, SAVE|PROTECTED, F_MODE_AS);
+ Set(P_ALLOWED_SHADOW, SAVE|SECURED, F_MODE_AS);
+ Set(P_SECOND, SAVE, F_MODE_AS);
+ Set(P_INVIS, SAVE, F_MODE_AS);
+ Set(P_READ_NEWS, SAVE, F_MODE_AS);
+ Set(P_START_HOME, SAVE, F_MODE_AS);
+ Set(P_PRAY_ROOM, SAVE, F_MODE_AS);
+ Set(P_MAILADDR, SAVE, F_MODE_AS);
+ Set(P_HOMEPAGE, SAVE, F_MODE_AS);
+ Set(P_ICQ, SAVE, F_MODE_AS);
+ Set(P_MESSENGER, SAVE, F_MODE_AS);
+ Set(P_LOCATION, SAVE, F_MODE_AS);
+
+ Set(P_NO_ASCII_ART, SAVE, F_MODE_AS);
+
+ Set(P_VISUALBELL, SAVE, F_MODE_AS);
+ Set(P_CARRIED_VALUE, SAVE, F_MODE_AS);
+
+ Set(P_PROMPT, "> ");
+ Set(P_PROMPT, SAVE, F_MODE_AS);
+ Set(P_CALLED_FROM_IP, SAVE, F_MODE_AS);
+ Set(P_INFORMME,SAVE|PROTECTED,F_MODE_AS);
+ Set(P_WAITFOR,SAVE|PROTECTED,F_MODE_AS);
+ Set(P_WAITFOR_REASON,SAVE|PROTECTED,F_MODE_AS);
+ Set(P_DAILY_PLAYTIME,SAVE|PROTECTED,F_MODE_AS);
+ Set(P_NETDEAD_ENV, PROTECTED|NOSETMETHOD, F_MODE_AS);
+
+ autoload = ([]);
+ autoload_rest = ([]);
+ autoload_error = ({});
+ SetProp(P_ARTICLE,0);
+ Set(P_GUILD,SAVE,F_MODE_AS);
+ Set(P_GUILD_TITLE,SAVE,F_MODE_AS);
+ Set(P_GUILD_LEVEL,SAVE,F_MODE_AS);
+ Set(P_GUILD_RATING,SAVE,F_MODE_AS);
+ Set(P_NEWSKILLS,SAVE,F_MODE_AS);
+ Set(P_NEEDED_QP,REQ_QP);
+ Set(P_DEADS,0);
+ Set(P_DEADS, NOSETMETHOD,F_SET_METHOD);
+ Set(P_DEADS,SAVE|PROTECTED|SECURED,F_MODE_AS);
+ Set(P_LAST_LOGIN,-1);
+ Set(P_LAST_LOGIN, NOSETMETHOD,F_SET_METHOD);
+ Set(P_LAST_LOGIN,SAVE|PROTECTED|SECURED,F_MODE_AS);
+ Set(P_LAST_LOGOUT,-1);
+ Set(P_LAST_LOGOUT, NOSETMETHOD,F_SET_METHOD);
+ Set(P_LAST_LOGOUT,SAVE|PROTECTED|SECURED,F_MODE_AS);
+ Set(P_CLOCKMSG,SAVE,F_MODE_AS);
+ Set(P_TIMEZONE,SAVE,F_MODE_AS);
+ Set(P_SHOWEMAIL,SAVE,F_MODE_AS);
+ Set(P_LAST_QUIT,SAVE|PROTECTED|SECURED,F_MODE_AS);
+
+ Set(P_CMSG, 0, F_MODE); // to clean out the old clone messages
+ Set(P_DMSG, 0, F_MODE);
+ Set(P_CLONE_MSG, SAVE, F_MODE);
+ SetProp(P_CLONE_MSG, "zaubert etwas hervor");
+ Set(P_DESTRUCT_MSG, SAVE, F_MODE);
+ SetProp(P_DESTRUCT_MSG, "verschwindet einfach");
+
+ Set(P_FAO, SAVE|SECURED, F_MODE_AS);
+ Set(P_FAO_PORTALS, SAVE, F_MODE_AS);
+
+ SetProp(P_NEWBIE_GUIDE,0);
+ Set(P_NEWBIE_GUIDE,SAVE,F_MODE_AS);
+
+ //TODO: Remove - Property ist not needed any more.
+ Set(P_TELNET_KEEP_ALIVE, PROTECTED|SAVE, F_MODE_AD);
+ SetProp(P_TELNET_KEEP_ALIVE, 0);
+
+ AddId("Interactive");
+
+ realip="";
+}
+
+// ACHTUNG: Falls hier mal sonst noch weitere Resets geerbt werden, muss das
+// hier ordentlich definiert werden!
+void reset() {
+ comm::reset();
+ // momentan kann der Reset jetzt abgeschaltet werden, da er nur die
+ // TM-History loescht und ggf. von Netdead() und Disconnect() reaktiviert
+ // wird.
+ set_next_reset(-1);
+}
+
+protected void NotifyMove(object dest, object oldenv, int method)
+{
+ moving::NotifyMove(dest,oldenv,method);
+ // ggf. Daten ueber neues Env per GMCP senden.
+ if (dest != oldenv)
+ GMCP_Room();
+}
+
+string Forschung()
+{
+ return LEPMASTER->QueryForschung();
+}
+
+/** Setzt Defaultwerte fuer Rassen - wird von den Shells ueberschrieben.
+*/
+mixed RaceDefault(string arg)
+{
+ if (!arg)
+ return 0;
+ switch(arg)
+ {
+ case P_HANDS :
+ return ({" mit blossen Haenden",30,DT_BLUDGEON});
+ case P_BODY :
+ return 0;
+ }
+ return 0;
+}
+
+/** Prueft Spielerobjekt auf Konsistenz.
+ Ueberprueft das Spielerobjekt nach kompletter Initialisierung (im
+ callout) auf korrekte Werte einiger Props.
+*/
+void checkConsistency()
+{ mixed h;
+ int m;
+
+ if (pointerp(h=RaceDefault(P_HANDS)) && sizeof(h)>1)
+ m=(int)h[1];
+ else
+ m=30;
+ if((h=Query(P_HANDS))[1] > m && !IS_LEARNER(this_object())) {
+ log_file("inconsistent", sprintf(
+ "[%s] %O: HANDS: %d\n", dtime(time()), this_player(), h[1]));
+ h[1] = m;
+ Set(P_HANDS,h);
+ }
+
+ if (Query(P_BODY)!=(m=RaceDefault(P_BODY)))
+ Set(P_BODY,m);
+
+ if (!Query(P_SECOND_MARK,F_MODE))
+ Set(P_SECOND_MARK,SAVE|PROTECTED,F_MODE_AS);
+ if (!Query(P_TTY_SHOW,F_MODE)&SAVE)
+ {
+ Set(P_TTY_SHOW,0,F_VALUE);
+ Set(P_TTY_SHOW,SAVE,F_MODE_AS);
+ }
+ if (Query(P_TTY_COLS,F_MODE)&SAVE)
+ {
+ Set(P_TTY_COLS,SAVE,F_MODE_AD);
+ Set(P_TTY_ROWS,SAVE,F_MODE_AD);
+ Set(P_TTY_TYPE,SAVE,F_MODE_AD);
+ }
+}
+
+/** Bittet das Spielerobjekt, sich zu zerstoeren.
+ \param[in] silent Flag, ob ohne Textausgaben zerstoert werden soll
+ \return Erfolg der Selbstzerstoerung
+*/
+varargs int remove(int silent)
+{
+ return moving::remove(silent);
+}
+
+/** Schaltet in allen Objekten im Inv HBs aus.
+ Schaltet im Inv in allen Objekten (rekursiv, deep_inventory)
+ die Heartbeats aus. Falls obs uebergeben wird, werden diese Objekte
+ statt des Inventars benutzt.
+ Speichert die Objekte, die einen HB hatten, in der glob. Var. hb_obs.
+ @param[in] obs mixed - Objekte, in denen der HB abgeschaltet werden soll.
+ Container werden rekursiv behandelt.
+ @attention Achtung! Niemals zweimal hintereinander rufen, ohne
+ zwischendurch restart_heart_beats() gerufen zu haben!
+ @sa restart_heart_beats
+*/
+varargs static void stop_heart_beats(mixed obs)
+{
+ int i;
+
+ if (!obs)
+ {
+ hb_obs=({});
+ obs=deep_inventory(ME);
+ }
+ foreach(mixed ob: obs) {
+ if (pointerp(ob))
+ stop_heart_beats(ob);
+ else if (set_object_heart_beat(ob,0))
+ hb_obs+=({ob});
+ }
+}
+
+/** Schaltet HBs in Objekten im Inv wieder ein.
+ Schaltet in allen Objekten in hb_obs den Heartbeat ein.
+ In hb_obs (glob. Var.) stehen alle Objekte, die beim letzten Aufruf von
+ stop_heart_beats() einen HB hatten.
+ @sa stop_heart_beats
+*/
+static void restart_heart_beats()
+{
+ int i;
+
+ if (pointerp(hb_obs))
+ {
+ foreach(object ob: hb_obs)
+ set_object_heart_beat(ob,1);
+ hb_obs=0;
+ }
+}
+
+/** Prueft auf abgelaufene Spielzeit.
+ Prueft in Spielerobjekten, ob die taegliche Maximalspielzeit
+ abgelaufen ist.
+ Wird im heart_beat() gerufen.
+ @return int - Flag, ob Spielzeit abgelaufen und Logout erfolgen soll
+*/
+static int CheckDailyPlaytime() {
+ int *spieldauer,d;
+
+ if (!pointerp(spieldauer=Query(P_DAILY_PLAYTIME)))
+ return 0;
+ // 0:Minuten pro Tag, 1:Nr. des letzen Tages, 2:Nr. des angefangenen Tages,
+ // 3:letzte Zeitpruefung, 4:verbleibende Zeit
+ d=time()/86400;
+ if (spieldauer[1]<=d) { // Ende der zeitbeschraenkten Tage?
+ Set(P_DAILY_PLAYTIME,0);
+ return 0;
+ } else if (spieldauer[2]!=d) { // Neuer Tag?
+ spieldauer[4]=spieldauer[0];
+ spieldauer[2]=d;
+ } else {
+ spieldauer[4]-=(time()-spieldauer[3]);
+ }
+ spieldauer[3]=time(); // Letzte Zeitpruefung
+ Set(P_DAILY_PLAYTIME,spieldauer);
+ if (spieldauer[4]<0) { // Keine Zeit mehr uebrig fuer heute
+ if (!interactive(ME))
+ return 1;
+ write("Du hast lange genug gemuddet fuer heute.\n");
+ say(Name(WER)+" hat fuer heute genug gemuddet.\n");
+ remove_interactive(ME);
+ return 1;
+ }
+ return 0;
+}
+
+/** Gibt Erwartemeldung mit Grund aus.
+ @param[in] who string - Wer ist hereingekommen?
+ @param[in] invis int - Ist der Spieler Invis?
+*/
+static void Show_WaitFor_Reason(string who, int invis)
+{
+ mixed list;
+ string reason,name;
+
+ if (invis) name="("+who+")";
+ else name=who;
+ if ((mappingp(list=QueryProp(P_WAITFOR_REASON))) && (reason=list[who]))
+ tell_object(ME,sprintf("\nDu erwartest %s wegen:\n%s\n",name,reason));
+ else
+ tell_object(ME,sprintf("Du erwartest %s aus keinem bestimmten Grund.\n",
+ name));
+}
+
+/** Gibt Liste der Erwarteten Spieler an this_player() aus.
+*/
+static void ListAwaited() //Anwesende Erwartete auflisten
+{
+ string *list;
+ mixed mlist;
+ object ob;
+ int mag;
+
+ mag=IS_LEARNER(ME);
+
+ list=({});
+ foreach(string erwartet : QueryProp(P_WAITFOR)) {
+ if (objectp(ob=find_player(lower_case(erwartet)))) {
+ if (ob->QueryProp(P_INVIS)) {
+ if (mag) list+=({ sprintf("(%s)",erwartet) });
+ }
+ else list+=({erwartet});
+ }
+ }
+ if (sizeof(list))
+ printf("Anwesende Erwartete: %s.\n",
+ CountUp(sort_array(list,#'>)));
+
+ if ((mappingp(mlist=QueryProp(P_WAITFOR_REASON))) && (sizeof(mlist)))
+ {
+ foreach(string erwartet : mlist) {
+ if (!(ob=find_player(lower_case(erwartet))) ||
+ (!mag && ob->QueryProp(P_INVIS)));
+ else Show_WaitFor_Reason(erwartet,ob->QueryProp(P_INVIS));
+ }
+ }
+}
+/** Teilt den Gilden und anderen Spielern mit, wer reingekommen ist.
+ Ausserdem wird ggf. PlayerQuit() im Environment gerufen.
+ \param[in] rein int - wahr, wenn der Spieler einloggt.
+*/
+protected void call_notify_player_change(int rein)
+{
+ string wer = getuid(ME);
+ // erst die Gilde informieren
+ string gilde = QueryProp(P_GUILD);
+ if (stringp(gilde) && (find_object("/gilden/"+gilde)
+ || file_size("/gilden/"+gilde+".c")>0))
+ catch(("/gilden/"+gilde)->notify_player_change(ME, rein); publish);
+
+ // dann die anderen Spieler
+ int mag = IS_LEARNER(ME);
+ int invis = QueryProp(P_INVIS);
+ object *u = users() - ({ME}); // sich selber nicht melden
+ if (mag && invis) { // Invismagier nur Magiern melden
+ u = filter(u, function int (object o)
+ { return query_wiz_level(o) >= LEARNER_LVL; }
+ );
+ }
+ u->notify_player_change(capitalize(wer),rein,invis);
+
+ // und beim Ausloggen noch das Env informieren.
+ if (!rein) {
+ if(environment()) catch(environment()->PlayerQuit(ME);publish);
+ }
+}
+
+/** Ruft im uebergebenen Objekt ein init() auf, sofern notwendig.
+ Ruft in ob ein init() auf, falls das Objekt nach dem
+ letzten Ausloggen geschaffen wurde.
+ \param[in] ob object - Objekt, in dem init() gerufen wird.
+ \param[in] logout int - Letzter Logout
+ \return 1, falls init() gerufen wurde. 0 sonst.
+*/
+static int call_init( object ob, int logout )
+{
+ if ( objectp(ob) && object_time(ob) > logout )
+ return catch(ob->init(); publish), 1;
+ return(0);
+}
+
+/** Holt seit dem letzten Ausloggen ausgefallene Inits nach.
+ Ruft in den uebergebenen Objekten call_init(), was einen init()
+ ausloest, falls das Objekt seit dem letzten Ausloggen erstellt wurde.
+ \param[in] logout Zeitpunkt des letzten Logouts
+ \param[in] obs Array von Objekten
+ \sa call_init()
+*/
+static void inits_nachholen( int logout, object *obs )
+{
+ filter( obs, "call_init", ME, logout );
+}
+
+/** Belebt einen Netztoten wieder.
+ Wird im Login gerufen, wenn der Spieler netztot war. Aequivalent zu
+ start_player()
+ @param[in] silent Wenn Flag gesetzt, werden keine Meldung an den Raum
+ ausgegeben.
+ @param[in] ip Textuelle Repraesentation der IP-Adresse, von der der Spieler
+ kommt.
+ @see start_player()
+*/
+varargs void Reconnect( int silent )
+{
+ int num;
+ string called_from_ip;
+ object *inv;
+
+ if ( query_once_interactive(ME) )
+ {
+ // perform the telnet negotiations. (all that are available)
+ "*"::startup_telnet_negs();
+ Set( P_LAST_LOGIN, time() );
+ }
+
+ enable_commands();
+ set_living_name( getuid() );
+ _remove_netdead();
+ set_heart_beat(1);
+ // Hunttimes aktualisieren und ggf. Feinde vergessen.
+ update_hunt_times((time()-QueryProp(P_LAST_LOGOUT)) /__HEART_BEAT_INTERVAL__);
+ // Heartbeats in Objekten im Inv reaktiveren.
+ restart_heart_beats();
+ // life.c will ggf. was aufraeumen
+ life::reconnect();
+
+ log_file( "REENTER", sprintf( "%-11s %s, %-15s (%s).\n",
+ capitalize(getuid(ME)), ctime(time())[4..15],
+ query_ip_number(ME)||"Unknown",
+ query_ip_name(ME)||"Unknown" ),
+ 200000 );
+
+ if ( ndead_currently )
+ ndead_revive();
+
+ if ( !silent && interactive(ME) )
+ call_notify_player_change(1);
+
+ command::reconnect();
+ if ( query_once_interactive(ME) )
+ modify_prompt();
+
+ // Login-event ausloesen
+ EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
+ E_OBJECT: ME,
+ E_PLNAME: getuid(ME),
+ E_ENVIRONMENT: environment() ]) );
+
+ catch( num = "secure/mailer"->FingerMail(geteuid());publish );
+
+ if ( num )
+ write( "Du hast " + num + " neue" + (num == 1 ? "n Brief" : " Briefe")
+ + " im Postamt liegen.\n" );
+
+ if ( QueryProp(P_AWAY) )
+ write( break_string( "Du bist als abwesend gekennzeichnet: " +
+ QueryProp(P_AWAY) + ".", 78 ) );
+
+ catch( RegisterChannels(); publish );
+
+ if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
+ query_ip_number(ME) != called_from_ip ) {
+ string tmp;
+
+ if ( stringp(tmp = query_ip_name(called_from_ip)) &&
+ tmp != called_from_ip )
+ tmp = " [" + tmp + "]";
+ else
+ tmp = "";
+
+ write( "Das letzte Mal kamst Du von " + called_from_ip + tmp + ".\n" );
+ }
+
+ Set( P_CALLED_FROM_IP, query_ip_number(ME) );
+
+ // falls Gegenstaende mit 'upd -ar' upgedated wurden, muessen die
+ // "verloren gegangenen" init()'s nachgeholt werden
+ if ( query_once_interactive(ME) && !IS_LEARNER(ME) )
+ call_out( "inits_nachholen", 0, Query(P_LAST_LOGOUT),
+ all_inventory(ME) );
+
+ // noch nicht geclonte Autoloader "nach"clonen
+ while ( remove_call_out("load_auto_objects") != -1 )
+ /* do nothing */;
+
+ if ( sizeof(autoload_rest) )
+ call_out( "load_auto_objects", 0, autoload_rest );
+
+ if (ndead_location) {
+ catch( ndead_location->BecomesNetAlive(ME);publish );
+ inv = all_inventory(ndead_location);
+ ndead_location = 0;
+ }
+ else
+ inv = ({});
+
+ inv += deep_inventory(ME);
+
+ //ZZ foreach statt call_other(), damit nen bug in BNA nicht die anderen
+ //BNA verhindert.
+ foreach(object ob: inv) {
+ //es ist nicht auszuschliessen, dass Items durch BecomesNetAlive()
+ //eines anderen zerstoert werden.
+ if (objectp(ob))
+ catch( call_other(ob, "BecomesNetAlive", ME);publish );
+ }
+
+ // Erst an dieser Stelle, weil der Spieler u.U. durch ein BecomesNetAlive()
+ // noch bewegt wurde.
+ if ( !silent && environment() && object_name(environment()) != NETDEAD_ROOM )
+ {
+ if(query_hc_play()<=1)
+ tell_room(environment(),QueryProp(P_NAME) + " weilt wieder unter den Lebenden.\n",({ME}) );
+ else
+ tell_room(environment(),QueryProp(P_NAME) + " weilt wieder unter den Verstorbenen.\n",({ME}) );
+ }
+
+ NewbieIntroMsg();
+
+ if ( query_once_interactive(ME) )
+ ListAwaited();
+}
+
+/** Loggt einen Spieler aus und macht ihn netztot.
+ Bewegt einen Spieler in den Netztotenraum, deaktiviert Heartbeats im
+ Inventar, ruft BecomesNetDead(), loest Erwartemeldungen aus, triggert
+ Ausloggevent.
+*/
+void NetDead()
+{
+ object *inv;
+ int num;
+
+ catch(RemoveChannels();publish);
+
+ if(query_hc_play()>1)
+ say("Ploetzlich weicht alle spirituelle Energie aus "+QueryProp(P_NAME)+".\n");
+ else
+ say("Ploetzlich weicht alles Leben aus "+QueryProp(P_NAME)+".\n");
+
+ _set_netdead();
+ remove_call_out("quit");
+ remove_living_name();
+ // Wird zwar im save_me() gemacht, aber die Zeitunterschiede beim
+ // fingern direkt nach dem Ausloggen fuehren immer wieder zu Verwirrungen
+ if(query_once_interactive(ME) && !QueryProp(P_INVIS))
+ Set(P_LAST_LOGOUT,time());
+ if (ME)
+ ndead_location = environment();
+
+ if (query_once_interactive(ME))
+ call_notify_player_change(0);
+
+ // Logout-event ausloesen
+ EVENTD->TriggerEvent(EVT_LIB_LOGOUT, ([
+ E_OBJECT: ME,
+ E_PLNAME: getuid(ME),
+ E_ENVIRONMENT: environment() ]) );
+
+ set_next_reset(900);
+ /* Bei Nicht-Magier-Shells wird comm::reset() aufgerufen, das prueft, ob
+ der Spieler immer noch netztot ist, und falls ja, die tmhist loescht.
+ Die Methode wird von /std/shells/magier.c ueberschrieben, netztote
+ Magier (die eigentlich schon anderweitig beseitigt worden sein sollten)
+ werden remove()d und destruct()ed. --Amynthor 05.05.2008 */
+
+ if (environment()) {
+ catch(environment()->BecomesNetDead(ME);publish);
+ inv = deep_inventory(ME)+all_inventory(environment());
+ }
+ else inv=deep_inventory(ME);
+ foreach(object ob: inv) {
+ if (objectp(ob)) //man weiss nie was BND() macht...
+ catch( call_other(ob, "BecomesNetDead", ME);publish );
+ }
+}
+
+
+/** Sendet ggf. Telnet Timing Marks als Keep-Alive Pakete an den Client.
+ * Wird in heart_beat() gerufen.
+ * @return 1, falls der Spieler Keep-Alive Paket wuenscht, sonst 0.
+ * @see heart_beat()
+*/
+protected int CheckTelnetKeepAlive() {
+ if (telnet_tm_counter > 0) {
+ // Spieler hat offenbar ein Keep-Alive konfiguriert ...
+ if (!(--telnet_tm_counter)) {
+ // und das Intervall ist gerade abgelaufen.
+ // Prop offenbar gesetzt, FEature ist wirklich gewuenscht,
+ // Telnet Timing Mark senden
+ send_telnet_timing_mark();
+ // alle 120 HBs (240s, 4min).
+ // sollte eigentlich 240 / __HEART_BEAT_INTERVAL__ sein. Aber spart
+ // eine Operation im HB. ;-)
+ telnet_tm_counter = 120;
+ }
+ return 1; // Keep-Alive ist eingeschaltet
+ }
+ return 0;
+}
+
+static void ndead_move_me();
+
+/** Heartbeat des Spielerobjektes.
+ Prueft taegliche Spielzeit, speichert regelmaessig den Spieler,
+ bewegt Netztote in den Netztodenraum, ruft die HBs aus living/combat und
+ player/life.
+*/
+protected void heart_beat() {
+ if (!ME)
+ return;
+ if (ndead_currently)
+ {
+ if (interactive(ME))
+ {
+ ndead_revive();
+ ndead_location=0;
+ }
+ else return;
+ }
+ else
+ if (!(ndead_next_check--))
+ {
+ ndead_next_check=NETDEAD_CHECK_TIME;
+ if (!interactive(ME))
+ if (ndead_lasttime)
+ {
+ save_me(1);
+ if (IS_LEARNER(ME))
+ {
+ quit();
+ if (ME)
+ remove();
+ if (ME)
+ destruct(ME);
+ return;
+ }
+ ndead_move_me();
+ // Zumindest bei Gaesten ist das Objekt jetzt zerstoert
+ if (!objectp(this_object()))
+ return;
+ }
+ else
+ ndead_lasttime=1;
+ }
+ if (ME && ndead_lasttime && interactive(ME))
+ ndead_lasttime=0;
+ if (CheckDailyPlaytime())
+ return;
+
+ CheckTelnetKeepAlive();
+
+ life::heart_beat();
+ combat::heart_beat();
+ skills::heart_beat();
+}
+
+/** ID-Funktion fuer Spielerobjekte.
+ * id() fuer Spieler. Besondere Behandlung fuer Froesche und Geister,
+ * sowie Invis-Status.
+ * Gibt immer 0 zurueck, wenn P_INVIS && lvl < P_LEVEL
+ * @param[in] str angefragter ID-String
+ * @param[in] lvl Level des Anfragenden
+ * @return 1, falls str auf den Spieler zutrifft, 0 sonst.
+ */
+varargs int id(string str, int lvl)
+{
+ if (::id(str))
+ return 1;
+ if (Query(P_INVIS) && lvl < QueryProp(P_LEVEL))
+ return 0;
+ if (QueryProp(P_GHOST)&& str == "geist von "+ lower_case(QueryProp(P_NAME)))
+ return 1;
+ if (QueryProp(P_FROG) && str == "frosch") return 1;
+ return(0);
+}
+
+/** Setzt Spielerhomepage (Spielerkommando).
+ * @param[in] str Spielereingabe
+ * @return 1 bei Erfolg, 0 sonst.
+ */
+static int set_homepage(string str)
+{
+ mixed tmp;
+ if (!(str=_unparsed_args())) {
+ if (!QueryProp(P_HOMEPAGE))
+ write("Du hast keine URL-Adresse gesetzt!\n");
+ else
+ write("Deine offizielle URL-Adresse lautet: " + QueryProp(P_HOMEPAGE)
+ +"\n");
+ return 1;
+ }
+ write("Deine offizielle URL-Adresse wurde geaendert.\n");
+ if (str=="keine")
+ SetProp(P_HOMEPAGE, 0);
+ else {
+ tmp = filter(regexplode(str, "[<][^>]*[>]"),
+ lambda(({'e}), ({#'!=, ({#'[, 'e, 0}), '<'})));
+ write("Sie lautet jetzt: "+(str = implode(tmp, ""))+"\n");
+ SetProp(P_HOMEPAGE, str);
+ }
+ return 1;
+}
+
+/** Setzt Spieler-Wohnort (Spielerkommando).
+ * \param[in] str Spielereingabe
+ * \return 1 bei Erfolg, 0 sonst.
+ */
+static int set_location( string str )
+{
+ mixed ort;
+
+ if ( str == "0" || str == "loeschen" ){
+ Set( P_LOCATION, 0 );
+ write( "Du loescht Deine Ortsangabe.\n" );
+ return 1;
+ }
+
+ if ( stringp(str = _unparsed_args()) && str != "" ){
+ Set( P_LOCATION, capitalize(str) );
+ printf( "Du aenderst Deine Ortsangabe auf \"%s\".\n",
+ Query(P_LOCATION) );
+ }
+ else if ( stringp(ort = Query(P_LOCATION)) )
+ printf( "Deine Ortsangabe lautet \"%s\".\n", Query(P_LOCATION) );
+ else{
+ Set( P_LOCATION, 0, F_VALUE );
+ write( "Du hast keine Ortsangabe gesetzt.\n" );
+ }
+
+ return 1;
+}
+
+/** Setzt ICQ-UIN des Spielers (Spielerkommando).
+ * \param[in] str Spielereingabe
+ * \return 1 bei Erfolg, 0 sonst.
+ */
+static int set_icq(string str) {
+ int num;
+
+ if (!str || str=="") {
+ if (!num=QueryProp(P_ICQ))
+ write("Du hast keine ICQ-Nummer gesetzt!\n");
+ else
+ printf("Deine ICQ-Nummer lautet: %d\n",num);
+ return 1;
+ }
+ if (sscanf(str,"%d",num)!=1 || !num) {
+ write("Deine ICQ-Nummer wurde geloescht.\n");
+ SetProp(P_ICQ, 0);
+ } else {
+ write("Deine ICQ-Nummer wurde geaendert.\n");
+ printf("Sie lautet jetzt: %d\n",num);
+ SetProp(P_ICQ, num);
+ }
+ return 1;
+}
+
+/** Setzt Instant Messanger vom Spieler (Spielerkommando).
+ * \param[in] str Spielereingabe
+ * \return 1 bei Erfolg, 0 sonst.
+ */
+static int set_messenger(string str) {
+ int num;
+ string s;
+
+ if (!str || str=="") {
+ if (!s=QueryProp(P_MESSENGER))
+ if (!num=QueryProp(P_ICQ))
+ write("Du hast keine Messenger-ID gesetzt.\n");
+ else
+ printf("Du hast keine Messenger-ID gesetzt, aber eine ICQ-Nummer: %d\n", num);
+ else
+ printf("Deine Messenger-ID lautet: %s\n", s);
+ return 1;
+ }
+ if (str=="loeschen" || str=="keine") {
+ write("Deine Messenger-ID wurde geloescht.\n");
+ SetProp(P_MESSENGER, 0);
+ } else {
+ s = _unparsed_args();
+ printf("Deine Messenger-ID lautet nun: %s\n", s);
+ SetProp(P_MESSENGER, s);
+ }
+ return 1;
+}
+
+
+// Prueft, ob der String vermutlich eine eMail-Adresse ist.
+// dies ist nicht narrensicher, wird aber die meisten eMail-Adressen zulassen
+// und viel Schrott ablehnen.
+private string check_email(string str) {
+ if (!stringp(str)) return 0;
+ return regmatch(lower_case(str),
+ "[a-z0-9._%+-]+@(?:[a-z0-9-]+\.)+[a-z]{2,4}",RE_PCRE);
+}
+
+/** Setzt Email-Adresse des Spielers (Spielerkommando).
+ * \param[in] str Spielereingabe
+ * \return 1 bei Erfolg, 0 sonst.
+ */
+static int set_email(string str)
+{
+ if (!(str=_unparsed_args())) {
+ write("Deine offizielle Email-Adresse lautet: " + QueryProp(P_MAILADDR)
+ +"\n");
+ return 1;
+ }
+ str = check_email(str);
+ if (!str) {
+ notify_fail("Deine Eingabe scheint keine gueltige EMail-Adresse "
+ "zu sein.\n");
+ return 0;
+ }
+ write("Deine EMail-Adresse wurde geaendert zu:\n"
+ +str+"\n");
+ SetProp(P_MAILADDR, str);
+ return 1;
+}
+
+/** Spielerkommando 'selbstloeschung'.
+ * Gibt Meldung aus und fragt nach einer Bestaetigung.
+ * \return 1 bei Erfolg, 0 sonst.
+ */
+static int self_delete()
+{
+ write(
+ " B I S T D U D I R W I R K L I C H S I C H E R ????????????\n"+
+ "Wenn Du Dich selbstloeschen willst, ist Dein Charakter UNWIDERRUFLICH\n"+
+ "verloren. Es gibt KEINE Moeglichkeit ihn wiederzuerschaffen. Solltest\n"+
+ "Du nur zeitweilig vom "MUDNAME" wegbleiben wollen, so benutze bitte\n"+
+ "den Befehl 'spielpause'.\n"+
+ "Fallst Du Dich immer noch selbstloeschen willst, gib Dein Password ein."+
+ "\n\n");
+ input_to("self_delete2",INPUT_PROMPT|INPUT_NOECHO, "Bitte das Password angeben: ");
+ return 1;
+}
+
+/** Spielerkommando 'selbstloeschung'.
+ * Empfaengt Bestaetigung des Spielers und ruft Loeschfunktion in
+ * /secure/master auf.
+ * \param[in] str Spielereingabe
+ * \return 1 bei Erfolg, 0 sonst.
+ */
+int self_delete2(string str)
+{
+ int ret;
+ ret=(int)"secure/master"->delete_player(str, getuid(PL));
+ if (!ret)
+ {
+ write("Das hat nicht hingehauen (Gott sei Dank ....)\n");
+ return 1;
+ }
+ if (QueryProp(P_GUILD)&&file_size(GUILD_DIR+QueryProp(P_GUILD)+".c")>-1)
+ catch(call_other(GUILD_DIR+QueryProp(P_GUILD), "austreten");publish);
+
+ if (QueryProp(P_DEADS) < 5) {
+ write("Adios! Man sieht sich.\n");
+ say(name(WER,1)+" hat sich gerade selbst zerstoert.\n");
+ }
+ else {
+ write(
+ "\nTod kommt auf seinem weissen Pferd angeritten.\n"
+ +"Er steigt ab, baut sich drohend vor Dir auf und mustert Dich schadenfroh.\n"
+ +"\nTod sagt: ENDLICH! NUN KANN DIR AUCH LARS NICHT MEHR HELFEN!\n"
+ +"\nTod holt weit mit seiner Sense aus. Mit grossem Schwung laesst er sie auf\n"
+ +"Dich zusausen und dann...\n");
+ say(name(WER,1)+" schied gerade endgueltig von uns.\n");
+ }
+
+ // Event ausloesen. ;-)
+ EVENTD->TriggerEvent(EVT_LIB_PLAYER_DELETION, ([
+ E_PLNAME: getuid(ME),
+ E_ENVIRONMENT: environment(),
+ E_GUILDNAME: QueryProp(P_GUILD) ]) );
+
+ remove(1);
+ return 1;
+}
+
+/** Setzt neue taegliche Spieldauer (Spielerkommando).
+ * \param[in] str Spielereingabe
+ * \return 1 bei Erfolg, 0 sonst.
+ */
+static int spieldauer(string str) {
+ int min,day;
+ string foo;
+
+ notify_fail(" spieldauer <x> minuten fuer %d tage\noder\n"+
+ " spieldauer <x> stunden fuer %d tage\n");
+ if (!str)
+ return 0;
+ if (sscanf(str,"%d stunde%s fuer %d tag%s",min,foo,day,foo)==4)
+ min*=60;
+ else if (sscanf(str,"%d minute%s fuer %d tag%s",min,foo,day,foo)!=4)
+ return 0;
+ if (min<5)
+ min=5;
+ if (min>=1440)
+ return notify_fail("Witzbold.\n"),0;
+
+ Set(P_DAILY_PLAYTIME,
+ ({min*60,time()/86400+day,time()/86400,time(),min*60}));
+ // 0:Minuten pro Tag, 1:Nr. des letzen Tages, 2:Nr. des angefangenen Tages,
+ // 3:letzte Zeitpruefung, 4:verbleibende Zeit
+ printf("Du darfst die naechsten %d Tag(e) nur noch\n"+
+ "%d Minuten am Tag mudden.\n",day,min);
+ return 1;
+}
+
+/** Interpretiert Angabe des Spielers fuer Spielpause.
+ * \param[in] a Zeitangabe fuer Spielpause.
+ * \return Zeitpunkt, Ende der Spielpause.
+ */
+private int InterpretTime(string a){
+ // akzeptiert folgende Formate:
+ // dd.mm.jj (Rueckgabe: 0:00 des entsprechenden Tages)
+
+ int *ts = allocate(9);
+ int i,j,k,nrargs;
+
+ if ((nrargs=sscanf(a,"%d.%d.%d",i,j,k))==3 ||
+ (nrargs=sscanf(a,"%d.%d.",i,j))==2) {
+ // wenn kein jahr angegeben ist, das aktuelle nehmen.
+ if (nrargs == 2)
+ ts[TM_YEAR] = localtime()[TM_YEAR];
+ else {
+ // Zwei-Ziffern-Angabe des Jahres...
+ if (k<100)
+ k += 2000;
+ ts[TM_YEAR] = k;
+ }
+ ts[TM_MDAY] = i;
+ ts[TM_MON] = j - 1;
+
+ int zeit = mktime(ts);
+
+ // negative und vergangene Zeiten pruefen.
+ if (zeit <= time()) {
+ write("Dieser Zeitpunkt liegt in der Vergangenheit.\n");
+ return 0;
+ }
+ return zeit;
+ }
+ return 0;
+}
+
+/** Setzt neue Spielpause (Spielerkommando).
+ * Fragt vorher nach Bestaetigung durch den Spieler.
+ * \param[in] str Spielereingabe
+ * \return 1 bei Erfolg, 0 sonst.
+ * \sa spielpause2()
+ */
+static int spielpause(string str)
+{
+ int days,endezeit;
+ string foo;
+
+ notify_fail("spielpause <x> tage oder\n"+
+ "spielpause bis tt.mm[.jj]\n");
+ if (!str) return 0;
+ if(sscanf(_unparsed_args(),"bis %s",foo)==1) {
+ endezeit = InterpretTime(foo);
+ if (endezeit == 0)
+ return 0;
+ days = ((endezeit - time()) / 86400) + 1;
+ }
+ else if(sscanf(str, "%d tag%s", days, foo) == 2) {
+ if (days < 0)
+ days = -1;
+ else
+ endezeit = (time()/86400) * 86400 + days * 86400;
+ }
+ else return 0;
+
+ if (days > 0)
+ write(strftime("Du wirst Dich erst wieder am %d.%m.%Y einloggen koennen!\n",
+ endezeit));
+ else if (days < 0)
+ write( "Du wirst Dich auf unbestimmte Zeit nicht mehr einloggen koennen.\n"
+ +"Wenn Du wieder spielen willst, musst Du Dich an einen Gott oder\n"
+ +"Erzmagier wenden (mit einem Gast oder Mail von aussen).\n" );
+ else {
+ write( "Die Spielpause ist aufgehoben.\n" );
+ master()->TBanishName(getuid(this_object()), 0);
+ return 1;
+ }
+ write( "Wenn Du das wirklich willst, gib jetzt 'ja' ein.\n" );
+ input_to( "spielpause2", INPUT_PROMPT, "]", days);
+ return 1;
+}
+
+/** Setzt neue taegliche Spieldauer, wird ueber spielpause() gerufen.
+ * \param[in] str Spielereingabe, Bestaetigung
+ * \param[in] days Dauer der Spielpause (in Tagen).
+ * \sa spielpause()
+ */
+static void spielpause2(string str, int days)
+{
+ if (str && (str == "ja" || str == "Ja" || str == "JA")) {
+ master()->TBanishName(getuid(this_object()), days);
+ write(
+ "Ok, die Spielpause wird mit dem naechsten Ausloggen wirksam.\n"
+ +"Solltest Du es Dir bis dahin noch einmal ueberlegt haben, so kannst\n"
+ +"Du den Vorgang mit 'spielpause 0 tage' wieder rueckgaengig machen.\n" );
+ return;
+ }
+ write("Vorgang wurde abgebrochen.\n" );
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+ * Fragt nach altem Passwort und ruft change_password2().
+ * \return 1 bei Erfolg, 0 sonst.
+ * \sa change_password2(), change_password3(), change_password4()
+ */
+static int change_password() {
+ string verb;
+ verb=query_verb();
+ if (verb!="passwd"&&verb!="password"&&verb!="passwort")
+ return 0;
+ input_to("change_password2",INPUT_NOECHO|INPUT_PROMPT,
+ "Bitte das ALTE Passwort angeben: ");
+ return 1;
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+ * Prueft altes Passwort, fragt nach neuem Passwort und ruft
+ * change_password3().
+ * \param[in] str Spielereingabe des alten Passwortes
+ * \return 1 bei Erfolg, 0 sonst.
+ * \sa change_password(), change_password3(), change_password4()
+ */
+static int change_password2(string str) {
+ write("\n");
+ if (!str)
+ str="";
+ if (MASTER->update_password(str,str) == 0) {
+ write("Falsches Passwort!\n");
+ return 1;
+ }
+ passwold = str;
+ input_to("change_password3",INPUT_NOECHO|INPUT_PROMPT,
+ "Bitte das NEUE Passwort eingeben: ");
+ return 1;
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+ * Prueft neues Passwort, fragt nach Bestaetigung des neues
+ * Passwortes und ruft change_password4().
+ * \param[in] str Spielereingabe des neuen Passwortes
+ * \return 1 bei Erfolg, 0 sonst.
+ * \sa change_password(), change_password2(), change_password4()
+ */
+static int change_password3( string str )
+{
+ write( "\n" );
+
+ if ( !str || str == "" ){
+ write( "Abgebrochen !\n" );
+ passwold = passw = 0;
+ return 1;
+ }
+
+ if ( passwold == str ){
+ write( "Das war Dein altes Passwort.\n" );
+ input_to( "change_password3", INPUT_NOECHO|INPUT_PROMPT,
+ "Bitte das NEUE Passwort eingeben (zum Abbruch Return druecken): ");
+ return 1;
+ }
+
+ if ( !MASTER->good_password( str, getuid(ME) ) ){
+ input_to( "change_password3", INPUT_NOECHO|INPUT_PROMPT,
+ "Bitte das NEUE Passwort eingeben: ");
+ return 1;
+ }
+
+ passw = str;
+ input_to( "change_password4", INPUT_NOECHO|INPUT_PROMPT,
+ "Bitte nochmal: ");
+ return 1;
+}
+
+/** Setzt neues Passwort (Spielerkommando).
+ * Prueft neues Passwort und setzt neues Passwort.
+ * \param[in] str Spielereingabe des neuen Passwortes
+ * \return 1 bei Erfolg, 0 sonst.
+ * \sa change_password(), change_password2(), change_password3()
+ */
+static int change_password4( string str )
+{
+ write( "\n" );
+
+ if ( !str || str != passw ){
+ write( "Das war verschieden! Passwort NICHT geaendert.\n" );
+ passwold = passw = 0;
+ return 1;
+ }
+
+ if ( MASTER->update_password( passwold, passw ) )
+ write( "Passwort geaendert.\n" );
+ else
+ write( "Hat nicht geklappt!\n" );
+
+ passwold = passw = 0;
+ return 1;
+}
+
+
+/*
+ *-----------------------------------------------------------------
+ * Rueckmeldungen von Spielern an Magier
+ *-----------------------------------------------------------------
+ */
+static int fehlerhilfe(string str) {
+ write("Welche Art von Fehler moechtest Du denn melden?\n"
+ "Fehlfunktionen -> bug\n"
+ "Ideen/Anregungen -> idee\n"
+ "Tippfehler/Typos -> typo\n"
+ "fehlende Details -> detail\n");
+
+ return 1;
+}
+
+/** Setzt eine Fehlermeldung an Magier ab (Spielerkommando).
+ * Fragt nach der Fehlermeldung und liest sie via bug2() ein, fall der
+ * Spieler kein Argument angeben hat.
+ * \param[in] str optionale Spielereingabe der Fehlerbeschreibung
+ * \return 1 bei Erfolg, 0 sonst.
+ * @see bug2(string)
+ */
+static int bug(string str) {
+ if (!(str=_unparsed_args())) {
+ write( "Wie sieht der Fehler denn aus?\n" );
+ input_to("bug2", INPUT_PROMPT, "]");
+ return 1;
+ }
+ write("Vielen Dank fuer die Hilfe.\n");
+ smart_log("BUGS",str);
+ return 1;
+}
+
+/** Setzt eine Fehlermeldung an Magier ab (Spielerkommando).
+ * Lies Fehlerbeschreibung ein und speichert sie ab.
+ * \param[in] str Spielereingabe der Fehlerbeschreibung.
+ * \return 1 bei Erfolg, 0 sonst.
+ * @see bug(string)
+ */
+static int bug2(string str) {
+ if (!str || str == "") {
+ write("Bug abgebrochen...\n");
+ return 1;
+ }
+ write("Vielen Dank fuer die Hilfe.\n");
+ smart_log("BUGS",str);
+ return 1;
+}
+
+/** Setzt eine Typomeldung an Magier ab (Spielerkommando).
+ * Fragt nach der Typomeldung und liest sie via typo2() ein, fall der
+ * Spieler kein Argument angeben hat.
+ * \param[in] str optionale Spielereingabe der Typobeschreibung
+ * \return 1 bei Erfolg, 0 sonst.
+ * @see typo2(string)
+ */
+static int typo(string str) {
+ if (!(str=_unparsed_args())) {
+ write( "Wo ist denn der Tippfehler?\n" );
+ input_to("typo2", INPUT_PROMPT, "]");
+ return 1;
+ }
+ write("Vielen Dank fuer die Hilfe.\n");
+ smart_log("TYPO",str);
+ return 1;
+}
+
+/** Setzt eine Fehlermeldung an Magier ab (Spielerkommando).
+ * Liest die Typobeschreibung ein und speichert sie.
+ * \param[in] str Spielereingabe der Typobeschreibung
+ * \return 1 bei Erfolg, 0 sonst.
+ * @see typo(string)
+ */
+static int typo2(string str) {
+ if (!str || str == "") {
+ write("Typo abgebrochen...\n");
+ return 1;
+ }
+ smart_log("TYPO",str);
+ write("Vielen Dank fuer die Hilfe.\n");
+ return 1;
+}
+
+/** Setzt eine Idee an Magier ab (Spielerkommando).
+ * Fragt nach der Idee und liest sie via idee2() ein, falls der
+ * Spieler kein Argument angeben hat.
+ * \param[in] str optionale Spielereingabe der Idee
+ * \return 1 bei Erfolg, 0 sonst.
+ * @see idea2(string)
+ */
+static int idea(string str) {
+ if (!(str=_unparsed_args())) {
+ write( "Was fuer eine Idee hast Du denn?\n" );
+ input_to("idea2",INPUT_PROMPT, "]");
+ return 1;
+ }
+ write("Vielen Dank fuer die Hilfe.\n");
+ smart_log("IDEA",str);
+ return 1;
+}
+
+/** Setzt eine Idee an Magier ab (Spielerkommando).
+ * Liest die Idee ein und speichert sie.
+ * \param[in] str Spielereingabe der Idee
+ * \return 1 bei Erfolg, 0 sonst.
+ * @see idea(string)
+ */
+static int idea2(string str) {
+ if (!str || str == "") {
+ write("Idee abgebrochen...\n");
+ return 1;
+ }
+ write("Vielen Dank fuer die Hilfe.\n");
+ smart_log("IDEA",str);
+ return 1;
+}
+
+/** Setzt ein fehlendes Detail an Magier ab (Spielerkommando).
+ * Fragt nach dem Detail und liest es via idee2() ein, falls der
+ * Spieler kein Argument angeben hat.
+ * \param[in] str optionale Spielereingabe des fehlenden Details
+ * \return 1 bei Erfolg, 0 sonst.
+ * @see md2(string)
+ */
+static int md(string str) {
+ if (!(str=_unparsed_args())) {
+ write( "Fuer welches Detail fehlt denn die Beschreibung?\n" );
+ input_to("md2",INPUT_PROMPT, "]");
+ return 1;
+ }
+ write("Vielen Dank fuer die Hilfe.\n");
+ smart_log("DETAILS",str);
+ return 1;
+}
+
+/** Setzt ein fehlendes Detail an Magier ab (Spielerkommando).
+ * Liest das Detail ein und speichert es.
+ * \param[in] str Spielereingabe des fehlenden Details.
+ * \return 1 bei Erfolg, 0 sonst.
+ * @see md(string)
+ */
+static int md2(string str) {
+ if (!str || str == "") {
+ write("Details abgebrochen...\n");
+ return 1;
+ }
+ write("Vielen Dank fuer die Hilfe.\n");
+ smart_log("DETAILS",str);
+ return 1;
+}
+
+/** Loggt eine Spielermeldung an Magier.
+ * Loggt die Spielermeldung in das passende File unter /log/report/ oder im
+ * vom Magier gewuenschten File. Hierbei werden Fehler, Ideen, MDs und Typos
+ * in getrennte Files sortiert.
+ * \param[in] str Spielermeldung
+ * \param[in] myname Art der Spielermeldung (DETAILS, BUG, TYPO, MD)
+ * @see md(string), idea(string), bug(string), typo(string)
+ */
+void smart_log(string myname, string str)
+{
+ string obnam;
+ object obj;
+
+ string *tmp = explode(str, ":");
+ if (sizeof(tmp) > 1) {
+ obnam = lower_case(trim(tmp[0]));
+ obj = present(obnam, environment()) || present(obnam);
+ if (!obj) {
+ obj = environment(this_object());
+ }
+ else // nur hier Teil vor dem : wegschneiden
+ str = trim(implode(tmp[1..],":"));
+ }
+ else {
+ obj = QueryProp(P_REFERENCE_OBJECT);
+ if (!obj || !present(obj))
+ obj = environment(this_interactive());
+ }
+
+ mapping err = ([ F_PROG: "unbekannt",
+ F_LINE: 0,
+ F_MSG: str,
+ F_OBJ: obj
+ ]);
+
+ string desc="etwas unbekanntes";
+ switch(myname) {
+ case "BUGS":
+ desc="einen Fehler";
+ err[F_TYPE]=T_REPORTED_ERR;
+ break;
+ case "DETAILS":
+ desc="ein fehlendes Detail";
+ err[F_TYPE]=T_REPORTED_MD;
+ break;
+ case "IDEA":
+ desc="eine Idee";
+ err[F_TYPE]=T_REPORTED_IDEA;
+ break;
+ case "TYPO":
+ desc="einen Typo";
+ err[F_TYPE]=T_REPORTED_TYPO;
+ break;
+ }
+
+ // Eintragung in die Fehler-DB
+ string hashkey = (string)ERRORD->LogReportedError(err);
+
+ // ggf. will das Objekte mit noch irgendwas anfangen.
+ obj->SmartLog(0, myname, str, strftime("%d. %b %Y"));
+
+ tell_object(this_object(), break_string( sprintf(
+ "Du hast an %s erfolgreich %s abgesetzt.\n"
+ "Die ID der abgesetzten Meldung lautet: %s\n",
+ (obj->IsRoom() ? "diesem Raum" : obj->name(WEM,1)),desc,
+ hashkey||"N/A"),78,BS_LEAVE_MY_LFS));
+}
+
+/** Speichert den Spieler und loggt ihn aus (Spielerkommando 'ende').
+ * Der Spieler wird vollstaendig ausgeloggt, d.h. das Spielerobjekt
+ * zerstoert.
+ * \return 1 bei Erfolg, 0 sonst.
+ * @see disconnect()
+ */
+int quit()
+{
+ int arg;
+ SetProp(P_LAST_QUIT,time());
+ catch(RemoveChannels();publish);
+ if(!QueryGuest())
+ {
+ save_me(0);
+ tell_object(ME,"Speichere "+QueryProp(P_NAME)+".\n");
+ }
+
+ if (interactive(ME))
+ call_notify_player_change(0);
+
+ remove_living_name();
+ // EVT_LIB_LOGOUT wird in remove() getriggert.
+ if(catch(remove();publish)) destruct(ME);
+ return 1;
+}
+
+/** Wrapper im quit() herum, verhindert 'ende', falls Spieler kaempft.
+ * \return 0 oder Rueckgabewert von quit()
+ * @see quit()
+ */
+static int new_quit() {
+ notify_fail("Du bist in Gedanken noch bei Deinem letzten Kampf.\n"+
+ "Warte noch etwas bevor Du das Spiel verlaesst,\n"+
+ "damit Du so nicht in RL weitermachst...\n");
+ if (time()-Query(P_LAST_COMBAT_TIME)<120 && !IS_LEARNING(ME))
+ return 0;
+ return quit();
+}
+
+/** Gibt die Infos ueber den Char an den Spieler aus (Spielerkommando 'info').
+ * \param[in] arg Wenn arg=="short", wird eine Kurzuebersicht ausgegeben.
+ * \return 1
+ * @see short_score()
+ */
+static int score(string arg) {
+ string tmp, gender;
+ int i,sz,val;
+ mixed ind;
+ object *enem1, *enem2, *inv;
+
+ if (QueryProp(P_GHOST)) {
+ write("Im ewigen Leben gibt es keine Punkte.\n");
+ return 1;
+ }
+
+ int plev = LEPMASTER->QueryLevel();
+
+ switch(tmp = QueryProp(P_GENDER)) {
+ case MALE: gender = "maennlich"; break;
+ case FEMALE: gender = "weiblich"; break;
+ case NEUTER: gender = "neutral"; break;
+ default: gender = lower_case(tmp);
+ }
+
+ ind = m_indices(QueryProp(P_ATTRIBUTES));
+ tmp = "";
+ foreach(string index: ind) {
+ string aname;
+ switch (index) {
+ case "int": aname = "Intelligenz"; break;
+ case "con": aname = "Ausdauer"; break;
+ case "dex": aname = "Geschicklichkeit"; break;
+ case "str": aname = "Kraft"; break;
+ default:
+ if(stringp(index)) aname = capitalize(index);
+ else aname = "Unbekannt";
+ }
+ aname = sprintf("%-18'.'s %2.2d", aname+" ", QueryRealAttribute(index));
+ if((val = QueryAttributeOffset(index)))
+ aname += sprintf(" (%s%d)", (val>=0?"+":""), val);
+ tmp += aname + "\n";
+ }
+
+ printf("- %-'-'68s\n",
+ TeamPrefix()+capitalize(implode(explode(short()||"","\n"),""))+" ");
+ if(arg!="short") {
+ printf("Rasse ............ %-' '18s Abenteuer ........ %d %s\n",
+ QueryProp(P_RACE), QueryProp(P_QP),
+ (val = QM->QueryTotalQP()) == QueryProp(P_QP) ? "" : "("+val+")");
+ printf("Geschlecht ....... %-' '18s Groesse .......... %d cm\n",
+ gender, QueryProp(P_SIZE));
+ printf("Stufe ............ %-3.3d %-' '14s Gewicht .......... %d kg\n",
+ QueryProp(P_LEVEL), (QueryProp(P_LEVEL) < plev ? "("+plev+")" : ""),
+ QueryProp(P_WEIGHT) / 1000);
+ printf("Gilde ............ %-' '18s Gildenstufe ...... %d\n",
+ capitalize(QueryProp(P_GUILD)), QueryProp(P_GUILD_LEVEL));
+ }
+ printf("Erfahrung ........ %-' '18s Charakter ........ %-s\n\n",
+ QueryProp(P_XP)+ " Punkte", al_to_title(QueryProp(P_ALIGN)));
+ printf("%#-76.2s\n\n", tmp);
+ printf("Gesundheit ....... %-3.3d %-' '14s Gift ............. %s\n",
+ QueryProp(P_HP),
+ (QueryProp(P_HP) == (val = QueryProp(P_MAX_HP)) ? "" : "("+val+")"),
+ ((val = QueryProp(P_POISON)) ?
+ (val < 4 ? "leicht" : "gefaehrlich") : "gesund"));
+ printf("Konzentration .... %-3.3d %-' '14s Vorsicht ......... %s\n",
+ QueryProp(P_SP),
+ (QueryProp(P_SP) == (val = QueryProp(P_MAX_SP)) ? "" : "("+val+")"),
+ ((ind = QueryProp(P_WIMPY)) ? ""+ind : "mutig"));
+ printf("Todesfolgen....... %-' '18s %s\n",
+ ((val = death_suffering()) ? ""+((val+9)/10) : "kein Malus"),
+ (QueryProp(P_WIMPY) && ind=QueryProp(P_WIMPY_DIRECTION))
+ ? sprintf("Fluchtrichtung ... %O", ind) : "");
+ printf("%s",
+ (time()-Query(P_LAST_COMBAT_TIME)<120 && !IS_LEARNING(ME)) ?
+ "Spiel verlassen .. nicht moeglich\n" : ""
+ );
+
+ if(arg!="short") {
+ write(break_string(Forschung(), 70));
+ if(ind=QueryProp(P_AWAY))
+ printf("Du bist nicht ansprechbar: %O\n",ind);
+ }
+
+ if(sizeof(enem1=((mixed)QueryEnemies())[0])) {
+ enem2=({});
+ inv=all_inventory(environment(ME));
+ foreach(object en: enem1) {
+ if (member(inv,en)==-1) // Ist unser Feind und ist nicht hier
+ enem2+=({en});
+ }
+ if(sizeof(enem2))
+ {
+ write(break_string(
+ "Du verfolgst " + CountUp(map_objects(enem2, "name", WEN))+".",
+ 78));
+ }
+ }
+ if(arg!="short") show_age();
+ printf("%-'-'70s\n", "");
+ return 1;
+}
+
+/** Gibt eine kuerzere Info ueber den Char aus (Spielerkommando punkte|score).
+ Ruft score("short").
+ * \param[in] arg UNUSED
+ * \return 1 bei Erfolg, 0 sonst.
+ * @see score(string), very_short_score()
+ */
+static int short_score(string arg) {
+ return score("short");
+}
+
+/** Gibt eine Miniinfo ueber LP / KP aus (Spielerkommando: kurzinfo)
+ * \return 1
+ * @see score(string), short_score(string)
+ */
+static int very_short_score(string arg) {
+ int lp,mlp,xlp,kp,mkp,xkp;
+ string bar;
+
+ lp=QueryProp(P_HP); mlp=QueryProp(P_MAX_HP);
+ kp=QueryProp(P_SP); mkp=QueryProp(P_MAX_SP);
+ if (mlp)
+ xlp=(lp*40/mlp);
+ if (mkp)
+ xkp=(kp*40/mkp);
+ bar=" . . . . . . . . ";
+ if (QueryProp(P_NO_ASCII_ART) || arg == "-k")
+ printf("Gesundheit: %3.3d (%3.3d), Konzentration: %3.3d (%3.3d)\n",
+ lp, mlp, kp, mkp);
+ else
+ printf("Gesundheit: 0 |%'#'40.40s| %3.3d%s\n"+
+ "Konzentration: 0 |%'#'40.40s| %3.3d%s\n",
+ (xlp<0?bar:bar[xlp..]),lp,(lp==mlp?"":sprintf(" (%d)",mlp)),
+ (xkp<0?bar:bar[xkp..]),kp,(kp==mkp?"":sprintf(" (%d)",mkp))
+ );
+ return 1;
+}
+
+/** Gibt eine Manpage/Hilfeseite an den Spieler aus.
+ Beruecksichtigt hierbei die Synonymliste aus dir/.synonym, um die richtige
+ Manpage auszugeben.
+ * \param[in] dir Verzeichnis der gewuenschten Manpage
+ * \param[in] page Name der gewuenschten Manpage
+ * \return String der gewuenschten Manpage
+ */
+static string getmanpage(string dir, string page)
+{
+ string text, *syn;
+ int i;
+
+ if (dir[<1] != '/')
+ dir += "/";
+
+ if ((text=read_file(dir+page)) && sizeof(text))
+ return text;
+
+ if (text = read_file(dir+".synonym")) {
+ syn = regexplode(text, "([ \t][ \t]*|\n)");
+ if ((i=member(syn, page))!=-1)
+ return read_file(dir+syn[i+2]);
+ }
+ return 0;
+}
+
+/** Gibt eine Hilfeseite an den Spieler aus (Spielerkommando hilfe|man).
+ * Die Hilfeseite wird in div. Verzeichnissen gesucht (je nach Gilde,
+ * Magier-/Spielerstatus).
+ * \param[in] str Name der gewuenschten Hilfeseite.
+ * \return 1
+ */
+static int help(string str) {
+ string verb, rest, text, gilde;
+ mixed found;
+
+ found=0;
+ text = "";
+ if (str) {
+ str = implode( explode(str, ".." ), "");
+
+ if ( sscanf( str, "gilde %s %s", gilde, rest)==2)
+ str=rest;
+ else
+ gilde=QueryProp(P_GUILD);
+ if (!gilde) gilde="abenteurer";
+
+ if ( sscanf( str, "%s %s",verb,rest )==2 ) str = verb;
+
+ if ((IS_LEARNER(PL)) ) {
+ if (rest = getmanpage("/doc/wiz/",str)) {
+ found = 1;
+ text += rest;
+ }
+ else if (rest = getmanpage("/doc/mcmd/", str)) {
+ found = 1;
+ text += rest;
+ }
+ }
+
+ if ((IS_SEER(PL)) /*&& !found*/ ) {
+ if (rest = getmanpage("/doc/scmd/",str)) {
+ if (found)
+ text += "\n--------------------\n";
+ found = 1;
+ text += rest;
+ }
+ }
+
+ if (rest = getmanpage("/doc/g."+gilde+"/",str)) {
+ if (found)
+ text += "\n--------------------\n";
+ found = 1;
+ text += rest;
+ } else {
+ if (rest = getmanpage("/doc/help/",str)) {
+ if (found)
+ text += "\n--------------------\n";
+ found = 1;
+ text += rest;
+ }
+ else if (rest = getmanpage("/doc/pcmd/",str)) {
+ if (found)
+ text += "\n--------------------\n";
+ found = 1;
+ text += rest;
+ }
+ else if (rest = getmanpage("/doc/REGELN/",str)) {
+ if (found)
+ text += "\n--------------------\n";
+ found = 1;
+ text += rest;
+ }
+ }
+
+ if (!found)
+ text = "Dazu ist keine Hilfe verfuegbar.\n";
+
+ More(text,0);
+ return 1;
+ }
+ if (IS_LEARNER(PL))
+ text = read_file("/doc/hilfe.magier");
+ else if (IS_SEER(PL))
+ text = read_file("/doc/hilfe.seher");
+
+ More(text + read_file("/doc/hilfe.spieler"), 0);
+ return 1;
+}
+
+/** Ermittelt angebene Optionen fuer das Spielerkommando 'wer'.
+ * \param[in] str vom Spieler spezifizierter String von Filteroptionen: -k,
+ * -v, -a, -s (+ Langformen).
+ * \return Array von Spieleroptionen als veroderte Int-Flags und Rest der
+ * Spielereingabe ohne die Optionen.
+ */
+static mixed filter_who_options(string str)
+{
+ string* opt, *ans;
+ int i,len,res;
+
+ opt = explode(str," "); len=sizeof(opt);
+ ans = ({});
+ res = 0;
+ for(i=0;i<len;i++)
+ switch(opt[i]){
+ case "-k":
+ case "-kurz":
+ res |= WHO_SHORT; break;
+ case "-v":
+ case "-vertikal":
+ res |= WHO_VERTICAL; break;
+ case "-alphabetisch":
+ case "-a":
+ case "-alpha":
+ res |= WHO_ALPHA; break;
+ case "-s":
+ case "-spieler":
+ res |= WHO_PLAYER_VIEW; break;
+ default:
+ return ({ res, implode(opt[i..]," ") });
+ }
+ return ({ res, 0 });
+
+}
+
+/** Spielerkommando 'wer', fragt /obj/werliste ab.
+ * \param[in] str Spielereingabe mit Optionen fuer wer.
+ * \return 1
+ */
+static int who(string str) {
+ int i,shrt;
+ string ret;
+ mixed ans;
+
+ if ((str=_unparsed_args())&&str[0..0]!="-") {
+ ans = filter_who_options(str);
+ shrt = ans[0];
+ str = ans[1];
+ if (!shrt) {
+ if (ret=INETD->_send_udp(str,
+ ([ REQUEST: "who", SENDER: getuid(ME) ]), 1 ))
+ write(ret);
+ else
+ write("Anfrage abgeschickt.\n");
+ return 1;
+ }
+ }
+ if (str) i=(member(str,'o')>0); else i=0;
+ if (sizeof(str)>1 && str[0] == '-') str = str[1..1];
+ More(implode( "/obj/werliste"->QueryWhoListe(
+ IS_LEARNER(ME) && QueryProp(P_WANTS_TO_LEARN),shrt,0,str,i),"\n"),0);
+ return 1;
+}
+
+/** Spielerkommando 'kwer', fragt /obj/werliste ab.
+ * \param[in] str Spielereingabe mit Optionen fuer wer.
+ * \return 1
+ */
+static int kwho(string str)
+{
+ int shrt;
+ mixed res;
+
+ if(str) {
+ res = filter_who_options(str);
+ shrt = res[0];
+ str = res[1];
+ }
+ More(implode( "/obj/werliste"->QueryWhoListe(
+ IS_LEARNER(ME) && QueryProp(P_WANTS_TO_LEARN), shrt|WHO_SHORT ,0,str),
+ "\n")+"\n\n",0);
+ return 1;
+}
+
+/** Spielerkommando 'kkwer', gibt eine einfache Liste der Anwesenden aus.
+ Filtert unsichtbare Spieler aus, falls SPielerobjekt kein Magier ist.
+ * \param[in] str Spielereingabe mit Optionen fuer wer.
+ * \return 1
+ */
+static varargs int kkwho(string str) {
+ object *obs;
+ string *namen;
+
+ obs=filter_users(str);
+ namen=({});
+ if (IS_LEARNER(this_player())) {
+ foreach(object ob: obs) {
+ if (environment(ob))
+ namen+=({capitalize(geteuid(ob))});
+ }
+ }
+ else {
+ foreach(object ob: obs) {
+ if (!ob->QueryProp(P_INVIS) && environment(ob))
+ namen+=({capitalize(geteuid(ob))});
+ }
+ }
+ if (sizeof(namen))
+ write(break_string(CountUp(sort_array(namen,#'>),", ", ", ")+".",75));
+ else
+ write("Keine passenden Spieler gefunden.\n");
+
+ return 1;
+}
+
+/** Spielerkommando 'toete'.
+ * Prueft auf Geist, Gast, toten HC-Spieler, Waffe/freie Hand. Versucht einen
+ * Gegner zu finden und ruft dann Kill(string).
+ * \param[in] str Spielereingabe
+ * \return 1 oder 0, falls kein potentieller Gegner bei 'toete alle' gefunden
+ * wird.
+ */
+static int kill(string str) {
+ object eob,wob;
+
+ if (QueryProp(P_GHOST))
+ {
+ write("Das kannst Du in Deinem immateriellen Zustand nicht.\n");
+ return 1;
+ }
+
+ if(hc_play>1)
+ {
+ write("DAS HAST DU HINTER DIR.\n");
+ return 1;
+ }
+
+ if (QueryGuest())
+ {
+ write("Du bist doch nur Gast hier.\n");
+ return 1;
+ }
+ if (!str || str == "") {
+ write("WEN willst Du toeten?\n");
+ return 1;
+ }
+ if( !QueryProp(P_WEAPON) && QueryProp(P_FREE_HANDS)==0 ) {
+ write(
+ "Dazu solltest Du eine Waffe gezueckt oder eine Hand frei haben.\n");
+ return 1;
+ }
+ str=lower_case(str);
+ if (str=="alle") {
+ object livs;
+ livs=filter(all_inventory(environment(PL)),
+ function int (object ob) {
+ if (living(ob) && !query_once_interactive(ob)
+ && !ob->QueryProp(P_INVIS)
+ && !ob->QueryProp(P_NO_GLOBAL_ATTACK)
+ && !ob->QueryProp(P_FRIEND))
+ {
+ Kill(ob);
+ return 1;
+ }
+ return 0;
+ } );
+ // wenn Gegner gefunden, raus, ansonsten kommt die Fehlermeldung unten.
+ if (sizeof(livs)) return 1;
+ }
+ else {
+ int i=1;
+ while(objectp(eob = present(str,i++,environment(PL)))) {
+ if (living(eob) && !eob->QueryProp(P_INVIS))
+ break;
+ else
+ eob=0;
+ }
+ }
+ if (!objectp(eob)) {
+ // per write und return 1 ist hier mal ok, weil dies Kommando im Spieler
+ // eh zuletzt in der Kommandokette ausgefuehrt wird und per return 0 eh
+ // kein anderes mehr zum Zug kommt.
+ write("Du siehst hier kein derartiges Wesen!\n");
+ return 1;
+ }
+ else if (eob == PL) {
+ write("Selbstmord ist keine Loesung!\n");
+ return 1;
+ }
+
+ /* Kill him */
+ Kill(eob);
+ return 1;
+}
+
+/** Spielerkommando 'stop'.
+ * Loescht die Gegnerliste, sofern man nicht InFight() ist.
+ * \param[in] str Spielereingabe, wird ignoriert.
+ * \return 1
+ */
+static int stop( string str )
+{
+ if ( InFight() ){
+ write( "Das geht nicht mitten im Kampf.\n" );
+ return 1;
+ }
+
+ if ( !str ){
+ StopHuntingMode();
+ write( "Ok.\n" );
+ return 1;
+ }
+
+ if ( !StopHuntID(str) )
+ write( "So jemanden verfolgst Du nicht!\n" );
+
+ return 1;
+}
+
+/** Spielerkommando fuers emoten ':'.
+ * \param[in] str Spielereingabe
+ * \param[in] genitiv Genetivflag
+ * \return 1 oder 0, falls Spieler nicht emoten kann (kein CAN_EMOTE Flag in
+ * P_CAN_FLAGS).
+ */
+int emote(string str,int genitiv)
+{
+ string *commands,message,verb;
+ object living;
+ int i,size;
+
+ if (!(Query(P_CAN_FLAGS)&CAN_EMOTE)) return 0;
+ if (query_verb()[0]==';') genitiv=1;
+ if (query_verb()[0]==':'||query_verb()[0]==';')
+ verb=query_verb()[1..]+" ";
+ else
+ verb="";
+ str=this_player()->_unparsed_args();
+ commands=explode(verb+(str||""),"#");
+ message=break_string((IS_SEER(ME) ? "" : ">")
+ +capitalize(genitiv ? name(WESSEN) :
+ name())
+ +" "+commands[0],78);
+ size=sizeof(commands);
+ if(size>=3)
+ {
+ living=find_living(lower_case(commands[1]));
+ if(!living || environment(living)!=environment() ||
+ (living->QueryProp(P_INVIS)) && !IS_LEARNER(ME))
+ {
+ write(capitalize(commands[1])+" sehe ich hier nicht!\n");
+ return 1;
+ }
+ if(living!=this_object())
+ tell_object(living,break_string((IS_SEER(this_player()) ? "" : ">")
+ +capitalize(genitiv ?
+ this_player()->name(WESSEN) :
+ this_player()->name())
+ +" "+commands[2],78));
+ }
+ if(size>=4)
+ write(break_string(commands[3],78));
+ else
+ write(message);
+ tell_room(environment(),message,({this_object(),living}));
+ return 1;
+}
+
+/** Spielerkommando fuers remoten 'r:'.
+ * \param[in] str Spielereingabe
+ * \param[in] flag Genetivflag
+ * \return 1 oder 0, falls Spieler nicht emoten kann (kein CAN_REMOTE Flag in
+ * P_CAN_FLAGS).
+ */
+static int remote(string str, int flag)
+{
+ int m;
+ string tmp, dest;
+ string *exstr;
+ object destpl;
+
+ if ( !(Query(P_CAN_FLAGS) & CAN_REMOTE) )
+ return 0;
+
+ if ( !(str=_unparsed_args()) ||
+ sizeof( (exstr=explode(str," ")) - ({""}) ) <= 1 ){
+ write("Was willst Du zu wem `emoten`?\n");
+ return 1;
+ }
+
+ dest = lower_case(exstr[0]);
+
+ if( !(destpl=find_player( dest ) ) ||
+ (destpl->QueryProp(P_INVIS) && !IS_LEARNER(ME)) ){
+ write("Einen solchen Spieler gibt es derzeit nicht.\n");
+ return 1;
+ }
+
+ tmp = implode( exstr[1..], " " );
+
+ tmp = regreplace( tmp, "(^|[^#])#($|[^#])", "\\1aus der Ferne\\2", 1 );
+ tmp = regreplace( tmp, "(^|[^\\^])\\^($|[^\\^])", "\\1in der Ferne\\2", 1 );
+ tmp = regreplace( tmp, "##", "#", 1 );
+ tmp = regreplace( tmp, "\\^\\^", "^", 1 );
+
+ if ( strstr( tmp, "aus der Ferne" ) == -1
+ && strstr( tmp, "in der Ferne" ) == -1 )
+ tmp += " aus der Ferne";
+
+ if ( QueryProp(P_INVIS) && IS_LEARNER(destpl) ){
+ str = "(" + capitalize(getuid(ME));
+ if ( flag )
+ str += member( "sxz", str[<1] ) == -1 ? "s" : "'";
+ str += ")";
+ }
+ else
+ str = (flag ? capitalize(name(WESSEN)) : capitalize(name(WER)));
+
+ str += " " + tmp + (member( ".?!", tmp[<1] ) == -1 ? "." : "") + "\n";
+
+ m = destpl->ReceiveMsg(str, MT_COMM|MT_FAR, MA_EMOTE,0, ME);
+ switch(m)
+ {
+ case MSG_DELIVERED:
+ _recv(destpl, capitalize(destpl->name()) + "->" + str, MSGFLAG_REMOTE);
+ break;
+ case MSG_BUFFERED:
+ write( capitalize(destpl->name(WER) + " ist gerade beschaeftigt.\n") );
+ break;
+ case MSG_IGNORED:
+ write( capitalize(destpl->name(WER) + " ignoriert Dich.\n") );
+ break;
+ case MSG_VERB_IGN:
+ case MSG_MUD_IGN:
+ write( capitalize(destpl->name(WER) + " ignoriert Deine Meldung.\n") );
+ break;
+ default:
+ write( capitalize(destpl->name(WER) + " kann Dich gerade nicht "
+ "wahrnehmen.\n") );
+ }
+ return 1;
+}
+
+/** Spielerkommando fuers emoten im Genitiv ';'.
+ * Ruft emote(string,int) auf.
+ */
+static int gemote(string str)
+{
+ return emote(str, 1);
+}
+
+/** Spielerkommando fuers remoten im Genitiv 'r;'.
+ * Ruft remote(string, int) auf.
+ */
+static int gremote(string str)
+{
+ return remote(str, 1);
+}
+
+static void load_auto_objects(mapping map_ldfied);
+
+private void InitPlayer();
+private void InitPlayer2();
+private void InitPlayer3();
+
+/** Gibt eine Zufallszahl um P_AVERAGE_SIZE herum zurueck.
+ * \return Zufaellige Groesse.
+ */
+private int RandomSize()
+{
+ return (100+random(13)-random(13)+random(13)-random(13))*
+ (QueryProp(P_AVERAGE_SIZE)||170)/100;
+}
+
+/** Setzt bestimmte Props im Spieler, falls diese nicht gesetzt sind oder
+ * loescht obsolete Props. Repariert bestimmte Datenstrukturen im Spieler.
+ * Wird von start_player() nach Laden des Savefiles gerufen.
+ * Momentan wird z.B. die Groesse gesetzt, falls sie bisher 0 ist und die
+ * Skills des Spielers initialisiert bzw. repariert oder auf die aktuellste
+ * Version des Skillsystems
+ * Ruft ggf. InitSkills() und FixSkills().
+ * @param[in] newflag Gibt an, ob es ein neuerstellter Spieler ist.
+ * @sa start_player(), InitSkills(), FixSkills()
+ */
+private void updates_after_restore(int newflag) {
+
+ // Seher duerfen die Fluchtrichtung uebermitteln lassen.
+ // Eigentlich koennte es Merlin machen. Dummerweise gibt es ja auch alte
+ // Seher und dann kann es gleiche fuer alle hier gemacht werden. (Ob der
+ // Code jemals rauskann?)
+ //TODO: Irgendwann alle Seher korrigieren und Code nach Merlin schieben...
+ if (IS_SEER(ME))
+ SetProp(P_CAN_FLAGS,QueryProp(P_CAN_FLAGS) | CAN_REPORT_WIMPY_DIR);
+
+ // ggf. Invis-Eigenschaft aus dem Loginobjekt abrufen (Invislogin), koennte
+ // ja anders als aus Savefile sein. Gesetztes P_INVIS aus diesem aber
+ // beibehalten.
+ if (IS_LEARNER(ME) && !QueryProp(P_INVIS)
+// && load_name(previous_object()) == "/secure/login"
+ )
+ {
+ SetProp(P_INVIS, previous_object()->query_invis());
+ if (QueryProp(P_INVIS))
+ tell_object(ME, "DU BIST UNSICHTBAR!\n" );
+ }
+ "*"::updates_after_restore(newflag);
+
+ attributes::UpdateAttributes();
+
+ int size=Query(P_SIZE);
+ if (!size) size=RandomSize();
+ while(size==QueryProp(P_AVERAGE_SIZE))
+ size=RandomSize();
+ Set(P_SIZE,size);
+
+ // Prop wird nicht mehr genutzt. TODO: irgendwann entfernen.
+ Set(P_SECOND_LIST, SAVE, F_MODE_AD);
+ Set(P_SECOND_LIST, 0, F_VALUE);
+}
+
+
+/** Setzt den HC-Modus.
+ */
+varargs nomask void set_hc_play(string str,int val)
+{
+ string str1;
+
+ str1 = explode( object_name(previous_object()), "#" )[0];
+
+ if ( str1 != "/secure/login" &&
+ previous_object()!=this_object() &&
+ extern_call() &&
+ (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
+ capitalize(geteuid(ME)) != str ||
+ geteuid(ME) != geteuid(previous_object())) ){
+ write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
+ return;
+ }
+
+ hc_play=val;
+}
+
+/** gibt den HC-Modus zurueck.
+ */
+nomask int query_hc_play()
+{
+ return hc_play;
+}
+
+/** Initialisiert und aktiviert das Spielerobjekt.
+ * Kann nur von /secure/login oder /secure/master gerufen werden.
+ * Startet Telnet Negotiation, laedt Savefile, setzt einige Props.
+ * Ruft updates_after_restore(), um div. Daten zu aktualisieren/reparieren.
+ * Ruft create() aus /std/player/potion.c.
+ * Bei neuem Spieler wird der entsprechende Event ausgeloest die Attribute
+ * auf die Startwerte gesetzt.
+ * Prueft Zweitiemarkierungen.
+ * Ruft InitPlayer().
+ * @param[in] str Name des Charakters, der geladen werden soll.
+ * @param[in] ip textuelle Repraesentation der IP-Adresse des Spielers.
+ * @return 1 bei Erfolg, 0 sonst.
+ * @todo Div. Reparaturen/Updates nach updates_after_restore() auslagern.
+ */
+varargs nomask int start_player( string str, string ip )
+{
+ mixed second;
+ int newflag; /* could player be restored? */
+ string str1;
+
+ call_out( "disconnect", 600 );
+
+ str1 = explode( object_name(previous_object()), "#" )[0];
+
+ if ( str1 != "/secure/login" &&
+ str1 != "/secure/master" &&
+ (!geteuid(ME) || geteuid(ME) != getuid(ME) ||
+ capitalize(geteuid(ME)) != str ||
+ geteuid(ME) != geteuid(previous_object())) ){
+ write( "DIESER VERSUCH WAR ILLEGAL !!\n" );
+ destruct(ME);
+ return 0;
+ }
+
+ /* try to restore player. If it doesn't exist, set the new flag */
+ newflag = !restore_object( "/" + SAVEPATH + lower_case(str)[0..0] + "/"
+ +lower_case(str) );
+
+ updates_after_restore(newflag);
+
+ if ( query_once_interactive(ME) )
+ {
+ // Telnet-Negotiations durchfuehren, aber nur die grundlegenden aus
+ // telnetneg. Alle anderen sollten erst spaeter, nach vollstaendiger
+ // Initialisierung gemacht werden.
+ telnetneg::startup_telnet_negs();
+ modify_prompt();
+ Set( P_LAST_LOGIN, time() );
+ }
+
+ Set( P_WANTS_TO_LEARN, 1 ); // 1 sollte der default sein !!!
+ Set( P_WANTS_TO_LEARN, PROTECTED, F_MODE_AS );
+ // Eingefuegt 18.11.99, kann nach einem Jahr wieder raus:
+ Set( P_TESTPLAYER, PROTECTED, F_MODE_AS );
+
+ if ( IS_LEARNER(ME) )
+ SetProp( P_CAN_FLAGS, QueryProp(P_CAN_FLAGS)|CAN_REMOTE );
+
+ Set( P_NAME, str );
+ Set( P_NAME, SECURED, F_MODE_AS );
+
+ if ( !QueryProp(P_NEEDED_QP) )
+ SetProp( P_NEEDED_QP, REQ_QP );
+
+ Set( P_NEEDED_QP, NOSETMETHOD, F_SET_METHOD );
+ Set( P_NEEDED_QP, SAVE|SECURED, F_MODE_AS );
+
+ /* autosave the player after 500 heartbeats */
+ time_to_save = age + 500;
+ potion::create(); /* DO IT HERE AFTER THE RESTORE !! */
+
+ AddId( getuid() );
+ SetProp( P_AC, 0 );
+ SetProp( P_WEAPON, 0 );
+
+ /* Set some things which wont be set when all is OK */
+ SetProp( P_MAX_HP, (QueryAttribute(A_CON) * 8 + 42 ));
+ SetProp( P_MAX_SP, (QueryAttribute(A_INT) * 8 + 42 ));
+
+ catch( bb = "/secure/bbmaster"->query_bb() );
+
+ /* If this is a new character, we call the adventurers guild to get
+ * our first title !
+ */
+ if ( newflag ) {
+ if ( QueryGuest())
+ SetProp( P_TITLE, "ueberkommt das "MUDNAME" ..." );
+
+ Set( P_LEVEL, -1 );
+ SetProp( P_ATTRIBUTES, ([ A_STR:1, A_CON:1, A_INT:1, A_DEX:1 ]) );
+ SetProp( P_HP, QueryProp(P_MAX_HP) );
+
+ // Event ausloesen
+ EVENTD->TriggerEvent(EVT_LIB_PLAYER_CREATION, ([
+ E_OBJECT: ME,
+ E_PLNAME: getuid(ME) ]) );
+ }
+
+ InitPlayer();
+
+ // Padreic 01.02.1999
+ if ( !IS_LEARNER(ME) && second = QueryProp(P_SECOND) ) {
+ if ( stringp(second) && lower_case(second)[0..3] == "von " ) {
+ second = lower_case(second[4..]);
+ SetProp( P_SECOND, second );
+ }
+
+ if ( !stringp(second ) ||
+ file_size( "/save/" + second[0..0] + "/" + second + ".o" ) <= 0 ){
+ if ( stringp(second) &&
+ file_size( "/save/" + lower_case(second[0..0]) + "/" +
+ lower_case(second) + ".o" ) >0 ){
+ SetProp( P_SECOND, lower_case(second) );
+ log_file( "WRONG_SECOND",
+ sprintf( "%s: %s: P_SECOND = %O -> Automatisch "
+ "korrigiert,\n",
+ dtime(time()), object_name(), second ) );
+ }
+ else {
+ tell_object( ME,
+ "*\n*\n* Deine Zweitiemarkierung ist ungueltig, "
+ "bitte aendere diese und sprich im\n* Zweifel "
+ "bitte einen Erzmagier an.\n*\n*\n" );
+
+ log_file( "WRONG_SECOND",
+ sprintf( "%s: %s: P_SECOND = %O\n",
+ dtime(time()), object_name(), second ) );
+ // ein bisschen deutlicher auffordern.. Padreic 08.04.1999
+ move( "/d/gebirge/room/zwafflad", M_GO );
+ }
+ }
+ }
+ return(0);
+}
+
+/** Letzte Phase der Spielerinitialisierung beim Laden des Charakters.
+ * Ruft enable_commands(), aktiviert den Heartbeat und aktiviert die
+ * Kommandos aus den geerbten command.c, put_and_get.c, team.c, soul.c,
+ * guide.c, setzt den Living Name.
+ * Registriert UseSpell() als Catchall-Kommando.
+ * Laesst den Spieler ggf. einen Level per /std/gilde aufsteigen, ruft
+ * call_notify_player_change(), loest Login-Event aus.
+ * Gibt Willkommenstexte, News und neue Mails aus.
+ * Findet den Startraum und bewegt den Spieler dorthin.
+ * Ruft FinalSetup() (aus den Rassenshells).
+ * Begrenzt Geldmenge im Spieler (wegen Bankzweities) nach Tragkraft und
+ * erstattet Geld bei Reboot.
+ * Ruft set_is_wizard().
+ * Startet Clonen der Autoloader.
+ * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
+ */
+private void InitPlayer4()
+{
+ int num, str, ski;
+ string err, called_from_ip;
+ mixed start_place;
+ object mon;
+
+ enable_commands();
+ set_heart_beat(1);
+ command::initialize();
+ add_put_and_get_commands();
+ add_team_commands();
+ add_soul_commands();
+ add_guide_commands();
+ add_action( "UseSpell", "", 1 );
+ set_living_name( getuid() );
+ while ( remove_call_out("disconnect") != -1 )
+ ;
+
+ if ( QueryProp(P_LEVEL) == -1 )
+ {
+ catch( "/std/gilde"->try_player_advance(this_object()) ;publish );
+ }
+
+ if ( interactive(ME) )
+ call_notify_player_change(1);
+
+ if ( interactive(this_object()) ) {
+ cat( "/etc/NEWS" );
+
+ NewbieIntroMsg();
+
+ if ( QueryProp(P_INVIS) && !IS_WIZARD(ME) )
+ SetProp( P_INVIS, 0 );
+
+ catch( num = "secure/mailer"->FingerMail(getuid());publish );
+
+ if ( num )
+ write( "Du hast " + num + " neue" +
+ (num == 1 ? "n Brief" : " Briefe")+" im Postamt liegen.\n" );
+
+ catch( RegisterChannels();publish );
+
+ if ( (called_from_ip = Query(P_CALLED_FROM_IP)) &&
+ query_ip_number(ME) != called_from_ip ){
+ string tmp;
+
+ if ( stringp(tmp = query_ip_name(called_from_ip)) &&
+ tmp != called_from_ip )
+ tmp = " [" + tmp + "]";
+ else
+ tmp = "";
+
+ write( "Das letzte Mal kamst Du von " + called_from_ip
+ + tmp + ".\n" );
+ }
+
+ Set( P_CALLED_FROM_IP, query_ip_number(ME) );
+ }
+
+ if ( !stringp(default_home) || default_home == "" )
+ default_home = "/gilden/abenteurer";
+
+ if ( IS_SEER(ME) && !IS_LEARNER(ME) )
+ catch( start_place = HAUSVERWALTER->FindeHaus(getuid(ME));publish );
+ // wenn der Spieler noch ganz frisch ist und noch wenig Stufenpunkte
+ // gekriegt hat und das Tutorial noch nicht gemacht hat, startet er im
+ // Tutorial.
+ else if (QueryProp(P_LEP) <= 120
+ && QM->HasMiniQuest(this_object(),
+ "/d/anfaenger/ennox/tutorial/npcs/verkaeufer") != 1)
+ start_place = "/room/welcome/"+ getuid(this_object());
+ else
+ start_place = 0;
+
+ if ( !start_place )
+ start_place = QueryProp(P_START_HOME);
+
+ if( objectp(start_place) || (stringp(start_place) && start_place != "" ) ){
+ if ((err = catch(move( start_place, M_GO|M_SILENT|M_NO_SHOW );publish))
+ || !environment() )
+ err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
+ }
+ else
+ err = catch(move( default_home, M_GO|M_SILENT|M_NO_SHOW );publish);
+
+ if ( err )
+ catch(move( "/gilden/abenteurer", M_GO|M_SILENT|M_NO_SHOW );publish);
+
+ // Die Shell muss FinalSetup() nicht implementieren. Daher Callother
+ catch( ME->FinalSetup();publish );
+
+ // Login-event ausloesen
+ EVENTD->TriggerEvent(EVT_LIB_LOGIN, ([
+ E_OBJECT: ME,
+ E_PLNAME: getuid(ME),
+ E_ENVIRONMENT: environment() ]) );
+
+ // erst jetzt GMCP freigeben und zu verhandeln.
+ gmcp::startup_telnet_negs();
+
+ // Schonmal sichern, falls ein Bug (Evalcost...) dazwischen kommen sollte.
+ autoload_rest = autoload;
+
+ // Um Geld-Xties mit illegal viel Geld zu vermeiden, kommt ein Check:
+ if ( !IS_LEARNER(ME) && !Query(P_TESTPLAYER) &&
+ mon = present( "\ngeld", ME ) ){
+ // maximale Kraft, die der Spieler haette haben koennen
+ str = QueryAttribute(A_STR) + 4;
+ if ( Query(P_FROG) )
+ str += 30;
+ if ( str < 1 )
+ str = QueryRealAttribute(A_STR) + 4;
+ if ( str > 30 )
+ str = 30;
+
+ // Trageskill beachten
+ ski = to_int(UseSkill( SK_CARRY, ([SI_SKILLARG : str ]) ));
+ if ( !intp(ski) )
+ ski = 0;
+
+ // Wieviel konnte der Spieler insgesamt maximal tragen?
+ num = 9200 + str * 800 + ski;
+ if ( num < 3000 )
+ num = 3000;
+
+ // Verdoppeln fuer einen guten Container und nochmal 20% draufschlagen
+ // zur Sicherheit. Das Ganze danach *4, um die maximale Anzahl Muenzen
+ // zu erhalten.
+ num = (int) (num * 8.8);
+
+ // Geld, das zuviel ist, auf den Maximalwert kuerzen.
+ // Zur Sicherheit wird mitgeloggt. Falls ein Spieler sich (zu recht)
+ // beschwert, kann man immer noch wieder die Summe korrigieren ;-)
+ if ( (str = mon->QueryProp(P_AMOUNT)) > num ){
+ mon->SetProp( P_AMOUNT, num );
+ log_file( "ZUVIEL_GELD", sprintf( "%s: %s hatte %d Muenzen bei "
+ "sich. Korrigiert auf %d.\n ",
+ ctime(time())[4..],
+ capitalize(getuid(ME)),
+ str, num ) );
+ }
+ }
+ int entschaedigung = QueryProp(P_CARRIED_VALUE);
+ if ( entschaedigung > 0 )
+ {
+ write( "Du findest " + entschaedigung +
+ " Muenzen, die Du beim letzten Mal verloren hast.\n" );
+
+ if ( MayAddWeight( entschaedigung / 4 ) ){
+ write( "Weil Du nicht mehr soviel tragen kannst, spendest Du den "+
+ "Rest der Zentralbank.\n" );
+
+ num = (QueryProp(P_MAX_WEIGHT) - query_weight_contents()) * 4;
+ entschaedigung = (num < 0) ? 0 : num;
+ }
+
+ AddMoney( entschaedigung );
+ SetProp(P_CARRIED_VALUE,0);
+ }
+
+ if ( !QueryProp(P_INVIS) )
+ say( capitalize(name(WER)) + " betritt diese Welt.\n" );
+ else
+ write( "DU BIST UNSICHTBAR!\n\n" );
+#if __EFUN_DEFINED__(set_is_wizard)
+ if ( IS_WIZARD(getuid(ME)) )
+ set_is_wizard( ME, 1 );
+ else
+ set_is_wizard( ME, 0 );
+#endif
+ if ( query_once_interactive(ME) )
+ ListAwaited();
+
+ // Autoloader werden ganz zum Schluss geclont, da das bis zum bitteren
+ // (Evalcost-)Ende geschieht und danach u.U. keine Rechenzeit fuer
+ // andere Aktionen mehr ueber ist
+ load_auto_objects( autoload );
+}
+
+/** Setzt die Spielerinitialisierung nach start_player() fort.
+ * Prueft den Wert der vom Spieler getragenen Sachen (fuer Entschaedigung
+ * nach Reboot).
+ * Setzt div. Properties des "Spielerkoerpers", die eigentlich gespeichert
+ * werden, nach einem Reboot zurueck.
+ * Fragt ggf. nach eMail-Adresse und uebergibt per input_to an
+ * InitPlayer2().
+ * @sa InitPlayer3(),InitPlayer2(), InitPlayer(), start_player().
+*/
+private void InitPlayer()
+{
+ string mailaddr;
+
+ // wenn es einen Crash gab, sollen Spieler nicht noch extra bestraft werden
+ if ( file_time( "/save/" + getuid()[0..0] + "/" + getuid() + ".o" )
+ < last_reboot_time() ){
+ SetProp( P_FOOD, 0 );
+ SetProp( P_DRINK, 0 );
+ SetProp( P_ALCOHOL, 0 );
+ SetProp( P_BLIND, 0 );
+ SetProp( P_DEAF, 0 );
+ SetProp( P_POISON, 0 );
+ SetProp( P_GHOST, 0 );
+ SetProp( P_FROG, 0 );
+ SetProp( P_HP, QueryProp(P_MAX_HP) );
+ SetProp( P_SP, QueryProp(P_MAX_SP) );
+ }
+
+ if ( QueryGuest() )
+ Set( P_MAILADDR, "none" );
+ else if ( !(mailaddr = Query(P_MAILADDR)) || mailaddr == "" ) {
+ write(break_string(
+ "Eine gueltige EMail-Adresse erleichtert es erheblich, Dir "
+ "ein neues Passwort setzen zu lassen, falls Du einmal Dein "
+ "Passwort vergisst.",78));
+ input_to( "getmailaddr",INPUT_PROMPT,
+ "Gib bitte Deine EMail-Adresse an: " );
+ return;
+ }
+ InitPlayer2();
+}
+
+/** liest eMail-Adresse vom Spieler ein und speichert sie.
+ * Uebergibt anschliessend an InitPlayer2()
+ * @param[in] maddr Spielereingabe der Emailadresse.
+ * @sa InitPlayer2().
+ */
+static void getmailaddr( string maddr )
+{
+ maddr = check_email(maddr);
+
+ if ( !stringp(maddr)) {
+ write("Deine Eingabe scheint keine gueltige EMail-Adresse gewesen "
+ "zu sein.\n");
+ input_to( "getmailaddr", INPUT_PROMPT,
+ "Gib bitte Deine EMail-Adresse an: " );
+ return;
+ }
+ Set( P_MAILADDR, maddr );
+ InitPlayer2();
+}
+
+
+/** Prueft Geschlecht des Spielers und fragt ggf. beim Spieler nach.
+ * Uebergibt an InitPlayer3() oder get_gender().
+ * @sa InitPlayer3(), get_gender().
+ */
+private void InitPlayer2()
+{
+ if( member(({ MALE, FEMALE }), QueryProp(P_GENDER) ) == -1 ) {
+ input_to( "getgender", INPUT_PROMPT,
+ "Bist Du maennlich oder weiblich: ");
+ return;
+ }
+
+ InitPlayer3();
+}
+
+/** Liest Spielerantwort auf die Frage nach dem Geschlecht des Chars ein.
+ * Wird gerufen von input_to().
+ * Uebergibt an InitPlayer3().
+ * @sa InitPlayer3().
+ */
+static void getgender( string gender_string )
+{
+ gender_string = lower_case( gender_string );
+
+ if ( sizeof(gender_string)==1 && gender_string[0] == 'm' ){
+ write( "Willkommen, mein Herr!\n" );
+ SetProp( P_GENDER, MALE );
+ }
+ else if ( sizeof(gender_string)==1 && gender_string[0] == 'w' ){
+ write( "Willkommen, gnae' Frau!\n" );
+ SetProp( P_GENDER, FEMALE );
+ }
+ else {
+ write( "Wie? Was? Verstehe ich nicht!\n" );
+ input_to( "getgender", INPUT_PROMPT,
+ "Bist Du maennlich oder weiblich? (tippe m oder w): ");
+ return;
+ }
+
+ InitPlayer3();
+}
+
+
+/** Prueft Terminaltyp des Spielers und fragt ggf. beim Spieler nach.
+ * Uebergibt an InitPlayer4() oder gettty().
+ * @sa InitPlayer4(), gettty().
+ */
+private void InitPlayer3()
+{
+ if ( !QueryProp(P_TTY) || QueryProp(P_TTY) == "none" )
+ {
+ write( "Waehle einen Terminaltyp (kann spaeter mit <stty> geaendert "
+ "werden)\n");
+ input_to( "gettty", INPUT_PROMPT, "vt100, ansi, dumb (Standard: dumb): " );
+ return;
+ }
+ InitPlayer4();
+}
+
+/** Liest Spielerantwort auf die Frage nach dem Terminaltyp ein.
+ * Wird gerufen von input_to().
+ * Uebergibt an InitPlayer4().
+ * @sa InitPlayer4().
+ */
+static void gettty( string ttystr )
+{
+ if ( !ttystr || ttystr == "" )
+ ttystr = "dumb";
+
+ ttystr = lower_case(ttystr);
+
+ if ( ttystr == "vt100" ){
+ write( "Dies sollte " + ANSI_BOLD + "fett" + ANSI_NORMAL + " sein.\n" );
+ SetProp( P_TTY, ttystr );
+ }
+ else
+ if ( ttystr == "ansi" ){
+ write( "Dies sollte " + ANSI_RED + "rot" + ANSI_NORMAL +
+ " sein.\n" );
+ SetProp( P_TTY, "ansi" );
+ }
+ else if ( ttystr == "dumb" ){
+ write( "Ohje, oede! Besorg Dir ein besseres Terminal!\n" );
+ SetProp( P_TTY, "dumb" );
+ }
+ else {
+ write( "Dieser Terminaltyp wird nicht unterstuetzt. Nimm bitte "
+ "einen aus:\nvt100, ansi or dumb (Standard ist dumb).\n" );
+ input_to( "gettty", INPUT_PROMPT,
+ "vt100, ansi or dumb (Standard ist dumb): ");
+ return;
+ }
+
+ InitPlayer4();
+}
+
+
+/** Liefert die UID des Charakters zurueck, also den Charakternamen.
+ * @return UID des Objekts (Charaktername).
+ */
+nomask string query_real_name() {
+ /* ACHTUNG !! DIES LFUN DARF NICHT ENTFERNT WERDEN !!! */
+ /* Sie wird vom Gamedriver (zB bei F_ED) aufgerufen !! */
+ /* Ich bin da zwar nicht so ueberzeugt von, dass der Driver die heutzutage
+ * noch ruft, aber die halbe Mudlib ruft sie. ;-) (Zesstra, 27.4.08)
+ */
+ return getuid();
+}
+
+/*
+ * the wizard command review: show player moving messages
+ */
+int review() {
+ string *msg;
+ write(short());
+ write("Deine Bewegungen werden wie folgt gemeldet:\n"+
+ "mout: "+name(WER)+" "+(msg=explode(QueryProp(P_MSGOUT),"#"))[0]
+ +" <Richtung>"+(sizeof(msg)>1 ? msg[1] : "")+".\n"+
+ "min: "+name(WER)+" "+QueryProp(P_MSGIN)+".\n"+
+ "mmout: "+name(WER)+" "+QueryProp(P_MMSGOUT)+".\n"+
+ "mmin: "+name(WER)+" "+QueryProp(P_MMSGIN)+".\n"+
+ (IS_LEARNER(ME) ?
+ "cmsg: "+name(WER)+" "+QueryProp(P_CLONE_MSG)+".\n"+
+ "dmsg: <Irgendetwas> "+QueryProp(P_DESTRUCT_MSG)+".\n"
+ : "")+
+ "Wenn Du jemanden angreifst, sieht das so aus:\n"+
+ name(WER)+" greift Dich"+QueryProp(P_HANDS)[0]+" an.\n");
+ return 1;
+}
+
+/*
+ * set player moving messages
+ */
+
+static int setmin(string str)
+{
+ SetProp(P_MSGIN, _unparsed_args()||"kommt an");
+ write("Ok.\n");
+ return 1;
+}
+
+static int setmout(string str)
+{
+ string *msg;
+
+ if(sizeof(msg=explode((_unparsed_args()||"geht"),"#"))>2)
+ {
+ write("Du darfst nur einmal '#' fuer die Richtung angeben.\n");
+ return 1;
+ }
+ if(sizeof(msg)>1)
+ {
+ if (msg[0]!="" && msg[0][<1]==' ') msg[0]=msg[0][0..<2];
+ SetProp(P_MSGOUT, msg[0]+"#"+msg[1]);
+ }
+ else
+ SetProp(P_MSGOUT, _unparsed_args()||"geht");
+ write("Ok.\n");
+ return 1;
+}
+
+static int setmmin(string str)
+{
+ SetProp(P_MMSGIN, _unparsed_args()||"erscheint");
+ write("Ok.\n");
+ return 1;
+}
+
+static int setmmout(string str)
+{
+ SetProp(P_MMSGOUT, _unparsed_args()||"verschwindet");
+ write("Ok.\n");
+ return 1;
+}
+
+static int setcmsg(string str)
+{
+ SetProp(P_CLONE_MSG, _unparsed_args()||"zaubert etwas aus "
+ + QueryPossPronoun(MALE,WEM) + " Aermel hervor");
+ write("Ok.\n");
+ return 1;
+}
+
+static int setdmsg(string str)
+{
+ SetProp(P_DESTRUCT_MSG, _unparsed_args()||"wird von " + name(WER,1)
+ + " zerstaeubt");
+ write("Ok.\n");
+ return 1;
+}
+
+static int set_title(string str)
+{
+ string bonus;
+
+ if(!IS_SEER(this_object()) && !FAO_HAS_TITLE_GIFT(this_object()) )
+ {
+ return 0;
+ }
+
+ bonus = "";
+ if (!(str=_unparsed_args()))
+ str = "";
+ else if( str[0..2]=="\\b," || str[0..2]=="\\b'" )
+ {
+ bonus = "\b"; // ein backspace fuer ein (hoch)komma ist ok! :-)
+ str = str[2..];
+ }
+ if(str=="0") // damit der Gildentitel zum Vorschein kommen kann
+ SetProp(P_TITLE, 0);
+ else
+ SetProp(P_TITLE, bonus+str);
+ write("Ok.\n");
+ return 1;
+}
+
+static int extra_input(string str, string look)
+{
+ if (str=="**")
+ {
+ if (look=="")
+ SetProp(P_EXTRA_LOOK,0);
+ else
+ SetProp(P_EXTRA_LOOK,look);
+ write("Ok.\n");
+ return 1;
+ }
+ input_to("extra_input",INPUT_PROMPT, "]" ,look+str+"\n");
+ return 1;
+}
+
+static int extralook(mixed str)
+{
+ if( str=="?" )
+ {
+ write( "Dein Extralook ist : "+QueryProp(P_EXTRA_LOOK) + "\n");
+ return 1;
+ }
+ write("Bitte gib Deinen Extra-Look ein. Beenden mit **:\n");
+ input_to("extra_input",INPUT_PROMPT,"]","");
+ return 1;
+}
+
+static void calculate_value()
+{
+ int i, carried_value, value;
+
+ carried_value=0;
+ foreach(object ob: deep_inventory(ME)) {
+ if (!ob->QueryProp(P_AUTOLOADOBJ))
+ carried_value+=((value=(int)ob->QueryProp(P_VALUE)) > 1000 ? 1000 : value);
+ }
+ SetProp(P_CARRIED_VALUE, carried_value);
+}
+
+/* Called by command 'save' */
+int save_character() {
+ save_me(1);
+ write("Ok.\n");
+ return 1;
+}
+
+void save_me(mixed value_items)
+{
+ // Gaeste werden nicht gespeichert
+ if( getuid()[0..3]=="gast" )
+ return;
+
+ // Autoloader identifizieren und speichern
+ autoload=([]);
+ foreach(object ob: deep_inventory(ME)) {
+ int val = ob->QueryProp( P_AUTOLOADOBJ );
+ if (val && clonep(ob))
+ {
+ string obname=load_name(ob);
+ if (obname == GELD)
+ autoload[obname] += val;
+ else
+ autoload += ([obname:val]);
+ }
+ }
+ // An noch nicht geclonte Autoloader denken!
+ autoload += autoload_rest;
+
+ // Im Bedarfsfall Wert des Inventory bestimmen
+ if (value_items)
+ calculate_value();
+ else
+ SetProp(P_CARRIED_VALUE, 0);
+
+ // Logout-Zeit speichern
+ if(query_once_interactive(ME) && !QueryProp(P_INVIS))
+ Set(P_LAST_LOGOUT,time());
+
+ // Funktion zur Bestimmung des Gildenrating aufrufen
+ string gilde=GUILD_DIR+QueryProp(P_GUILD);
+ if (find_object(gilde) || file_size(gilde+".c")>-1)
+ catch(call_other(gilde,"GuildRating",this_object());publish);
+
+ // Speichern des Spielers
+ save_object("/"+SAVEPATH+getuid()[0..0]+"/" + getuid());
+}
+
+static varargs void log_autoload( string file, string reason, mixed data, string error )
+{
+ if (member(autoload_error,file)!=-1) return;
+ log_file(SHELLLOG("NO_AUTO_FILE"),sprintf("%s: %s: %s\nreason: cannot %s file\ndata: %O\n%s\n",
+ ctime()[4..15],capitalize(getuid()),file,reason,data,
+ (error?"Fehlermeldung: "+error+"\n":"")));
+ autoload_error+=({file});
+}
+
+// tics, die fuer autoloader reichen sollten:
+#define SAFE_FOR_AUTOLOADER __MAX_EVAL_COST__/4
+
+private void load_auto_object( string file, mixed data )
+{
+ object ob;
+ string error;
+
+ if( get_eval_cost() < SAFE_FOR_AUTOLOADER ) return;
+ m_delete( autoload_rest, file );
+ autoload_error-=({file});
+
+ if ( file == "/obj/money" )
+ file = "/items/money";
+ if ( file == "/obj/seercard" )
+ file = "/items/seercard";
+
+ ob = find_object(file);
+
+ if (!ob)
+ {
+ if (file_size(file+".c")<0&&
+ file_size(implode(explode(file,"/")[0..<2],"/")+
+ "/virtual_compiler.c")<0)
+ {
+ log_autoload(file,"find",data,0);
+ return;
+ }
+ if (error = catch(load_object(file); publish))
+ {
+ log_autoload(file,"load",data,error);
+ return;
+ }
+ }
+ if ( error = catch(ob = clone_object(file); publish) )
+ {
+ log_autoload(file,"clone",data, error);
+ return;
+ }
+
+ if ( error = catch(ob->SetProp( P_AUTOLOADOBJ, data ); publish) )
+ {
+ log_autoload(file,"SetProp",data, error);
+ ob->remove(1);
+ if (ob) destruct(ob);
+ return;
+ }
+
+ if ( error = catch(ob->move( ME, M_NOCHECK );publish) ) {
+ log_autoload(file,"move",data, error);
+ ob->remove(1);
+ if(ob) destruct(ob);
+ return;
+ }
+}
+
+static void load_auto_objects( mapping map_ldfied )
+{
+ if ( (!mappingp(map_ldfied) || !sizeof(map_ldfied)) && !sizeof(autoload_rest) )
+ return;
+
+ // Mit Netztoten Spielern rechnen manche Autoloader nicht. Also
+ // das Clonen unterbrechen und in Reconnect() wieder anwerfen.
+ if ( !interactive() )
+ return;
+
+ // Kleiner Hack: autoload_rest ist eine globale Variable, die beim
+ // Clonen der einzelnen Autoloader direkt veraendert wird.
+ // So lange das Mapping noch Eintraege hat, muessen wir noch fehlende
+ // Autoloader clonen.
+ if ( !sizeof(autoload_rest) )
+ autoload_rest = map_ldfied;
+
+ // Schon hier einen call_out() zum "Nach"clonen von noch nicht geclonten
+ // Autoloadern starten, da spaeter u.U. keine Rechenzeit mehr dafuer da ist.
+ while ( remove_call_out("load_auto_objects") != -1 )
+ /* do nothing */;
+
+ // Mit Parameter '0' aufrufen, da das globale Mapping benutzt wird.
+ // Verzoegerung 0 in rekursiven Callouts ist bloed, also 1s Delay
+ call_out( "load_auto_objects", 2, 0 );
+
+ // Mit catch() gegen die Evalcost-Falle!
+ // Mit Absicht das walk_mapping() aus der "alten" Version erhalten und
+ // nicht durch eine (einfachere) Schleife inkl. get_eval_cost() ersetzt,
+ // da eine Schleife gegenueber der walk_mapping()-Loesung den Aufbau
+ // der previous_object()-Kette veraendern wuerde; darauf testen aber
+ // manche Objekte.
+ catch( walk_mapping( autoload_rest, #'load_auto_object/*'*/ ) );
+}
+
+/*
+ * al_to_title: Make the numeric alignment value into a string
+ */
+static string al_to_title(int a)
+{
+ if (a >= KILL_NEUTRAL_ALIGNMENT * 100)
+ return "heilig";
+ if (a > KILL_NEUTRAL_ALIGNMENT * 20)
+ return "gut";
+ if (a > KILL_NEUTRAL_ALIGNMENT * 4)
+ return "nett";
+ if (a > - KILL_NEUTRAL_ALIGNMENT * 4)
+ return "neutral";
+ if (a > - KILL_NEUTRAL_ALIGNMENT * 20)
+ return "frech";
+ if (a > - KILL_NEUTRAL_ALIGNMENT * 100)
+ return "boese";
+ return "satanisch";
+}
+
+static int toggle_whimpy_dir(string str) {
+ SetProp(P_WIMPY_DIRECTION,str=_unparsed_args()||str);
+ if (str)
+ printf("Ok, Fluchtrichtung %O.\n",str);
+ else
+ printf("Ok, bevorzugte Fluchtrichtung deaktiviert.\n");
+ return 1;
+}
+
+static int toggle_whimpy(string str)
+{
+ int i;
+
+ if(!str || str=="" || (sscanf(str,"%d",i)<0))
+ {
+ write("vorsicht <hp>, 0<=hp<"+QueryProp(P_MAX_HP)+"\n");
+ return 1;
+ }
+ if(i>=QueryProp(P_MAX_HP) || i<0)
+ {
+ write("Der Wert ist nicht erlaubt.\n");
+ return 1;
+ }
+ if(!i) write("Prinz Eisenherz-Modus.\n");
+ else write("Vorsicht-Modus ("+i+")\n");
+ SetProp(P_WIMPY,i);
+ return 1;
+}
+
+/** Bestimmt, ob das Spielerobjekt beschattet werden darf.
+ * Beschatten ist nur fuer Objekte erlaubt, die in /std/player/shadows
+ * abgelegt sind.
+ * Ausnahme: Testspieler mit gesetztem P_ALLOWED_SHADOW
+ * @param[in] obj Objekt, was beschatten moechte.
+ * @return 0, wenn Beschatten erlaubt, 1 sonst.
+ */
+varargs nomask int query_prevent_shadow(object obj)
+{
+ string what, allowed_shadow;
+ int dummy;
+
+// if ( Query(P_TESTPLAYER) )
+// return 0;
+
+ if (obj){
+ what=object_name(obj);
+ if (what && what != "" &&
+ sscanf(what,"/std/player/shadows/%s#%d",what,dummy)==2)
+ return 0;
+
+ // Einem Testspieler kann man P_ALLOWED_SHADOW auf einen zu testenden
+ // Shadow setzen.
+ if ( Query(P_TESTPLAYER) &&
+ stringp(what) &&
+ stringp(allowed_shadow=Query(P_ALLOWED_SHADOW)) &&
+ strstr(what, allowed_shadow)==0)
+ return 0;
+ }
+ return 1;
+}
+
+static int uhrzeit()
+{
+ write(dtime(time()+QueryProp(P_TIMEZONE)*3600)+".\n");
+ return 1;
+}
+
+protected string SetDefaultHome(string str)
+{
+ return default_home=str;
+}
+
+string QueryDefaultHome()
+{
+ return default_home;
+}
+
+protected string SetDefaultPrayRoom(string str)
+{
+ if(hc_play>1)
+ {
+ default_pray_room="/room/nirvana";
+ }
+ else
+ default_pray_room=str;
+
+ return default_pray_room;
+}
+
+string QueryPrayRoom()
+{
+ if(hc_play>1)
+ {
+ return "/room/nirvana";
+ }
+ string room = QueryProp(P_PRAY_ROOM);
+ if (stringp(room))
+ return room;
+ else if (default_pray_room)
+ return default_pray_room;
+ // hoffentlich ist das wenigstens gesetzt.
+ return default_home;
+}
+
+void _restart_beat()
+{
+ tell_object(ME,
+ "Der GameDriver teilt Dir mit: Dein Herzschlag hat wieder eingesetzt.\n");
+ set_heart_beat(1);
+}
+
+static int weg(string str)
+{
+ if (!(str=_unparsed_args()))
+ {
+ printf("Du bist nicht%s als abwesend gekennzeichnet.\n",
+ QueryProp(P_AWAY) ? " mehr" : "");
+ SetProp(P_AWAY, 0);
+ return 1;
+ }
+ write("Du bist jetzt als abwesend gekennzeichnet.\n");
+ SetProp(P_AWAY, str);
+ return 1;
+}
+
+/* Ein Befehl zum anschauen der Wegmeldung anderer Spieler */
+static int wegmeldung(string player)
+{
+
+ object player_ob;
+ string weg;
+
+ if ( !player || player=="" ||
+ player==lowerstring(this_player()->QueryProp(P_NAME)))
+ {
+ weg=this_player()->QueryProp(P_AWAY);
+ write ("Du bist "+(weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
+ if (weg)
+ write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
+ return 1;
+ }
+
+ // Welcher Spieler ist gemeint?
+ player_ob=find_player(player);
+
+ // Spieler nicht da oder Invis und Anfrager is kein Magier
+ if (!player_ob ||
+ (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
+ {
+ write(capitalize(player)+" ist gerade nicht im Spiel.\n");
+ return 1;
+ }
+
+ weg=player_ob->QueryProp(P_AWAY);
+
+ // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
+ write (player_ob->QueryProp(P_NAME)+" ist "+
+ (weg?"":"nicht ")+"als abwesend gekennzeichnet.\n");
+
+ if (weg)
+ write(break_string(weg, 78,"Grund: ",BS_INDENT_ONCE));
+
+ return 1;
+}
+
+static string timediff(int time)
+{
+ string ret;
+
+ ret="";
+ if(time>=86400) {
+ ret+=time/86400+"d ";
+ time%=86400;
+ }
+ if(time<36000) ret+="0";
+ ret+=time/3600+":";
+ time%=3600;
+ if(time<600) ret+="0";
+ ret+=time/60+":";
+ time%=60;
+ if(time<10) ret+="0";
+ ret+=time+"";
+ return ret;
+}
+
+
+/* Ein Befehl zum anschauen der Idlezeit anderer Spieler */
+static int idlezeit(string player)
+{
+
+ object player_ob;
+ int idle;
+
+ if ( !player || player=="" ||
+ player==lowerstring(this_player()->QueryProp(P_NAME)))
+ {
+ write ("Du bist selber natuerlich gerade nicht idle.\n");
+ return 1;
+ }
+
+ // Welcher Spieler ist gemeint?
+ player_ob=find_player(player);
+
+ // Spieler nicht da oder Invis und Anfrager is kein Magier
+ if (!player_ob ||
+ (player_ob->QueryProp(P_INVIS) && !IS_LEARNER(this_player())))
+ {
+ write(capitalize(player)+" ist gerade nicht im Spiel.\n");
+ return 1;
+ }
+
+ idle=query_idle(player_ob);
+
+ // player_ob->Name() gibt bei invis-Magiern "Jemand" zurueck
+ write (player_ob->QueryProp(P_NAME)+" ist "+
+ (idle>=60?timediff(idle):"nicht")+" passiv.\n");
+
+ return 1;
+}
+
+
+/** Belebt einen netztoten Spieler wieder.
+ * Reaktiviert Heartbeats, bewegt den Spieler zurueck an den Ort, der eim
+ * Einschlafen zum Aufwachen ermittelt wurde (im einfachsten Fall der Raum,
+ * wo man eingeschlafen ist).
+ */
+static void ndead_revive()
+{
+ string fname;
+ int ret;
+
+ set_heart_beat(1);
+ ndead_next_check = NETDEAD_CHECK_TIME;
+ ndead_currently = 0;
+ ndead_lasttime = 0;
+
+ if ( !objectp(ndead_location) &&
+ stringp(ndead_l_filename) && sizeof(ndead_l_filename)) {
+
+ if ( member( ndead_l_filename, '#' ) == -1 ){
+ catch(load_object( ndead_l_filename); publish);
+ ndead_location = find_object(ndead_l_filename);
+ }
+ else {
+ ndead_location = find_object(ndead_l_filename);
+ if ( !ndead_location && env_ndead_info ){
+ fname = explode( ndead_l_filename, "#" )[0];
+ catch(ndead_location =
+ (load_object(fname)->SetProp(P_NETDEAD_INFO, env_ndead_info));
+ publish);
+ if ( !objectp(ndead_location) ){
+ catch(load_object( ndead_location);publish);
+ ndead_location = find_object(ndead_location);
+ }
+ }
+ }
+ }
+
+ if ( !objectp(ndead_location)
+ || catch(ret = move( ndead_location, M_GO|M_SILENT );publish)
+ || ret != 1 ) {
+ move( "gilden/abenteurer", M_GO|M_SILENT );
+ ndead_location = environment();
+ }
+
+ // ndead_location=0;
+ ndead_l_filename = 0;
+ env_ndead_info = 0;
+}
+
+/** Bewegt einen netztoten Spieler in den Netztotenraum
+ * Gerufen von heartbeat().
+ * Zerstoert Gaeste, verlaesst ggf. das Team, ermittelt, ob der Spieler beim
+ * Aufwachen in das alte Environment bewegt werden soll oder in einen anderen
+ * Raum, hierzu wird im Environment P_NETDEAD_INFO abgefragt.
+ * Deaktiviert die Kommandos per disable_commands().
+ */
+static void ndead_move_me() {
+ object team;
+ mixed amem;
+
+ set_heart_beat(0);
+ stop_heart_beats();
+ if (QueryGuest()) {
+ quit();
+ if (ME)
+ remove();
+ if (ME)
+ destruct(ME);
+ return;
+ }
+ ndead_next_check=NETDEAD_CHECK_TIME;
+ ndead_currently=1;
+ ndead_lasttime=0;
+ ndead_location=environment();
+ if (objectp(ndead_location))
+ ndead_l_filename=object_name(ndead_location);
+
+ if (objectp(environment())
+ && sizeof(explode(object_name(environment()),"#")) > 1)
+ env_ndead_info=environment()->QueryProp(P_NETDEAD_INFO);
+ else
+ env_ndead_info=0;
+
+ if ( objectp(team=Query(P_TEAM)) )
+ // Der Test auf assoziierte Teammitglieder (== FolgeNPCs)
+ // verhindert, dass Spieler nach "schlafe ein" aus dem
+ // Team ausgetragen werden. -- 29.01.2002 Tiamak
+ // && !objectp(amem=Query(P_TEAM_ASSOC_MEMBERS))
+ // && !(pointerp(amem) && sizeof(amem)))
+ team->RemoveMember(ME);
+
+ disable_commands();
+ move(NETDEAD_ROOM,M_GO|M_NO_ATTACK|M_NOCHECK,"ins Reich der Netztoten");
+}
+
+/** Ist dieser Character ein Gast?
+ * @return 1, wenn Gast, 0 sonst.
+ */
+int QueryGuest()
+{
+ string dummy;
+ return sscanf(getuid(),"gast%d",dummy);
+}
+
+/** Spielerkommando 'schlafe ein'.
+ * Ruft remove_interactive() bei Spielern, bei Magiern wird quit() gerufen,
+ * um das Magierobjekt zu zerstoeren.
+ * @sa quit()
+ */
+int disconnect(string str)
+{
+ string verb;
+ string desc = break_string(
+ "\"schlafe ein\" beendet Deine Verbindung mit "MUDNAME". Du behaeltst "
+ "Deine Sachen.\nFalls "MUDNAME" jedoch abstuerzt oder neu gestartet "
+ "wird, waehrend Du weg bist, verlierst Du die meisten allerdings "
+ "(genauso, als wenn Du Deine Verbindung mit \"ende\" beendet haettest). "
+ "In diesem Fall bekommst Du dann eine kleine Entschaedigung."
+ ,78,0,BS_LEAVE_MY_LFS);
+
+ verb=query_verb();
+ if (!verb)
+ verb="AUTOCALL";
+ if (verb[0..5]=="schlaf" && str!="ein")
+ {
+ notify_fail(desc);
+ return 0;
+ }
+ if (IS_LEARNER(this_object()))
+ return quit();
+
+ tell_object(this_object(), desc);
+
+ if (clonep(environment()) && !environment()->QueryProp(P_NETDEAD_INFO))
+ tell_object(this_object(),break_string(
+ "\nACHTUNG: Wenn Du hier laengere Zeit schlaefst, "
+ "kommst Du vermutlich nicht an diesen Ort zurueck!",78));
+
+ say(capitalize(name(WER))+" hat gerade die Verbindung zu "MUDNAME" gekappt.\n");
+ remove_interactive(ME);
+ call_out(#'clear_tell_history,4);
+ return 1;
+}
+
+static int finger (string str)
+{
+ string ret;
+ mixed xname;
+
+ if (!str || str==""
+ || sizeof(explode(str," ")-({"-n","-p","-s","-v","-a"}))>1)
+ {
+ write("finger <spielername> oder finger <spielername@mudname>\n"+
+ "Bitte nur den reinen Spielernamen verwenden, keine Namensvorsaetze oder Titel\n");
+ return 1;
+ }
+ xname=explode(str,"@");
+ if(sizeof(xname)==2)
+ {
+ if (xname[0]=="-n " || xname[0]=="-p " || xname[0]=="-s ") {
+ write("finger <spielername>@<mudname> - der Spielername fehlt.\n");
+ return 1;
+ }
+ if (ret=INETD->_send_udp(xname[1],([
+ REQUEST: "finger",
+ SENDER: getuid(ME),
+ DATA: (explode(xname[0]," ")-({"-n","-p","-s"}))[0]
+ ]), 1))
+ write(ret);
+ else
+ write("Anfrage abgeschickt.\n");
+ return 1;
+ }
+ "/p/daemon/finger"->finger_single(str,1);
+ return 1;
+}
+
+string lalign(string str, int wid)
+{
+ return (str+" "+
+ " ")[0..wid-1];
+}
+
+#define MUDS_BAR "\
+-------------------------------------------------------------------------------"
+
+private void format(mixed mud, mixed hosts, string output)
+{
+ output += lalign(hosts[mud][HOST_NAME], 20) + " " +
+ (hosts[mud][HOST_STATUS] ?
+ hosts[mud][HOST_STATUS] > 0 ?
+ "UP " + ctime(hosts[mud][HOST_STATUS])[4..15] :
+ "DOWN " + ctime(-hosts[mud][HOST_STATUS])[4..15]
+ : "UNKNOWN Never accessed.") + "\n";
+}
+
+static int muds() {
+ mapping hosts;
+ int i;
+ mixed muds, output;
+
+ output = lalign("Mudname", 20) + " Status Last access";
+ output += "\n" + MUDS_BAR[0..sizeof(output)] + "\n";
+ muds = sort_array(m_indices(hosts = INETD->query("hosts")),#'>);
+ map(muds, #'format, hosts, &output);
+ More(output);
+ return 1;
+}
+
+// **** local property methods
+static int _set_level(int i)
+{
+ if (!intp(i)) return -1;
+ if (i<1) return -1;
+ Set(P_LEVEL, i);
+ GMCP_Char( ([P_LEVEL: i]) );
+ return i;
+}
+
+static int _set_invis(int a)
+{
+ return Set(P_INVIS, intp(a) ? a : !Query(P_INVIS));
+}
+
+/* sets the terminal type */
+/* note: support vt100 (b/w), ansi (color), dumb (none) */
+static string _set_tty(string str) {
+ if(str != "dumb" && str != "vt100" && str != "ansi")
+ return Query(P_TTY);
+ return Set(P_TTY, str);
+}
+
+static int stty(string str)
+{
+ if(str!="dumb"&&str!="vt100"&&str!="ansi"&&str!="reset")
+ {
+ write("Kommando: stty dumb|vt100|ansi oder reset\n");
+ }
+ if(str == "reset") {
+ printf("[30;47m[0mDieser Text sollte lesbar sein!\n");
+ return 1;
+ }
+
+ write("TTY steht jetzt auf "+SetProp(P_TTY,str)+".\n");
+ if(str == "ansi" || str == "vt100") {
+ printf("Terminal Test:\n");
+ printf("VT100: [1mfett[0m [4munterstrichen[0m "+
+ "[5mblinkend[0m [7minvers[0m\n");
+ if(str == "ansi") {
+ printf("ANSI Farben und VT100 Attribute:\n");
+ foreach(int fg: 30 .. 37) {
+ foreach(int bg: 40 .. 47) {
+ printf("[%d;%dm[1m@[0m", fg, bg);
+ printf("[%d;%dm[4m@[0m", fg, bg);
+ printf("[%d;%dm[5m@[0m", fg, bg);
+ printf("[%d;%dm[7m@[0m", fg, bg);
+ }
+ printf("\n");
+ }
+ printf("Sollte dieser Text hier nicht richtig lesbar\nsein,"+
+ "benutze das Kommando stty reset!\n");
+ }
+
+ }
+ return 1;
+}
+
+int set_ascii_art(string str)
+{
+ if (str!="ein"&&str!="aus")
+ {
+ printf("Du moechtest 'Grafik' "+(QueryProp(P_NO_ASCII_ART)?"NICHT ":"")+
+ "sehen.\n");
+ }
+
+ if (str=="ein") {
+ SetProp(P_NO_ASCII_ART, 0);
+ printf("Zukuenftig moechtest Du 'Grafik' sehen.\n");
+ }
+
+ if (str=="aus") {
+ SetProp(P_NO_ASCII_ART, 1);
+ printf("Zukuenftig moechtest Du KEINE 'Grafik' mehr sehen.\n");
+ }
+
+
+ return 1;
+}
+
+int _set_shell_version(int arg)
+{
+ if (!intp(arg))
+ return -1;
+ Set(P_SHELL_VERSION,({QueryProp(P_RACE),arg}));
+ return 1;
+}
+
+int _query_shell_version()
+{ mixed sv;
+
+ if (!(sv=Query(P_SHELL_VERSION)) || !pointerp(sv) || sizeof(sv)!=2 ||
+ sv[0]!=QueryProp(P_RACE) || !intp(sv[1]))
+ return 0;
+ return sv[1];
+}
+
+// XxXxXxXxXx
+
+int more(string str)
+{
+ if(!str)
+ {
+ notify_fail("Usage: more <file>\n");
+ return 0;
+ }
+ if (file_size(str) <= 0) {
+ notify_fail(str+": No such file\n");
+ return 0;
+ }
+ More(str, 1);
+ return 1;
+}
+
+static int set_visualbell(string str)
+{
+ if(!str)
+ {
+ write("Derzeitige Einstellung fuer Tonausgabe: "+
+ (QueryProp(P_VISUALBELL)?"AUS":"EIN")+".\n");
+ return 1;
+ }
+ if (str=="ein")
+ {
+ if(!QueryProp(P_VISUALBELL))
+ write("Die Tonausgabe stand schon auf EIN.\n");
+ else
+ {
+ SetProp(P_VISUALBELL,0);
+ write("OK, Tonausgabe auf EIN gestellt.\n");
+ }
+ }
+ else
+ if (str=="aus")
+ {
+ if(QueryProp(P_VISUALBELL))
+ write("Die Tonausgabe stand schon auf AUS.\n");
+ else
+ {
+ SetProp(P_VISUALBELL,1);
+ write("OK, Tonausgabe auf AUS gestellt.\n");
+ }
+ }
+ else
+ write("Syntax: ton [ein|aus]\n");
+ return 1;
+}
+
+static int set_screensize(string str)
+{
+ int size;
+
+ if (str && (str[0..2] == "abs" || str[0..2]=="rel")) {
+ size = QueryProp(P_MORE_FLAGS);
+ if (str[0..2] == "abs") {
+ size |= E_ABS;
+ write("Es wird beim Prompt die Zeilenzahl des Textes angegeben.\n");
+ }
+ else {
+ size &= ~E_ABS;
+ write("Es wird beim Prompt der prozentuale Anteil des Textes angegeben.\n");
+ }
+ SetProp(P_MORE_FLAGS, size);
+ return 1;
+ }
+
+ if ( str && (str=="auto" || sscanf( str, "auto %d", size )) ){
+ if ( size > 0 ){
+ write("Ungueltiger Wert! "
+ "In Verbindung mit 'auto' sind nur negative Werte erlaubt.\n");
+ return 1;
+ }
+
+ SetProp( P_SCREENSIZE, size-1 );
+
+ write("Ok, Deine Zeilenzahl wird nun automatisch ermittelt (derzeit "+
+ QueryProp(P_SCREENSIZE)+").\n"+
+ break_string("Bitte beachte, dass dies nur einwandfrei "
+ "funktioniert, wenn Dein Client Telnetnegotiations "
+ "unterstuetzt (siehe auch \"hilfe telnegs\").") );
+ return 1;
+ }
+
+ if ( !str || str=="" || !sscanf( str, "%d", size ) || size < 0 || size > 100){
+ write(break_string(
+ sprintf("Mit dem Befehl 'zeilen <groesse>' kannst Du einstellen, "
+ "wieviele Zeilen bei mehrseitigen Texten auf einmal ausgegeben "
+ "werden. Die angegebene Groesse muss zwischen 0 und 100 liegen. "
+ "Bei Groesse 0 wird einfach alles ausgegeben (ohne Pause). Mit "
+ "der Einstellung 'auto' wird die Groesse automatisch ueber "
+ "die Telnetnegotiations ermittelt (siehe auch 'hilfe telnegs'). "
+ "Um nach einer Seite Text noch etwas Platz zu haben, kann man z.B. "
+ "'zeilen auto -3' einstellen.\n"
+ "Die Voreinstellung ist 20 Zeilen.\n"
+ "Mit 'zeilen abs[olut]' und 'zeilen rel[ativ]' kannst Du fest"
+ "legen, ob im Prompt bei langen Texten die aktuelle Zeilennummer "
+ "oder eine prozentuale Angabe ausgegeben wird.\n"
+ "Deine aktuelle Einstellung ist %d%s Zeilen (%s).",
+ QueryProp(P_SCREENSIZE),
+ Query(P_SCREENSIZE) < 0 ? " 'automatische'" : "",
+ QueryProp(P_MORE_FLAGS) & E_ABS ? "absolut" : "relativ"),78,0,1));
+ return 1;
+ }
+
+ SetProp( P_SCREENSIZE, size );
+
+ printf( "Okay, Deine Zeilenzahl steht nun auf %d.\n", size );
+ return 1;
+}
+
+static int _query_screensize()
+{
+ int sz,rows;
+
+ if ( (sz=Query(P_SCREENSIZE)) >= 0 )
+ return sz;
+
+ if ( !rows=QueryProp(P_TTY_ROWS) )
+ return 0;
+
+ return (rows+=sz) >= 5 ? rows : 5;
+}
+
+static int presay(string str)
+{
+ if (!str=_unparsed_args())
+ write("Dein Presay ist jetzt geloescht.\n");
+ else
+ printf("Dein Presay lautet jetzt: \"%s\".\n",str=capitalize(str));
+ SetProp(P_PRESAY,str);
+ return 1;
+}
+
+static int sethands(string str)
+{
+ mixed *hands;
+
+ if (!(str=_unparsed_args()))
+ {
+ write("sethands <message>\n");
+ return 1;
+ }
+ if (str=="0")
+ hands=RaceDefault(P_HANDS);
+ if (!hands || !pointerp(hands))
+ hands=Query(P_HANDS);
+ hands[0]=" "+str;
+ Set(P_HANDS,hands);
+ write("Ok.\n");
+ return 1;
+}
+
+static int inform(string str)
+{
+ switch (str) {
+ case "on":
+ case "ein":
+ case "an":
+ if (Query(P_INFORMME))
+ write("Das hattest Du schon so eingestellt.\n");
+ else
+ {
+ write("Kuenftig wirst Du informiert, wenn jemand das "MUDNAME" verlaesst/betritt.\n");
+ Set(P_INFORMME,1);
+ }
+ return 1;
+ case "aus":
+ case "off":
+ if (!Query(P_INFORMME))
+ write("Das hattest Du schon so eingestellt.\n");
+ else
+ {
+ write("Ok.\n");
+ Set(P_INFORMME,0);
+ }
+ return 1;
+ case 0:
+ write("Inform-Mode ist "+(Query(P_INFORMME)?"an":"aus")+"geschaltet.\n");
+ return 1;
+ }
+ write("inform an oder inform aus, bitte.\n");
+ return 1;
+}
+
+void delayed_write(mixed *what)
+{
+ if (!pointerp(what)||!sizeof(what)||!pointerp(what[0]))
+ return;
+ tell_object(ME,what[0][0]);
+ if (sizeof(what)>1&&sizeof(what[0])>1)
+ call_out("delayed_write",what[0][1],what[1..]);
+}
+
+void notify_player_change(string who, int rein, int invis)
+{
+ string *list,name;
+ mixed mlist;
+
+ if (invis) name="("+who+")";
+ else name=who;
+
+ if (Query(P_INFORMME))
+ {
+ if (rein)
+ tell_object(ME,name+" ist gerade ins "MUDNAME" gekommen.\n");
+ else
+ tell_object(ME,name+" hat gerade das "MUDNAME" verlassen.\n");
+ }
+
+ if(Query(P_WAITFOR_FLAGS) & (0x01))return ;
+
+ if(pointerp(list=Query(P_WAITFOR)) && sizeof(list) && member(list,who)!=-1)
+ {
+ if (!QueryProp(P_VISUALBELL))
+ name+=sprintf("%c",7); // Char fuer Pieps an den String anhaengen.
+ // Moechte der Spieler keine ASCII-Grafik sehen, wird diese Meldung ohne
+ // Leerzeichen formatiert, so dass sie von Screenreadern vorgelesen wird.
+ // Anderenfalls wuerde sie einzeln buchstabiert.
+ if ( QueryProp(P_NO_ASCII_ART) )
+ {
+ delayed_write( ({ ({ sprintf("%s IST JETZT %sDA !!!\n",
+ name, (rein?"":"NICHT MEHR ")) }) }) );
+ }
+ else
+ {
+ delayed_write( ({ ({ sprintf("%s I S T J E T Z T %sD A !!!\n",
+ name, (rein?"":"N I C H T M E H R ")) }) }) );
+ }
+ }
+
+ if (rein && (sizeof(mlist=QueryProp(P_WAITFOR_REASON))) &&
+ (mappingp(mlist)) && (mlist[who]))
+ Show_WaitFor_Reason(who,invis);
+}
+
+static int erwarte(string str)
+{
+ string *list,*str1;
+ mixed mlist;
+ int i;
+
+ if (!mappingp(mlist=QueryProp(P_WAITFOR_REASON)))
+ mlist=([]);
+ if (!pointerp(list=Query(P_WAITFOR)))
+ list=({});
+
+ if (!str || str=="-u")
+ {
+ if(Query(P_WAITFOR_FLAGS)&0x01)
+ write("Du hast 'erwarte' temporaer deaktiviert.\n");
+ write("Du erwartest jetzt");
+ if (!sizeof(list))
+ write(" niemanden mehr.\n");
+ else
+ {
+ write(":\n");
+ if (!str) list=sort_array(list,#'>);
+ More(break_string(CountUp(list),78));
+ }
+ return 1;
+ }
+ if(str=="aus"){
+ Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)|0x01);
+ write("Erwarte ist jetzt deaktiviert.\n");
+ return 1;
+ }
+ if(str=="an" || str=="ein"){
+ Set(P_WAITFOR_FLAGS,Query(P_WAITFOR_FLAGS)&0xFE);
+ write("Erwarte ist jetzt aktiv.\n");
+ return 1;
+ }
+
+ str1=explode(_unparsed_args()||""," ");
+ if (sizeof(str1)==1)
+ {
+ if (str1[0]!="wegen")
+ {
+ str=capitalize(lower_case(str));
+ if (member(list,str)!=-1)
+ {
+ SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,str));
+ list-=({str});
+ write(str+" aus der Liste entfernt.\n");
+ } else
+ {
+ if (sizeof(list)>1000)
+ {
+ write("Du erwartest schon genuegend!\n");
+ return 1;
+ }
+ list+=({str});
+ write(str+" an die Liste angehaengt.\n");
+ }
+ Set(P_WAITFOR,list);
+ }
+ else
+ {
+ if (sizeof(mlist) && sizeof(list=m_indices(mlist)))
+ {
+ write("Du erwartest aus einem bestimmten Grund:\n");
+ write(break_string(CountUp(sort_array(list,#'>))+".",78));
+ }
+ else write("Du erwartest niemanden aus einem bestimmten Grund.\n");
+ }
+ return 1;
+ }
+ notify_fail("Falsche Syntax, siehe 'hilfe erwarte'!\n");
+ if (str1[1]!="wegen") return 0;
+ if (sizeof(str1)==2)
+ Show_WaitFor_Reason(capitalize(lower_case(str1[0])),0);
+ else {
+ string s=capitalize(lower_case(str1[0]));
+ if (sizeof(str1)==3 && (str1[2]=="nichts" || str1[2]=="loeschen"))
+ if (!mlist[s])
+ write("Du hast "+s+" aus keinem bestimmten Grund erwartet!\n");
+ else
+ {
+ SetProp(P_WAITFOR_REASON,m_copy_delete(mlist,s));
+ write("Du erwartest "+s+" aus keinem bestimmten Grund mehr!\n");
+ }
+ else
+ {
+ if (IS_ARCH(ME)) i=80; else if (IS_LEARNER(ME)) i=40;
+ else if (IS_SEER(ME)) i=20; else i=10;
+ if (!mlist[s] && sizeof(mlist)>=i)
+ write("Sorry, aber Du erwartest schon genuegend Leute!\n");
+ else
+ {
+ SetProp(P_WAITFOR_REASON,mlist+([s:implode(str1[2..]," ")]));
+ Show_WaitFor_Reason(s,0);
+ }
+ }
+ }
+ return 1;
+}
+
+static int uhrmeldung(string str)
+{
+ if (!(str=_unparsed_args()))
+ {
+ str=QueryProp(P_CLOCKMSG);
+ if (!str)
+ {
+ write("Du hast die Standard-Uhrmeldung.\n");
+ return 1;
+ }
+ if( !stringp(str) ) str = sprintf("%O\n",str);
+ printf("Deine Uhrmeldung ist:\n%s\n",str[0..<2]);
+ return 1;
+ }
+ if (str=="0")
+ {
+ SetProp(P_CLOCKMSG,0);
+ write("Ok, Du hast jetzt wieder die Standard-Meldung.\n");
+ return 1;
+ }
+ if (sizeof(explode(str,"%d"))>2)
+ {
+ write("Fehler, es darf nur ein %d in der Meldung vorkommen.\n");
+ return 1;
+ }
+ /* Mehrere %-Parameter verursachen das Abschalten der Uhr zur vollen Stunde.
+ */
+ if (sizeof(explode(str,"%"))>2)
+ {
+ write("Fehler: Zuviele %-Parameter in der Meldung.\n");
+ return 1;
+ }
+ /* Nur ein %-Parameter, aber der falsche: nicht sinnvoll. */
+ else
+ {
+ int i = strstr(str,"%",0);
+ if ( i>-1 && ( i==sizeof(str)-1 || str[i+1]!='d'))
+ {
+ write("Fehler: Falscher %-Parameter in der Meldung.\n");
+ return 1;
+ }
+ }
+ str+="\n";
+ SetProp(P_CLOCKMSG,str);
+ write("Ok.\n");
+ return 1;
+}
+
+static int zeitzone(string str)
+{
+ int zt;
+ if(!str || str==""){
+ if(!(zt=QueryProp(P_TIMEZONE)))
+ write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
+ "eingestellt.\n");
+ else if(zt>0)
+ printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
+ "eingestellt.\n",zt);
+ else
+ printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
+ "Berlin eingestellt.\n",-zt);
+ return 1;
+ }
+ if(sscanf(str,"utc %d",zt)==1) zt=(zt-1)%24;
+ else zt=to_int(str)%24;
+
+ SetProp(P_TIMEZONE,zt);
+
+ if(!zt)
+ write("Du hast derzeit die gleiche Zeitzone wie das "MUDNAME" "+
+ "eingestellt.\n");
+ else if(zt>0)
+ printf("Deine Zeitzone ist auf %d Stunden vor (oestlich) von Berlin "+
+ "eingestellt.\n",zt);
+ else
+ printf("Deine Zeitzone ist auf %d Stunden nach (westlich) von "+
+ "Berlin eingestellt.\n",-zt);
+ return 1;
+}
+
+static int emailanzeige(string str){
+ notify_fail("Syntax: emailanzeige [alle|freunde|niemand]\n");
+ if(!str || str==""){
+ if(!(str=QueryProp(P_SHOWEMAIL)))str="Niemandem";
+ else if(str=="alle")str="allen";
+ else if(str=="freunde")str="Deinen Freunden";
+ else if(str=="niemand")str="niemandem";
+ else{
+ SetProp(P_SHOWEMAIL,0);
+ str="Niemandem";
+ }
+ write("Deine Email wird "+str+" angezeigt.\n");
+ return 1;
+ }
+ else if(member(({"alle","freunde","niemand"}),str)==-1)return 0;
+
+ SetProp(P_SHOWEMAIL,str);
+
+ if(str=="alle")str="allen";
+ else if(str=="freunde")str="Deinen Freunden";
+ else str="niemandem";
+ write("Deine Email wird "+str+" angezeigt.\n");
+ return 1;
+}
+
+static int zaubertraenke()
+{
+ More("/room/orakel"->TipListe());
+ return 1;
+}
+
+varargs static int angriffsmeldung(string arg) {
+ if (arg=="ein" || arg=="an")
+ SetProp(P_SHOW_ATTACK_MSG,1);
+ else if (arg=="aus")
+ SetProp(P_SHOW_ATTACK_MSG,0);
+ if (QueryProp(P_SHOW_ATTACK_MSG))
+ write("Du siehst saemtliche Angriffsmeldungen von Dir.\n");
+ else
+ write("Du siehst nur neue Angriffsmeldungen von Dir.\n");
+ return 1;
+}
+
+static mixed _query_localcmds()
+{
+ return ({({"zeilen","set_screensize",0,0}),
+ ({"email","set_email",0,0}),
+ ({"url","set_homepage",0,0}),
+ ({"icq","set_icq",0,0}),
+ ({"messenger", "set_messenger", 0, 0}),
+ ({"ort","set_location",0,0}),
+ ({"punkte","short_score",0,0}),
+ ({"score","short_score",0,0}),
+ ({"info","score",0,0}),
+ ({"kurzinfo","very_short_score",0,0}),
+ ({"quit","new_quit",0,0}),
+ ({"ende","new_quit",0,0}),
+ ({"disconnect","disconnect",0,0}),
+ ({"schlaf","disconnect",1,0}),
+ ({"speichern","save_character",0,0}),
+ ({"save","save_character",0,0}),
+ ({"toete","kill",0,0}),
+ ({"angriffsmeldung","angriffsmeldung",0,0}),
+ ({"passw","change_password",1,0}),
+ ({"hilfe","help",1,0}),
+ ({"selbstloeschung","self_delete",0,0}),
+ ({"spielpause","spielpause",0,0}),
+ ({"spieldauer","spieldauer",0,0}),
+ ({"idee","idea",0,0}),
+ ({"typo","typo",0,0}),
+ ({"bug","bug",0,0}),
+ ({"fehler","fehlerhilfe",0,0}),
+ ({"md","md",0,0}),
+ ({"detail","md",0,0}),
+ ({"vorsicht","toggle_whimpy",0,0}),
+ ({"stop","stop",0,0}),
+ ({"kwho","kwho",0,0}),
+ ({"kwer","kwho",0,0}),
+ ({"kkwer","kkwho",0,0}),
+ ({"kkwho","kkwho",0,0}),
+ ({"who","who",0,0}),
+ ({"wer","who",0,0}),
+ ({"zeit","uhrzeit",0,0}),
+ ({"uhrzeit","uhrzeit",0,0}),
+ ({"weg","weg",0,0}),
+ ({"wegmeldung", "wegmeldung", 0, 0}),
+ ({"idlezeit", "idlezeit", 0, 0}),
+ ({"finger","finger",0,0}),
+ ({"muds","muds",0,0}),
+ ({"emote","emote",0,0}),
+ ({":","emote",1,0}),
+ ({";","emote",1,0}),
+ ({"remote","remote",0,SEER_LVL}),
+ ({"r:","remote",1,0}),
+ ({"r;","gremote",1,0}),
+ ({"titel","set_title",0,0}),
+ ({"review","review",0,SEER_LVL}),
+ ({"setmin","setmin",0,SEER_LVL}),
+ ({"setmout","setmout",0,SEER_LVL}),
+ ({"setmmin","setmmin",0,SEER_LVL}),
+ ({"setmmout","setmmout",0,SEER_LVL}),
+ ({"sethands","sethands",0,SEER_LVL}),
+ ({"presay","presay",0,SEER_LVL}),
+ ({"extralook","extralook",0,SEER_LVL}),
+ ({"fluchtrichtung","toggle_whimpy_dir",0,SEER_LVL}),
+ ({"inform","inform",0,0}),
+ ({"erwarte","erwarte",0,0}),
+ ({"stty","stty",0,0}),
+ ({"grafik", "set_ascii_art", 0, 0}),
+ ({"uhrmeldung","uhrmeldung",0,0}),
+ ({"zeitzone","zeitzone",0,0}),
+ ({"behalte","behalte",0,0}),
+ ({"zweitiemarkierung","zweitiemarkierung",0,0}),
+ ({"emailanzeige","emailanzeige",0,0}),
+ ({"topliste","topliste",0,0}),
+ ({"ton","set_visualbell",0,0}),
+ ({"telnegs","show_telnegs",0,0}),
+ ({"spotte", "spotte", 0, 0}),
+ ({"reise","reise",0,0}),
+ ({"zaubertraenke","zaubertraenke",0,0}),
+ ({"telnet","telnet_cmd",0,0}),
+ })+
+ command::_query_localcmds()+
+ viewcmd::_query_localcmds()+
+ comm::_query_localcmds()+
+ skills::_query_localcmds()+
+ description::_query_localcmds();
+}
+
+static int _check_keep(object ob)
+{
+ return (ob->QueryProp(P_KEEP_ON_SELL))==geteuid(ME);
+}
+
+static mixed _set_testplayer(mixed arg) {
+ mixed res;
+ object setob;
+
+ setob=this_player();
+ if (!objectp(setob) || !query_once_interactive(setob))
+ setob=this_interactive();
+ if (!objectp(setob))
+ setob=previous_object();
+ if (setob && !IS_DEPUTY(setob)) {
+ arg=geteuid(setob);
+ if (!arg || arg=="NOBODY")
+ arg=getuid(setob);
+ arg=capitalize(arg);
+ }
+ res=Set(P_TESTPLAYER,arg);
+ Set(P_TESTPLAYER,PROTECTED,F_MODE_AS);
+ return res;
+}
+
+int zweitiemarkierung(string arg)
+{
+ if (!QueryProp(P_SECOND))
+ return _notify_fail("Aber Du bist doch gar kein Zweiti.\n"),0;
+ notify_fail("Syntax: zweitiemarkierung [unsichtbar|sichtbar|name]\n");
+ if (!arg)
+ return 0;
+ switch (arg)
+ {
+ case "unsichtbar" :
+ SetProp(P_SECOND_MARK,-1);
+ write("Jetzt sieht kein Spieler mehr, dass Du ein Zweiti bist.\n");
+ return 1;
+ case "sichtbar" :
+ SetProp(P_SECOND_MARK,0);
+ write("Jetzt sieht kein Spieler mehr, wessen Zweiti Du bist.\n");
+ return 1;
+ case "name" :
+ SetProp(P_SECOND_MARK,1);
+ write("Jetzt koennen alle sehen, wessen Zweiti Du bist.\n");
+ return 1;
+ }
+ return 0;
+}
+
+int topliste(string arg)
+{
+ if (!arg)
+ {
+ printf("Du hast Dich fuer die Topliste %s.\n",
+ (QueryProp(P_NO_TOPLIST) ? "gesperrt" : "freigegeben"));
+ return 1;
+ }
+ else if (member(({"j","ja","n","nein"}),arg)==-1)
+ return _notify_fail("Syntax: topliste [ja|nein]\n"),0;
+ if (arg[0]=='j')
+ {
+ SetProp(P_NO_TOPLIST,0);
+ write("Du kannst jetzt (theoretisch) in der Topliste auftauchen.\n");
+ }
+ else
+ {
+ SetProp(P_NO_TOPLIST,1);
+ write("Du wirst jetzt nicht in der Topliste auftauchen.\n");
+ }
+ Set(P_NO_TOPLIST,SAVE|PROTECTED,F_MODE_AS);
+ return 1;
+}
+
+int show_telnegs(string arg)
+{
+ if (!arg)
+ {
+ write("Du bekommst Aenderungen Deiner Fenstergroesse "+
+ (QueryProp(P_TTY_SHOW)?"":"nicht ")+"angezeigt.\n");
+ return 1;
+ }
+ if (member(({"ein","an","aus"}),arg)==-1)
+ {
+ write("Syntax: telnegs [ein|aus]\n");
+ return 1;
+ }
+ if (arg=="ein" || arg=="an")
+ {
+ write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"":"nun ")+
+ "Aenderungen Deiner Fenstergroesse angezeigt.\n");
+ Set(P_TTY_SHOW,1);
+ return 1;
+ }
+ write("Du bekommst "+(QueryProp(P_TTY_SHOW)?"nun ":"")+
+ "Aenderungen Deiner Fenstergroesse nicht "+
+ (QueryProp(P_TTY_SHOW)?"mehr ":"")+"angezeigt.\n");
+ Set(P_TTY_SHOW,0);
+ return 1;
+}
+
+private int set_keep_alive(string str) {
+ if (str == "ein") {
+ telnet_tm_counter = 240 / __HEART_BEAT_INTERVAL__;
+ tell_object(this_object(), break_string(
+ "An Deinen Client werden jetzt alle 4 Minuten unsichtbare Daten "
+ "geschickt, um zu verhindern, dass Deine Verbindung zum "MUDNAME
+ " beendet wird.", 78));
+ }
+ else if (str == "aus") {
+ telnet_tm_counter = 0;
+ tell_object(this_object(),break_string(
+ "Du hast das Senden von unsichtbaren Daten (Keep-Alive-Pakete) an "
+ "Deinen Client ausgeschaltet.",78));
+ }
+ else {
+ if (!telnet_tm_counter)
+ tell_object(this_object(), break_string(
+ "An Deinen Client werden keine Keep-Alive-Pakete geschickt.",78));
+ else
+ tell_object(this_object(), break_string(
+ "An Deinen Client werden alle 4 Minuten "
+ "unsichtbare Daten geschickt, damit Deine Verbindung "
+ "zum "MUDNAME" nicht beendet wird.",78));
+ }
+ return 1;
+}
+
+private int print_telnet_rttime() {
+ int rtt = QueryProp(P_TELNET_RTTIME);
+ if (rtt>0)
+ tell_object(ME, break_string(
+ "Die letzte gemessene 'round-trip' Zeit vom MG zu Deinem Client "
+ "und zurueck betrug " + rtt + " us.",78));
+ else
+ tell_object(ME, break_string(
+ "Bislang wurde die 'round-trip' Zeit vom MG zu Deinem Client "
+ "noch nicht gemessen oder Dein Client unterstuetzt dieses "
+ "nicht.",78));
+ return 1;
+}
+
+int telnet_cmd(string str) {
+ if (!str) return 0;
+ string *args = explode(str, " ");
+ string newargs;
+ if (sizeof(args) > 1)
+ newargs = implode(args[1..], " ");
+ else
+ newargs = "";
+
+ switch(args[0])
+ {
+ case "keepalive":
+ return set_keep_alive(newargs);
+ case "rttime":
+ return print_telnet_rttime();
+ }
+ return 0;
+}
+
+int spotte( string str )
+{
+ _notify_fail( "Hier ist nichts, was Du verspotten koenntest!\n" );
+ return 0;
+}
+
+int behalte(string str)
+{
+ object ob,*obs;
+ string s;
+
+ if (str)
+ {
+ if (str=="alles") {
+ filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, getuid());
+ write("Ok!\n");
+ return 1;
+ }
+ if (str=="nichts") {
+ filter_objects(all_inventory(), "SetProp", P_KEEP_ON_SELL, 0);
+ write("Ok!\n");
+ return 1;
+ }
+ if (!sizeof(obs=find_obs(str,PUT_GET_NONE)))
+ {
+ _notify_fail("Aber sowas hast Du nicht dabei!\n");
+ return 0;
+ }
+ else ob=obs[0];
+
+ if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
+ ob->SetProp(P_KEEP_ON_SELL,0);
+ else
+ ob->SetProp(P_KEEP_ON_SELL,geteuid(ME));
+
+ // erneut abfragen, da sich der Wert nicht geaendert haben muss
+ if (ob->QueryProp(P_KEEP_ON_SELL)==geteuid(ME))
+ write(break_string(sprintf("Ok, Du wirst %s jetzt bei 'verkaufe alles' "
+ "behalten.\n",ob->name(WEN)),78));
+ else
+ write(break_string(sprintf("Ok, Du wirst %s beim naechsten 'verkaufe "
+ "alles' mitverkaufen!\n",ob->name(WEN)),78));
+
+ return 1;
+ }
+ s=make_invlist(ME,filter(all_inventory(ME),#'_check_keep)); //'));
+ More(s);
+ return 1;
+}
+
+static int _query_lep()
+{
+ int val;
+ val = LEPMASTER->QueryLEP();
+ Set( P_LEP, val );
+ return val;
+}
+
+static mixed _set_fraternitasdonoarchmagorum(mixed arg)
+{
+ if (!intp(arg)) return -1;
+
+ if ((!previous_object(1)||object_name(previous_object(1))!=FAO_MASTER) &&
+ (!this_interactive() || !IS_ARCH(this_interactive())))
+ return -1;
+
+ if (!intp(arg)) return -1;
+
+ log_file("fao/P_FAO",sprintf("%s - %s P_FAO gesetzt auf %O\n",
+ dtime(time()),query_real_name(),arg) );
+ return Set(P_FAO,arg);
+}
+
+nomask void set_realip(string str)
+{
+ if(previous_object() && strstr(object_name(previous_object()),"/secure")==0)
+ {
+ realip=str;
+ }
+}
+
+nomask string query_realip()
+{
+ return realip ? realip : "";
+}
+
+mixed _query_netdead_env() {
+ return ndead_location || ndead_l_filename;
+}